diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 837d80f28153c..a2d95405dcb82 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -25,7 +25,6 @@ /src/plugins/charts/ @elastic/kibana-vis-editors /src/plugins/management/ @elastic/kibana-vis-editors /src/plugins/kibana_legacy/ @elastic/kibana-vis-editors -/src/plugins/timelion/ @elastic/kibana-vis-editors /src/plugins/vis_default_editor/ @elastic/kibana-vis-editors /src/plugins/vis_types/metric/ @elastic/kibana-vis-editors /src/plugins/vis_type_table/ @elastic/kibana-vis-editors @@ -41,6 +40,8 @@ /src/plugins/chart_expressions/expression_tagcloud/ @elastic/kibana-vis-editors /src/plugins/url_forwarding/ @elastic/kibana-vis-editors /packages/kbn-tinymath/ @elastic/kibana-vis-editors +/x-pack/test/functional/apps/lens @elastic/kibana-vis-editors +/test/functional/apps/visualize/ @elastic/kibana-vis-editors # Application Services /examples/bfetch_explorer/ @elastic/kibana-app-services @@ -147,6 +148,7 @@ /src/plugins/vis_type_markdown/ @elastic/kibana-presentation /src/plugins/presentation_util/ @elastic/kibana-presentation /test/functional/apps/dashboard/ @elastic/kibana-presentation +/test/functional/apps/dashboard_elements/ @elastic/kibana-presentation /x-pack/plugins/canvas/ @elastic/kibana-presentation /x-pack/plugins/dashboard_enhanced/ @elastic/kibana-presentation /x-pack/test/functional/apps/canvas/ @elastic/kibana-presentation diff --git a/.i18nrc.json b/.i18nrc.json index 77c57ded8242b..11d31be8f891c 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -56,7 +56,7 @@ "server": "src/legacy/server", "statusPage": "src/legacy/core_plugins/status_page", "telemetry": ["src/plugins/telemetry", "src/plugins/telemetry_management_section"], - "timelion": ["src/plugins/timelion", "src/plugins/vis_type_timelion"], + "timelion": ["src/plugins/vis_type_timelion"], "uiActions": "src/plugins/ui_actions", "visDefaultEditor": "src/plugins/vis_default_editor", "visTypeMarkdown": "src/plugins/vis_type_markdown", diff --git a/api_docs/core_application.json b/api_docs/core_application.json index 72a11803976c9..35c12330898fb 100644 --- a/api_docs/core_application.json +++ b/api_docs/core_application.json @@ -1393,10 +1393,6 @@ "plugin": "kibanaOverview", "path": "src/plugins/kibana_overview/public/application.tsx" }, - { - "plugin": "timelion", - "path": "src/plugins/timelion/public/application.ts" - }, { "plugin": "management", "path": "src/plugins/management/target/types/public/application.d.ts" diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index e1328b249ce7a..82caea2104067 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -13,7 +13,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Deprecated API | Referencing plugin(s) | Remove By | | ---------------|-----------|-----------| -| | discover, visualizations, dashboard, lens, observability, maps, dashboardEnhanced, discoverEnhanced, securitySolution, visualize, timelion, presentationUtil | 8.1 | +| | discover, visualizations, dashboard, lens, observability, maps, dashboardEnhanced, discoverEnhanced, securitySolution, visualize, presentationUtil | 8.1 | | | lens, timelines, infra, securitySolution, stackAlerts, transform, indexPatternManagement, visTypeTimelion, visTypeVega | 8.1 | | | discover, visualizations, dashboard, lens, observability, timelines, maps, infra, dashboardEnhanced, discoverEnhanced, securitySolution, urlDrilldown, inputControlVis, visTypeTimelion, visualize, visTypeVega, presentationUtil, ml, visTypeTimeseries | 8.1 | | | discover, visualizations, dashboard, lens, observability, timelines, maps, infra, dashboardEnhanced, discoverEnhanced, securitySolution, urlDrilldown, inputControlVis, visTypeTimelion, visualize, visTypeVega, presentationUtil, ml, visTypeTimeseries | 8.1 | @@ -86,16 +86,16 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | discover | - | | | discover, ml, transform, canvas | - | | | embeddable, discover, presentationUtil, dashboard, graph | - | -| | visualizations, discover, dashboard, savedObjectsManagement, timelion | - | +| | visualizations, discover, dashboard, savedObjectsManagement | - | | | discover, savedObjectsTaggingOss, visualizations, dashboard, visualize, visDefaultEditor | - | -| | discover, visualizations, dashboard, timelion | - | +| | discover, visualizations, dashboard | - | | | data, discover, embeddable | - | | | advancedSettings, discover | - | | | advancedSettings, discover | - | | | security | - | | | security | - | | | security, licenseManagement, ml, fleet, apm, reporting, crossClusterReplication, logstash, painlessLab, searchprofiler, watcher | - | -| | management, fleet, security, kibanaOverview, timelion | - | +| | management, fleet, security, kibanaOverview | - | | | visualizations, dashboard | - | | | visualizations, dashboard | - | | | visualizations, dashboard | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 8d070428376b4..75eac66df04a1 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -811,17 +811,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex -## timelion - -| Deprecated API | Reference location(s) | Remove By | -| ---------------|-----------|-----------| -| | [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/timelion/public/plugin.ts#:~:text=esFilters), [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/timelion/public/plugin.ts#:~:text=esFilters) | 8.1 | -| | [saved_sheets.ts](https://github.com/elastic/kibana/tree/master/src/plugins/timelion/public/services/saved_sheets.ts#:~:text=SavedObjectLoader), [saved_sheets.ts](https://github.com/elastic/kibana/tree/master/src/plugins/timelion/public/services/saved_sheets.ts#:~:text=SavedObjectLoader) | - | -| | [_saved_sheet.ts](https://github.com/elastic/kibana/tree/master/src/plugins/timelion/public/services/_saved_sheet.ts#:~:text=SavedObjectClass) | - | -| | [application.ts](https://github.com/elastic/kibana/tree/master/src/plugins/timelion/public/application.ts#:~:text=appBasePath) | - | - - - ## transform | Deprecated API | Reference location(s) | Remove By | diff --git a/api_docs/saved_objects.json b/api_docs/saved_objects.json index a0e14bae47ba2..d400068df4f83 100644 --- a/api_docs/saved_objects.json +++ b/api_docs/saved_objects.json @@ -679,14 +679,6 @@ { "plugin": "savedObjectsManagement", "path": "src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx" - }, - { - "plugin": "timelion", - "path": "src/plugins/timelion/public/services/saved_sheets.ts" - }, - { - "plugin": "timelion", - "path": "src/plugins/timelion/public/services/saved_sheets.ts" } ], "children": [ @@ -3860,10 +3852,6 @@ { "plugin": "dashboard", "path": "src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts" - }, - { - "plugin": "timelion", - "path": "src/plugins/timelion/public/services/_saved_sheet.ts" } ] }, diff --git a/docs/api/features.asciidoc b/docs/api/features.asciidoc index dad3ef75c8117..69f0effb80023 100644 --- a/docs/api/features.asciidoc +++ b/docs/api/features.asciidoc @@ -134,7 +134,6 @@ The API returns the following: "index-pattern", "search", "visualization", - "timelion-sheet", "canvas-workpad" ] }, @@ -152,7 +151,6 @@ The API returns the following: "index-pattern", "search", "visualization", - "timelion-sheet", "canvas-workpad", "dashboard" ] diff --git a/docs/api/role-management/get.asciidoc b/docs/api/role-management/get.asciidoc index d1e9d1e6afa83..b18b2e231774a 100644 --- a/docs/api/role-management/get.asciidoc +++ b/docs/api/role-management/get.asciidoc @@ -73,9 +73,6 @@ The API returns the following: "indexPatterns": [ "read" ], - "timelion": [ - "all" - ], "graph": [ "all" ], diff --git a/docs/api/role-management/put.asciidoc b/docs/api/role-management/put.asciidoc index be46178100095..92750840aca10 100644 --- a/docs/api/role-management/put.asciidoc +++ b/docs/api/role-management/put.asciidoc @@ -94,9 +94,6 @@ $ curl -X PUT api/security/role/my_kibana_role "indexPatterns": [ "read" ], - "timelion": [ - "all" - ], "graph": [ "all" ], diff --git a/docs/api/saved-objects/bulk_create.asciidoc b/docs/api/saved-objects/bulk_create.asciidoc index 5bd3a7587dde9..a935907ef3f11 100644 --- a/docs/api/saved-objects/bulk_create.asciidoc +++ b/docs/api/saved-objects/bulk_create.asciidoc @@ -30,7 +30,7 @@ experimental[] Create multiple {kib} saved objects. ==== Request body `type`:: - (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`, and `timelion-sheet`. + (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`. `id`:: (Optional, string) Specifies an ID instead of using a randomly generated ID. diff --git a/docs/api/saved-objects/bulk_get.asciidoc b/docs/api/saved-objects/bulk_get.asciidoc index 4c6bf4c19a76c..1bcdf7ba33cf4 100644 --- a/docs/api/saved-objects/bulk_get.asciidoc +++ b/docs/api/saved-objects/bulk_get.asciidoc @@ -23,7 +23,7 @@ experimental[] Retrieve multiple {kib} saved objects by ID. ==== Request Body `type`:: - (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`, and `timelion-sheet`. + (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`. `id`:: (Required, string) ID of the retrieved object. The ID includes the {kib} unique identifier or a custom identifier. diff --git a/docs/api/saved-objects/create.asciidoc b/docs/api/saved-objects/create.asciidoc index e7e25c7d3bba6..437bdb497da26 100644 --- a/docs/api/saved-objects/create.asciidoc +++ b/docs/api/saved-objects/create.asciidoc @@ -24,7 +24,7 @@ experimental[] Create {kib} saved objects. (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. ``:: - (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`, and `timelion-sheet`. + (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`. ``:: (Optional, string) Specifies an ID instead of using a randomly generated ID. diff --git a/docs/api/saved-objects/delete.asciidoc b/docs/api/saved-objects/delete.asciidoc index 9c342cb4d843e..ab50fd6e37eac 100644 --- a/docs/api/saved-objects/delete.asciidoc +++ b/docs/api/saved-objects/delete.asciidoc @@ -22,7 +22,7 @@ WARNING: Once you delete a saved object, _it cannot be recovered_. (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. `type`:: - (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`, and `timelion-sheet`. + (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`. `id`:: (Required, string) The object ID that you want to remove. diff --git a/docs/api/saved-objects/get.asciidoc b/docs/api/saved-objects/get.asciidoc index 4c8cd020e0286..cfc591d811227 100644 --- a/docs/api/saved-objects/get.asciidoc +++ b/docs/api/saved-objects/get.asciidoc @@ -21,7 +21,7 @@ experimental[] Retrieve a single {kib} saved object by ID. `type`:: - (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`, and `timelion-sheet`. + (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`. `id`:: (Required, string) The ID of the object to retrieve. diff --git a/docs/api/saved-objects/resolve.asciidoc b/docs/api/saved-objects/resolve.asciidoc index abfad6a0a8150..aa18538975f5f 100644 --- a/docs/api/saved-objects/resolve.asciidoc +++ b/docs/api/saved-objects/resolve.asciidoc @@ -25,7 +25,7 @@ object can be retrieved via the Resolve API using either its new ID or its old I `type`:: - (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`, and `timelion-sheet`. + (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`. `id`:: (Required, string) The ID of the object to retrieve. diff --git a/docs/api/saved-objects/update.asciidoc b/docs/api/saved-objects/update.asciidoc index d237ced8b52d1..2bd95df1adf30 100644 --- a/docs/api/saved-objects/update.asciidoc +++ b/docs/api/saved-objects/update.asciidoc @@ -20,7 +20,7 @@ experimental[] Update the attributes for existing {kib} saved objects. (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. `type`:: - (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`, and `timelion-sheet`. + (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`. `id`:: (Required, string) The object ID to update. diff --git a/docs/api/spaces-management/get_all.asciidoc b/docs/api/spaces-management/get_all.asciidoc index e76848da80efb..3c95b1b904441 100644 --- a/docs/api/spaces-management/get_all.asciidoc +++ b/docs/api/spaces-management/get_all.asciidoc @@ -70,7 +70,7 @@ The API returns the following: "id": "sales", "name": "Sales", "initials": "MK", - "disabledFeatures": ["discover", "timelion"], + "disabledFeatures": ["discover"], "imageUrl": "" } ] @@ -124,7 +124,7 @@ The API returns the following: "id": "sales", "name": "Sales", "initials": "MK", - "disabledFeatures": ["discover", "timelion"], + "disabledFeatures": ["discover"], "imageUrl": "", "authorizedPurposes": { "any": true, diff --git a/docs/api/spaces-management/post.asciidoc b/docs/api/spaces-management/post.asciidoc index 1abfffd1c736f..28d60caa0d333 100644 --- a/docs/api/spaces-management/post.asciidoc +++ b/docs/api/spaces-management/post.asciidoc @@ -54,7 +54,7 @@ $ curl -X POST api/spaces/space "description" : "This is the Marketing Space", "color": "#aabbcc", "initials": "MK", - "disabledFeatures": ["timelion"], + "disabledFeatures": [], "imageUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD4AAABACAYAAABC6cT1AAAGf0lEQVRoQ+3abYydRRUH8N882xYo0IqagEVjokQJKAiKBjXExC9G/aCkGowCIghCkRcrVSSKIu/FEiqgGL6gBIlAYrAqUTH6hZgQFVEMKlQFfItWoQWhZe8z5uzMLdvbfbkLxb13d+fbvfe588x/zpn/+Z9zJpmnI81T3BaAzzfLL1h8weLzZAcWXH2eGHo7zAWLL1h8nuzAjFw9G1N6Kzq8HnuM36MR8iibF3Fv4q+7cv8yDV6K13bYq2furSP8Ag8ncr/vnSnwRViJT2GfCV7yL1yHGxLb+l3EdM9lluNEnIC9xz+f2ZL4Er6Z2DrdXN3fZwp8CU7OfDHxggle8lTLbQ1nJ/7Z7yKmey5zYGZt4h2IzR8/trRc2PDlxJPTzfVcgJ+CC0wMPOa9F6cm7up3EVM9V9386MxliVdM8GwAv6hh/awCz/w7lY25OtF5ruBz4ZLP42NYNrDAFbC3YPWuILnMAfgq3oaRQQYea/stViV+sgssvjKzLvGySeaaNVfP4d7Btokgvxj/bblgpueuF1hmWcyTCmfE3J3M1lTcv0vMswM88zR+jpw4osu6me8kzkpsfLZWzxyRuabO22buxxOJ12FxnXfWgEe83pB5sOE47BsLymzscOoi7nw2JJfZreUjiUsTyzKPZm5NvBDvSuw268AzNzV8H5/Am+qCnsAXgpgSW2Zq9cyKlksbPlTd+te4quWNieMHBfiNDdciYnwsdI/MaOaWhnMTf54J8CqNj8x8JXFIZltYu+HqlmNT8YSBsHgAPw/vxvlVV4du/s0oaxbxg0TbL/jMni0nNcVjQq7+HZfgtpbzBg342TgQ63AkmsymxBW4IjE6A+D7Vzd/fyWxIM/VuCe+HzTgZ2Jpy/kNJ2FJLmLm24mPJ/42A+Bvrxt4SISwlhsaPodH26LZB8rVA3inwwebsrixJCZzX+KMxI/7AV61eVh3DV6Mx3EOvh4kN6jAg8nfUCXm4d1wE66OyxNPTQc+s3/o/MoXizL3JE5O3F3P/uBZPPF4Zr+Wi5uSO48ZPRdyCwn7YB/A35m5KhWNHox4fcNnIs0ddOCRSBxf8+cQG+Huf0l8NJVYP+nI7NXy2ar4QqIGm69JfKPOE2w/mBavCzwM11R2D+ChsUO7hyUfmwx55qDM1xJvqZ7y08TpifuGBfjeURVJnNIVGpkNiXNS0ds7jcySDitDCCWW56LJ10fRo8sNA+3qXUSZD2CtQlZh9T+1rB7h9oliembflnMbzqgSNZKbKGHdPm7OwXb1CvQ1metSETMpszmzvikCJNh/h5E5PHNl4qga/+/cxqrdeWDYgIe7X5L4cGJPJX2940lOX8pD41FnFnc4riluvQKbK0dcHJFi2IBHNTQSlguru4d2/wPOTNzRA3x5y+U1E1uqWDkETOT026XuUJzx6u7ReLhSYenQ7uHua0fKZmwfmcPqsQjxE5WVONcRxn7X89zgn/EKPMRMxOVQXmP18Mx3q3b/Y/0cQE/IhFtHESMsHFlZ1Ml3CH3DZPHImY+pxcKumNmYirtvqMBfhMuU6s3iqOQkTsMPe1tCQwO8Ajs0lxr7W+vnp1MJc9EgCNd/cy6x+9D4veXmprj5wxMw/3C4egW6zzgZOlYZzfwo3F2J7ael0pJamvlPKgWNKFft1AAcKotXoFEbD7kaoSoQPVKB35+5KHF0lai/rJo+up87jWEE/qqqwY+qrL21LWLm95lPJ16ppKw31XC3PXYPJauPEx7B6BHCgrSizRs18qiaRp8tlN3ueCTYPHH9RNaunjI8Z7wLYpT3jZSCYXQ8e9vTsRE/q+no3XMKeObgGtaintbb/AvXj4JDkNw/5hrwYPfIvlZFUbLn7G5q+eQIN09Vnho6cqvnM/Lt99RixH49wO8K0ZL41WTWHoQzvsNVkOheZqKhEGpsp3SzB+BBtZAYve7uOR9tuTaaB6l0XScdYfEQPpkTUyHEGP+XqyDBzu+NBCITUjNWHynkrbWKOuWFn1xKzqsyx0bdvS78odp0+N503Zao0uCsWuSIDku8/7EO60b41vN5+Ses9BKlTdvd8bhp9EBvJjWJAIn/vxwHe6b3tSk6JFPV4nq85oAOrx555v/x/rh3E6Lo+bnuNS4uB4Cuq0ZfvO8X1rM6q/+vnjLVqZq7v83onttc2oYF4HPJmv1gWbB4P7s0l55ZsPhcsmY/WBYs3s8uzaVn5q3F/wf70mRuBCtbjQAAAABJRU5ErkJggg==" } -------------------------------------------------- diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 319ac1e8476fe..84e6668830edc 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -235,11 +235,6 @@ generating deep links to other apps, and creating short URLs. |This plugin adds the Advanced Settings section for the Usage and Security Data collection (aka Telemetry). -|{kib-repo}blob/{branch}/src/plugins/timelion/README.md[timelion] -|Contains the deprecated timelion application. For the timelion visualization, -which also contains the timelion APIs and backend, look at the vis_type_timelion plugin. - - |<> |UI Actions plugins provides API to manage *triggers* and *actions*. diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md b/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md index eb0dbb59e6c12..00e5da4a9a9f9 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.deprecationsservicesetup.md @@ -25,29 +25,29 @@ import { i18n } from '@kbn/i18n'; async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecationsContext): Promise { const deprecations: DeprecationsDetails[] = []; - const count = await getTimelionSheetsCount(savedObjectsClient); + // Example of an api correctiveAction + const count = await getFooCount(savedObjectsClient); if (count > 0) { - // Example of a manual correctiveAction deprecations.push({ - title: i18n.translate('xpack.timelion.deprecations.worksheetsTitle', { - defaultMessage: 'Timelion worksheets are deprecated' + title: i18n.translate('xpack.foo.deprecations.title', { + defaultMessage: `Foo's are deprecated` }), - message: i18n.translate('xpack.timelion.deprecations.worksheetsMessage', { - defaultMessage: 'You have {count} Timelion worksheets. Migrate your Timelion worksheets to a dashboard to continue using them.', + message: i18n.translate('xpack.foo.deprecations.message', { + defaultMessage: `You have {count} Foo's. Migrate your Foo's to a dashboard to continue using them.`, values: { count }, }), documentationUrl: - 'https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html', + 'https://www.elastic.co/guide/en/kibana/current/foo.html', level: 'warning', correctiveActions: { manualSteps: [ - i18n.translate('xpack.timelion.deprecations.worksheets.manualStepOneMessage', { - defaultMessage: 'Navigate to the Kibana Dashboard and click "Create dashboard".', - }), - i18n.translate('xpack.timelion.deprecations.worksheets.manualStepTwoMessage', { - defaultMessage: 'Select Timelion from the "New Visualization" window.', - }), + i18n.translate('xpack.foo.deprecations.manualStepOneMessage', { + defaultMessage: 'Navigate to the Kibana Dashboard and click "Create dashboard".', + }), + i18n.translate('xpack.foo.deprecations.manualStepTwoMessage', { + defaultMessage: 'Select Foo from the "New Visualization" window.', + }), ], api: { path: '/internal/security/users/test_dashboard_user', @@ -68,6 +68,7 @@ async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecations }, }); } + return deprecations; } diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index cbf32c35c55fd..6bf9ddb365290 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -470,13 +470,6 @@ The default period of time in the Security time filter. [[kibana-timelion-settings]] ==== Timelion -[horizontal] -[[timelion-defaultcolumns]]`timelion:default_columns`:: -The default number of columns to use on a Timelion sheet. - -[[timelion-defaultrows]]`timelion:default_rows`:: -The default number of rows to use on a Timelion sheet. - [[timelion-esdefaultindex]]`timelion:es.default_index`:: The default index when using the `.es()` query. @@ -502,9 +495,6 @@ experimental:[] Used with quandl queries, this is your API key from https://www.quandl.com/[www.quandl.com]. -[[timelion-showtutorial]]`timelion:showTutorial`:: -Shows the Timelion tutorial to users when they first open the Timelion app. - [[timelion-targetbuckets]]`timelion:target_buckets`:: Used for calculating automatic intervals in visualizations, this is the number of buckets to try to represent. diff --git a/docs/user/dashboard/timelion.asciidoc b/docs/user/dashboard/timelion.asciidoc index 85c8bbd436d25..ae92ed4259b65 100644 --- a/docs/user/dashboard/timelion.asciidoc +++ b/docs/user/dashboard/timelion.asciidoc @@ -13,8 +13,6 @@ The syntax enables some features that classical point series charts don't offer, [role="screenshot"] image:dashboard/images/timelion.png[Timelion] -deprecated::[7.0.0,"*Timelion* is still supported. The *Timelion app* is deprecated in 7.0, replaced by dashboard features. In 7.16 and later, the *Timelion app* is removed from {kib}. To prepare for the removal of *Timelion app*, you must migrate *Timelion app* worksheets to a dashboard. For information on how to migrate *Timelion app* worksheets, refer to the link:https://www.elastic.co/guide/en/kibana/7.10/release-notes-7.10.0.html#deprecation-v7.10.0[7.10.0 Release Notes]."] - [float] ==== Timelion expressions diff --git a/docs/user/dashboard/tsvb.asciidoc b/docs/user/dashboard/tsvb.asciidoc index 67a1be3c63ac1..3973d70793600 100644 --- a/docs/user/dashboard/tsvb.asciidoc +++ b/docs/user/dashboard/tsvb.asciidoc @@ -59,6 +59,8 @@ The index pattern mode unlocks many new features, such as: * Interactive filters for time series visualizations +* Custom field formats + * Better performance [float] diff --git a/package.json b/package.json index b9beacb296ebe..06755da56451a 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "**/pdfkit/crypto-js": "4.0.0", "**/react-syntax-highlighter": "^15.3.1", "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", - "**/request": "^2.88.2", "**/trim": "1.0.1", "**/typescript": "4.1.3", "**/underscore": "^1.13.1" @@ -189,7 +188,6 @@ "angular-recursion": "^1.0.5", "angular-route": "^1.8.0", "angular-sanitize": "^1.8.0", - "angular-sortable-view": "^0.0.17", "antlr4ts": "^0.5.0-alpha.3", "archiver": "^5.2.0", "axios": "^0.21.1", @@ -369,7 +367,6 @@ "regenerator-runtime": "^0.13.3", "remark-parse": "^8.0.3", "remark-stringify": "^9.0.0", - "request": "^2.88.0", "require-in-the-middle": "^5.0.2", "reselect": "^4.0.0", "resize-observer-polyfill": "^1.5.0", @@ -607,7 +604,6 @@ "@types/recompose": "^0.30.6", "@types/reduce-reducers": "^1.0.0", "@types/redux-actions": "^2.6.1", - "@types/request": "^2.48.2", "@types/seedrandom": ">=2.0.0 <4.0.0", "@types/selenium-webdriver": "^4.0.9", "@types/semver": "^7", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 282abe1e1741a..69cfffe1f08f0 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -70,7 +70,6 @@ pageLoadAssetSize: spaces: 57868 telemetry: 51957 telemetryManagementSection: 38586 - timelion: 29920 transform: 41007 triggersActionsUi: 100000 uiActions: 97717 diff --git a/packages/kbn-ui-shared-deps/src/entry.js b/packages/kbn-ui-shared-deps/src/entry.js index 013d5f894c013..7544e6953f3e9 100644 --- a/packages/kbn-ui-shared-deps/src/entry.js +++ b/packages/kbn-ui-shared-deps/src/entry.js @@ -56,3 +56,4 @@ export const KbnStd = require('@kbn/std'); export const SaferLodashSet = require('@elastic/safer-lodash-set'); export const RisonNode = require('rison-node'); export const History = require('history'); +export const Classnames = require('classnames'); diff --git a/packages/kbn-ui-shared-deps/src/index.js b/packages/kbn-ui-shared-deps/src/index.js index 3d3553ba23546..31e5e2c3b1e8e 100644 --- a/packages/kbn-ui-shared-deps/src/index.js +++ b/packages/kbn-ui-shared-deps/src/index.js @@ -101,6 +101,7 @@ exports.externals = { '@elastic/safer-lodash-set': '__kbnSharedDeps__.SaferLodashSet', 'rison-node': '__kbnSharedDeps__.RisonNode', history: '__kbnSharedDeps__.History', + classnames: '__kbnSharedDeps__.Classnames', }; /** diff --git a/rfcs/text/0007_lifecycle_unblocked.md b/rfcs/text/0007_lifecycle_unblocked.md index cb978d3dcd7ba..3f347891b2ba8 100644 --- a/rfcs/text/0007_lifecycle_unblocked.md +++ b/rfcs/text/0007_lifecycle_unblocked.md @@ -342,7 +342,6 @@ functions and will be impacted: 2. [tile_map](https://github.com/elastic/kibana/blob/6039709929caf0090a4130b8235f3a53bd04ed84/src/legacy/core_plugins/tile_map/public/plugin.ts#L62) 3. [vis_type_table](https://github.com/elastic/kibana/blob/6039709929caf0090a4130b8235f3a53bd04ed84/src/legacy/core_plugins/vis_type_table/public/plugin.ts#L61) 4. [vis_type_vega](https://github.com/elastic/kibana/blob/6039709929caf0090a4130b8235f3a53bd04ed84/src/legacy/core_plugins/vis_type_vega/public/plugin.ts#L59) -5. [timelion](https://github.com/elastic/kibana/blob/9d69b72a5f200e58220231035b19da852fc6b0a5/src/plugins/timelion/server/plugin.ts#L40) 6. [code](https://github.com/elastic/kibana/blob/5049b460b47d4ae3432e1d9219263bb4be441392/x-pack/legacy/plugins/code/server/plugin.ts#L129-L149) 7. [spaces](https://github.com/elastic/kibana/blob/096c7ee51136327f778845c636d7c4f1188e5db2/x-pack/legacy/plugins/spaces/server/new_platform/plugin.ts#L95) 8. [licensing](https://github.com/elastic/kibana/blob/4667c46caef26f8f47714504879197708debae32/x-pack/plugins/licensing/server/plugin.ts) diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 958cae8816efa..72fa6c5553f77 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -273,7 +273,6 @@ export class DocLinksService { }, visualize: { guide: `${KIBANA_DOCS}dashboard.html`, - timelionDeprecation: `${KIBANA_DOCS}timelion.html`, lens: `${ELASTIC_WEBSITE_URL}what-is/kibana-lens`, lensPanels: `${KIBANA_DOCS}lens.html`, maps: `${ELASTIC_WEBSITE_URL}maps`, diff --git a/src/core/server/deprecations/README.mdx b/src/core/server/deprecations/README.mdx index 197b25ac909c3..533f607c3d4c2 100644 --- a/src/core/server/deprecations/README.mdx +++ b/src/core/server/deprecations/README.mdx @@ -139,7 +139,6 @@ Plugins are responsible for registering any deprecations during the `setup` life the deprecations service. Examples of non-config deprecations include things like -- timelion sheets - kibana_user security roles This service is not intended to be used for non-user facing deprecations or cases where the deprecation diff --git a/src/core/server/deprecations/deprecations_service.ts b/src/core/server/deprecations/deprecations_service.ts index f6f48d2a88b01..b8a134fbf8cd2 100644 --- a/src/core/server/deprecations/deprecations_service.ts +++ b/src/core/server/deprecations/deprecations_service.ts @@ -37,28 +37,27 @@ import { SavedObjectsClientContract } from '../saved_objects/types'; * * async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecationsContext): Promise { * const deprecations: DeprecationsDetails[] = []; - * const count = await getTimelionSheetsCount(savedObjectsClient); - * + * const count = await getFooCount(savedObjectsClient); * if (count > 0) { * // Example of a manual correctiveAction * deprecations.push({ - * title: i18n.translate('xpack.timelion.deprecations.worksheetsTitle', { - * defaultMessage: 'Timelion worksheets are deprecated' + * title: i18n.translate('xpack.foo.deprecations.title', { + * defaultMessage: `Foo's are deprecated` * }), - * message: i18n.translate('xpack.timelion.deprecations.worksheetsMessage', { - * defaultMessage: 'You have {count} Timelion worksheets. Migrate your Timelion worksheets to a dashboard to continue using them.', + * message: i18n.translate('xpack.foo.deprecations.message', { + * defaultMessage: `You have {count} Foo's. Migrate your Foo's to a dashboard to continue using them.`, * values: { count }, * }), * documentationUrl: - * 'https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html', + * 'https://www.elastic.co/guide/en/kibana/current/foo.html', * level: 'warning', * correctiveActions: { * manualSteps: [ - * i18n.translate('xpack.timelion.deprecations.worksheets.manualStepOneMessage', { + * i18n.translate('xpack.foo.deprecations.manualStepOneMessage', { * defaultMessage: 'Navigate to the Kibana Dashboard and click "Create dashboard".', * }), - * i18n.translate('xpack.timelion.deprecations.worksheets.manualStepTwoMessage', { - * defaultMessage: 'Select Timelion from the "New Visualization" window.', + * i18n.translate('xpack.foo.deprecations.manualStepTwoMessage', { + * defaultMessage: 'Select Foo from the "New Visualization" window.', * }), * ], * api: { diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.ts b/src/core/server/saved_objects/migrations/core/elastic_index.ts index f473b3ed02526..5068c24df3414 100644 --- a/src/core/server/saved_objects/migrations/core/elastic_index.ts +++ b/src/core/server/saved_objects/migrations/core/elastic_index.ts @@ -45,6 +45,8 @@ export const REMOVED_TYPES: string[] = [ 'tsvb-validation-telemetry', // replaced by osquery-manager-usage-metric 'osquery-usage-metric', + // Was removed in 7.16 + 'timelion-sheet', ].sort(); // When migrating from the outdated index we use a read query which excludes diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_v1.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_v1.test.ts index fc01e6a408497..d7a64e7368bf4 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_v1.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_v1.test.ts @@ -118,18 +118,23 @@ describe('migration v2', () => { .getTypeRegistry() .getAllTypes() .reduce((versionMap, type) => { - if (type.migrations) { - const migrationsMap = - typeof type.migrations === 'function' ? type.migrations() : type.migrations; - const highestVersion = Object.keys(migrationsMap).sort(Semver.compare).reverse()[0]; + const { name, migrations, convertToMultiNamespaceTypeVersion } = type; + if (migrations || convertToMultiNamespaceTypeVersion) { + const migrationsMap = typeof migrations === 'function' ? migrations() : migrations; + const migrationsKeys = migrationsMap ? Object.keys(migrationsMap) : []; + if (convertToMultiNamespaceTypeVersion) { + // Setting this option registers a conversion migration that is reflected in the object's `migrationVersions` field + migrationsKeys.push(convertToMultiNamespaceTypeVersion); + } + const highestVersion = migrationsKeys.sort(Semver.compare).reverse()[0]; return { ...versionMap, - [type.name]: highestVersion, + [name]: highestVersion, }; } else { return { ...versionMap, - [type.name]: undefined, + [name]: undefined, }; } }, {} as Record); diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index c9b6fa3d9dda5..cee43fd85c90f 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -200,7 +200,6 @@ kibana_vars=( tilemap.options.minZoom tilemap.options.subdomains tilemap.url - timelion.enabled url_drilldown.enabled vega.enableExternalUrls vis_type_vega.enableExternalUrls diff --git a/src/plugins/discover/public/application/components/discover_grid/constants.ts b/src/plugins/discover/public/application/components/discover_grid/constants.ts index 34e6ca20740ad..1126c6cbbf279 100644 --- a/src/plugins/discover/public/application/components/discover_grid/constants.ts +++ b/src/plugins/discover/public/application/components/discover_grid/constants.ts @@ -17,6 +17,7 @@ export const gridStyle = { export const pageSizeArr = [25, 50, 100, 250]; export const defaultPageSize = 100; +export const defaultTimeColumnWidth = 190; export const toolbarVisibility = { showColumnSelector: { allowHide: false, diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx index 3cbac90aa39cb..46e30dd23525b 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx @@ -119,7 +119,7 @@ describe('Discover grid columns ', function () { ], "display": "Time (timestamp)", "id": "timestamp", - "initialWidth": 180, + "initialWidth": 190, "isSortable": true, "schema": "datetime", }, diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx index 3a27772662b56..2f4c0b5167df8 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx @@ -11,10 +11,11 @@ import { i18n } from '@kbn/i18n'; import { EuiDataGridColumn, EuiScreenReaderOnly } from '@elastic/eui'; import { ExpandButton } from './discover_grid_expand_button'; import { DiscoverGridSettings } from './types'; -import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; +import type { IndexPattern } from '../../../../../data/common'; import { buildCellActions } from './discover_grid_cell_actions'; import { getSchemaByKbnType } from './discover_grid_schema'; import { SelectButton } from './discover_grid_document_selection'; +import { defaultTimeColumnWidth } from './constants'; export function getLeadControlColumns() { return [ @@ -88,7 +89,7 @@ export function buildEuiGridColumn( if (column.id === indexPattern.timeFieldName) { column.display = `${timeString} (${indexPattern.timeFieldName})`; - column.initialWidth = 180; + column.initialWidth = defaultTimeColumnWidth; } if (columnWidth > 0) { column.initialWidth = Number(columnWidth); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx index c5b75dbe85aea..e4b67c49689ab 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx @@ -107,6 +107,7 @@ export function DiscoverGridFlyout({ size="m" data-test-subj="docTableDetailsFlyout" onKeyDown={onKeyDown} + ownFocus={false} > )} {activePage !== -1 && ( - + It exists for legacy purposes, and may still be used by Monitoring via Metricbeat. | - | +| **Kibana** | It reports the number of Saved Objects per type. It is limited to `dashboard`, `visualization`, `search`, `index-pattern`, `graph-workspace`.
It exists for legacy purposes, and may still be used by Monitoring via Metricbeat. | - | | **Saved Objects Counts** | Number of Saved Objects per type. | - | | **Localization data** | Localization settings: setup locale and installed translation files. | - | | **Ops stats** | Operation metrics from the system. | - | 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 d29d6d6a86e85..5f268a6fdfee7 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 @@ -124,7 +124,6 @@ export const applicationUsageSchema = { kibana: commonSchema, // It's a forward app so we'll likely never report it management: commonSchema, short_url_redirect: commonSchema, // It's a forward app so we'll likely never report it - timelion: commonSchema, visualize: commonSchema, error: commonSchema, status: commonSchema, diff --git a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/aws.test.ts b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/aws.test.ts index 0bba64823a3e2..68583502d3c9a 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/aws.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/aws.test.ts @@ -6,120 +6,120 @@ * Side Public License, v 1. */ -import fs from 'fs'; -import type { Request, RequestOptions } from './cloud_service'; +/* eslint-disable dot-notation */ +jest.mock('node-fetch'); +jest.mock('fs/promises'); import { AWSCloudService, AWSResponse } from './aws'; -type Callback = (err: unknown, res: unknown) => void; - -const AWS = new AWSCloudService(); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const fetchMock = require('node-fetch') as jest.Mock; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { readFile } = require('fs/promises') as { readFile: jest.Mock }; describe('AWS', () => { - const expectedFilenames = ['/sys/hypervisor/uuid', '/sys/devices/virtual/dmi/id/product_uuid']; - const expectedEncoding = 'utf8'; - // mixed case to ensure we check for ec2 after lowercasing - const ec2Uuid = 'eC2abcdef-ghijk\n'; - const ec2FileSystem = { - readFile: (filename: string, encoding: string, callback: Callback) => { - expect(expectedFilenames).toContain(filename); - expect(encoding).toEqual(expectedEncoding); - - callback(null, ec2Uuid); - }, - } as typeof fs; + const mockIsWindows = jest.fn(); + const awsService = new AWSCloudService(); + awsService['_isWindows'] = mockIsWindows.mockReturnValue(false); + readFile.mockResolvedValue('eC2abcdef-ghijk\n'); + beforeEach(() => jest.clearAllMocks()); it('is named "aws"', () => { - expect(AWS.getName()).toEqual('aws'); + expect(awsService.getName()).toEqual('aws'); }); describe('_checkIfService', () => { it('handles expected response', async () => { const id = 'abcdef'; - const request = ((req: RequestOptions, callback: Callback) => { - expect(req.method).toEqual('GET'); - expect(req.uri).toEqual( - 'http://169.254.169.254/2016-09-02/dynamic/instance-identity/document' - ); - expect(req.json).toEqual(true); - - const body = `{"instanceId": "${id}","availabilityZone":"us-fake-2c", "imageId" : "ami-6df1e514"}`; - - callback(null, { statusCode: 200, body }); - }) as Request; - // ensure it does not use the fs to trump the body - const awsCheckedFileSystem = new AWSCloudService({ - _fs: ec2FileSystem, - _isWindows: false, + + fetchMock.mockResolvedValue({ + json: () => + `{"instanceId": "${id}","availabilityZone":"us-fake-2c", "imageId" : "ami-6df1e514"}`, + status: 200, + ok: true, }); - const response = await awsCheckedFileSystem._checkIfService(request); + const response = await awsService['_checkIfService'](); + expect(readFile).toBeCalledTimes(0); + expect(fetchMock).toBeCalledTimes(1); + expect(fetchMock).toBeCalledWith( + 'http://169.254.169.254/2016-09-02/dynamic/instance-identity/document', + { + method: 'GET', + } + ); expect(response.isConfirmed()).toEqual(true); - expect(response.toJSON()).toEqual({ - name: AWS.getName(), - id, - region: undefined, - vm_type: undefined, - zone: 'us-fake-2c', - metadata: { - imageId: 'ami-6df1e514', - }, - }); + expect(response.toJSON()).toMatchInlineSnapshot(` + Object { + "id": "abcdef", + "metadata": Object { + "imageId": "ami-6df1e514", + }, + "name": "aws", + "region": undefined, + "vm_type": undefined, + "zone": "us-fake-2c", + } + `); }); it('handles request without a usable body by downgrading to UUID detection', async () => { - const request = ((_req: RequestOptions, callback: Callback) => - callback(null, { statusCode: 404 })) as Request; - const awsCheckedFileSystem = new AWSCloudService({ - _fs: ec2FileSystem, - _isWindows: false, + fetchMock.mockResolvedValue({ + json: () => null, + status: 200, + ok: true, }); - const response = await awsCheckedFileSystem._checkIfService(request); + const response = await awsService['_checkIfService'](); expect(response.isConfirmed()).toBe(true); - expect(response.toJSON()).toEqual({ - name: AWS.getName(), - id: ec2Uuid.trim().toLowerCase(), - region: undefined, - vm_type: undefined, - zone: undefined, - metadata: undefined, - }); + expect(response.toJSON()).toMatchInlineSnapshot(` + Object { + "id": "ec2abcdef-ghijk", + "metadata": undefined, + "name": "aws", + "region": undefined, + "vm_type": undefined, + "zone": undefined, + } + `); }); it('handles request failure by downgrading to UUID detection', async () => { - const failedRequest = ((_req: RequestOptions, callback: Callback) => - callback(new Error('expected: request failed'), null)) as Request; - const awsCheckedFileSystem = new AWSCloudService({ - _fs: ec2FileSystem, - _isWindows: false, + fetchMock.mockResolvedValue({ + status: 404, + ok: false, }); - const response = await awsCheckedFileSystem._checkIfService(failedRequest); + const response = await awsService['_checkIfService'](); expect(response.isConfirmed()).toBe(true); - expect(response.toJSON()).toEqual({ - name: AWS.getName(), - id: ec2Uuid.trim().toLowerCase(), - region: undefined, - vm_type: undefined, - zone: undefined, - metadata: undefined, - }); + expect(response.toJSON()).toMatchInlineSnapshot(` + Object { + "id": "ec2abcdef-ghijk", + "metadata": undefined, + "name": "aws", + "region": undefined, + "vm_type": undefined, + "zone": undefined, + } + `); }); it('handles not running on AWS', async () => { - const failedRequest = ((_req: RequestOptions, callback: Callback) => - callback(null, null)) as Request; - const awsIgnoredFileSystem = new AWSCloudService({ - _fs: ec2FileSystem, - _isWindows: true, + fetchMock.mockResolvedValue({ + json: () => null, + status: 404, + ok: false, }); - const response = await awsIgnoredFileSystem._checkIfService(failedRequest); + mockIsWindows.mockReturnValue(true); + + const response = await awsService['_checkIfService'](); + expect(mockIsWindows).toBeCalledTimes(1); + expect(readFile).toBeCalledTimes(0); - expect(response.getName()).toEqual(AWS.getName()); + expect(response.getName()).toEqual('aws'); expect(response.isConfirmed()).toBe(false); }); }); @@ -144,10 +144,10 @@ describe('AWS', () => { marketplaceProductCodes: null, }; - const response = AWSCloudService.parseBody(AWS.getName(), body)!; + const response = awsService.parseBody(body)!; expect(response).not.toBeNull(); - expect(response.getName()).toEqual(AWS.getName()); + expect(response.getName()).toEqual('aws'); expect(response.isConfirmed()).toEqual(true); expect(response.toJSON()).toEqual({ name: 'aws', @@ -169,141 +169,84 @@ describe('AWS', () => { it('ignores unexpected response body', () => { // @ts-expect-error - expect(AWSCloudService.parseBody(AWS.getName(), undefined)).toBe(null); + expect(awsService.parseBody(undefined)).toBe(null); // @ts-expect-error - expect(AWSCloudService.parseBody(AWS.getName(), null)).toBe(null); + expect(awsService.parseBody(null)).toBe(null); // @ts-expect-error - expect(AWSCloudService.parseBody(AWS.getName(), {})).toBe(null); + expect(awsService.parseBody({})).toBe(null); // @ts-expect-error - expect(AWSCloudService.parseBody(AWS.getName(), { privateIp: 'a.b.c.d' })).toBe(null); + expect(awsService.parseBody({ privateIp: 'a.b.c.d' })).toBe(null); }); }); - describe('_tryToDetectUuid', () => { + describe('tryToDetectUuid', () => { describe('checks the file system for UUID if not Windows', () => { - it('checks /sys/hypervisor/uuid', async () => { - const awsCheckedFileSystem = new AWSCloudService({ - _fs: { - readFile: (filename: string, encoding: string, callback: Callback) => { - expect(expectedFilenames).toContain(filename); - expect(encoding).toEqual(expectedEncoding); - - callback(null, ec2Uuid); - }, - } as typeof fs, - _isWindows: false, - }); + beforeAll(() => mockIsWindows.mockReturnValue(false)); - const response = await awsCheckedFileSystem._tryToDetectUuid(); + it('checks /sys/hypervisor/uuid and /sys/devices/virtual/dmi/id/product_uuid', async () => { + const response = await awsService['tryToDetectUuid'](); - expect(response.isConfirmed()).toEqual(true); - expect(response.toJSON()).toEqual({ - name: AWS.getName(), - id: ec2Uuid.trim().toLowerCase(), - region: undefined, - zone: undefined, - vm_type: undefined, - metadata: undefined, - }); - }); + readFile.mockImplementation(async (filename: string, encoding: string) => { + expect(['/sys/hypervisor/uuid', '/sys/devices/virtual/dmi/id/product_uuid']).toContain( + filename + ); + expect(encoding).toEqual('utf8'); - it('checks /sys/devices/virtual/dmi/id/product_uuid', async () => { - const awsCheckedFileSystem = new AWSCloudService({ - _fs: { - readFile: (filename: string, encoding: string, callback: Callback) => { - expect(expectedFilenames).toContain(filename); - expect(encoding).toEqual(expectedEncoding); - - callback(null, ec2Uuid); - }, - } as typeof fs, - _isWindows: false, + return 'eC2abcdef-ghijk\n'; }); - const response = await awsCheckedFileSystem._tryToDetectUuid(); - + expect(readFile).toBeCalledTimes(2); expect(response.isConfirmed()).toEqual(true); - expect(response.toJSON()).toEqual({ - name: AWS.getName(), - id: ec2Uuid.trim().toLowerCase(), - region: undefined, - zone: undefined, - vm_type: undefined, - metadata: undefined, - }); + expect(response.toJSON()).toMatchInlineSnapshot(` + Object { + "id": "ec2abcdef-ghijk", + "metadata": undefined, + "name": "aws", + "region": undefined, + "vm_type": undefined, + "zone": undefined, + } + `); }); it('returns confirmed if only one file exists', async () => { - let callCount = 0; - const awsCheckedFileSystem = new AWSCloudService({ - _fs: { - readFile: (filename: string, encoding: string, callback: Callback) => { - if (callCount === 0) { - callCount++; - throw new Error('oops'); - } - callback(null, ec2Uuid); - }, - } as typeof fs, - _isWindows: false, - }); + readFile.mockRejectedValueOnce(new Error('oops')); + readFile.mockResolvedValueOnce('ec2Uuid'); - const response = await awsCheckedFileSystem._tryToDetectUuid(); + const response = await awsService['tryToDetectUuid'](); + expect(readFile).toBeCalledTimes(2); expect(response.isConfirmed()).toEqual(true); - expect(response.toJSON()).toEqual({ - name: AWS.getName(), - id: ec2Uuid.trim().toLowerCase(), - region: undefined, - zone: undefined, - vm_type: undefined, - metadata: undefined, - }); + expect(response.toJSON()).toMatchInlineSnapshot(` + Object { + "id": "ec2uuid", + "metadata": undefined, + "name": "aws", + "region": undefined, + "vm_type": undefined, + "zone": undefined, + } + `); }); it('returns unconfirmed if all files return errors', async () => { - const awsFailedFileSystem = new AWSCloudService({ - _fs: ({ - readFile: () => { - throw new Error('oops'); - }, - } as unknown) as typeof fs, - _isWindows: false, - }); - - const response = await awsFailedFileSystem._tryToDetectUuid(); + readFile.mockRejectedValue(new Error('oops')); + const response = await awsService['tryToDetectUuid'](); expect(response.isConfirmed()).toEqual(false); }); - }); - it('ignores UUID if it does not start with ec2', async () => { - const notEC2FileSystem = { - readFile: (filename: string, encoding: string, callback: Callback) => { - expect(expectedFilenames).toContain(filename); - expect(encoding).toEqual(expectedEncoding); + it('ignores UUID if it does not start with ec2', async () => { + readFile.mockResolvedValue('notEC2'); - callback(null, 'notEC2'); - }, - } as typeof fs; - - const awsCheckedFileSystem = new AWSCloudService({ - _fs: notEC2FileSystem, - _isWindows: false, + const response = await awsService['tryToDetectUuid'](); + expect(response.isConfirmed()).toEqual(false); }); - - const response = await awsCheckedFileSystem._tryToDetectUuid(); - - expect(response.isConfirmed()).toEqual(false); }); it('does NOT check the file system for UUID on Windows', async () => { - const awsUncheckedFileSystem = new AWSCloudService({ - _fs: ec2FileSystem, - _isWindows: true, - }); - - const response = await awsUncheckedFileSystem._tryToDetectUuid(); + mockIsWindows.mockReturnValue(true); + const response = await awsService['tryToDetectUuid'](); expect(response.isConfirmed()).toEqual(false); }); diff --git a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/aws.ts b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/aws.ts index 69e5698489b30..785313e752c5e 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/aws.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/aws.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import fs from 'fs'; -import { get, isString, omit } from 'lodash'; -import { promisify } from 'util'; -import { CloudService, CloudServiceOptions, Request, RequestOptions } from './cloud_service'; +import { readFile } from 'fs/promises'; +import { get, omit } from 'lodash'; +import fetch from 'node-fetch'; +import { CloudService } from './cloud_service'; import { CloudServiceResponse } from './cloud_response'; // We explicitly call out the version, 2016-09-02, rather than 'latest' to avoid unexpected changes @@ -40,9 +40,9 @@ export interface AWSResponse { * @internal */ export class AWSCloudService extends CloudService { - private readonly _isWindows: boolean; - private readonly _fs: typeof fs; - + constructor() { + super('aws'); + } /** * Parse the AWS response, if possible. * @@ -64,7 +64,8 @@ export class AWSCloudService extends CloudService { * "version" : "2010-08-31", * } */ - static parseBody(name: string, body: AWSResponse): CloudServiceResponse | null { + parseBody = (body: AWSResponse): CloudServiceResponse | null => { + const name = this.getName(); const id: string | undefined = get(body, 'instanceId'); const vmType: string | undefined = get(body, 'instanceType'); const region: string | undefined = get(body, 'region'); @@ -88,64 +89,60 @@ export class AWSCloudService extends CloudService { } return null; - } + }; - constructor(options: CloudServiceOptions = {}) { - super('aws', options); + private _isWindows = (): boolean => { + return process.platform.startsWith('win'); + }; - // Allow the file system handler to be swapped out for tests - const { _fs = fs, _isWindows = process.platform.startsWith('win') } = options; - - this._fs = _fs; - this._isWindows = _isWindows; - } + protected _checkIfService = async () => { + try { + const response = await fetch(SERVICE_ENDPOINT, { + method: 'GET', + }); - async _checkIfService(request: Request) { - const req: RequestOptions = { - method: 'GET', - uri: SERVICE_ENDPOINT, - json: true, - }; + if (!response.ok || response.status === 404) { + throw new Error('AWS request failed'); + } - return promisify(request)(req) - .then((response) => - this._parseResponse(response.body, (body) => - AWSCloudService.parseBody(this.getName(), body) - ) - ) - .catch(() => this._tryToDetectUuid()); - } + const jsonBody: AWSResponse = await response.json(); + return this._parseResponse(jsonBody, this.parseBody); + } catch (_) { + return this.tryToDetectUuid(); + } + }; /** * Attempt to load the UUID by checking `/sys/hypervisor/uuid`. * * This is a fallback option if the metadata service is unavailable for some reason. */ - _tryToDetectUuid() { + private tryToDetectUuid = async () => { + const isWindows = this._isWindows(); // Windows does not have an easy way to check - if (!this._isWindows) { + if (!isWindows) { const pathsToCheck = ['/sys/hypervisor/uuid', '/sys/devices/virtual/dmi/id/product_uuid']; - const promises = pathsToCheck.map((path) => promisify(this._fs.readFile)(path, 'utf8')); - - return Promise.allSettled(promises).then((responses) => { - for (const response of responses) { - let uuid; - if (response.status === 'fulfilled' && isString(response.value)) { - // Some AWS APIs return it lowercase (like the file did in testing), while others return it uppercase - uuid = response.value.trim().toLowerCase(); - - // There is a small chance of a false positive here in the unlikely event that a uuid which doesn't - // belong to ec2 happens to be generated with `ec2` as the first three characters. - if (uuid.startsWith('ec2')) { - return new CloudServiceResponse(this._name, true, { id: uuid }); - } + const responses = await Promise.allSettled( + pathsToCheck.map((path) => readFile(path, 'utf8')) + ); + + for (const response of responses) { + let uuid; + if (response.status === 'fulfilled' && typeof response.value === 'string') { + // Some AWS APIs return it lowercase (like the file did in testing), while others return it uppercase + uuid = response.value.trim().toLowerCase(); + + // There is a small chance of a false positive here in the unlikely event that a uuid which doesn't + // belong to ec2 happens to be generated with `ec2` as the first three characters. + if (uuid.startsWith('ec2')) { + return new CloudServiceResponse(this._name, true, { id: uuid }); } } + } - return this._createUnconfirmedResponse(); - }); + return this._createUnconfirmedResponse(); } - return Promise.resolve(this._createUnconfirmedResponse()); - } + return this._createUnconfirmedResponse(); + }; } diff --git a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/azure.test.ts b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/azure.test.ts index 17205562fa335..5bdbbbda55de6 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/azure.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/azure.test.ts @@ -6,36 +6,47 @@ * Side Public License, v 1. */ -import type { Request, RequestOptions } from './cloud_service'; +/* eslint-disable dot-notation */ +jest.mock('node-fetch'); import { AzureCloudService } from './azure'; -type Callback = (err: unknown, res: unknown) => void; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const fetchMock = require('node-fetch') as jest.Mock; -const AZURE = new AzureCloudService(); - -describe('Azure', () => { +describe('AzureCloudService', () => { + const azureCloudService = new AzureCloudService(); it('is named "azure"', () => { - expect(AZURE.getName()).toEqual('azure'); + expect(azureCloudService.getName()).toEqual('azure'); }); describe('_checkIfService', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + it('handles expected response', async () => { const id = 'abcdef'; - const request = ((req: RequestOptions, callback: Callback) => { - expect(req.method).toEqual('GET'); - expect(req.uri).toEqual('http://169.254.169.254/metadata/instance?api-version=2017-04-02'); - expect(req.headers?.Metadata).toEqual('true'); - expect(req.json).toEqual(true); + fetchMock.mockResolvedValue({ + json: () => + `{"compute":{"vmId": "${id}","location":"fakeus","availabilityZone":"fakeus-2"}}`, + status: 200, + ok: true, + }); - const body = `{"compute":{"vmId": "${id}","location":"fakeus","availabilityZone":"fakeus-2"}}`; + const response = await azureCloudService['_checkIfService'](); - callback(null, { statusCode: 200, body }); - }) as Request; - const response = await AZURE._checkIfService(request); + expect(fetchMock).toBeCalledTimes(1); + expect(fetchMock).toBeCalledWith( + 'http://169.254.169.254/metadata/instance?api-version=2017-04-02', + { + method: 'GET', + headers: { Metadata: 'true' }, + } + ); expect(response.isConfirmed()).toEqual(true); expect(response.toJSON()).toEqual({ - name: AZURE.getName(), + name: azureCloudService.getName(), id, region: 'fakeus', vm_type: undefined, @@ -49,34 +60,30 @@ describe('Azure', () => { // NOTE: the CloudService method, checkIfService, catches the errors that follow it('handles not running on Azure with error by rethrowing it', async () => { const someError = new Error('expected: request failed'); - const failedRequest = ((_req: RequestOptions, callback: Callback) => - callback(someError, null)) as Request; + fetchMock.mockRejectedValue(someError); - expect(async () => { - await AZURE._checkIfService(failedRequest); - }).rejects.toThrowError(someError.message); + await expect(() => azureCloudService['_checkIfService']()).rejects.toThrowError( + someError.message + ); }); it('handles not running on Azure with 404 response by throwing error', async () => { - const failedRequest = ((_req: RequestOptions, callback: Callback) => - callback(null, { statusCode: 404 })) as Request; + fetchMock.mockResolvedValue({ status: 404 }); - expect(async () => { - await AZURE._checkIfService(failedRequest); - }).rejects.toThrowErrorMatchingInlineSnapshot(`"Azure request failed"`); + await expect(() => + azureCloudService['_checkIfService']() + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Azure request failed"`); }); it('handles not running on Azure with unexpected response by throwing error', async () => { - const failedRequest = ((_req: RequestOptions, callback: Callback) => - callback(null, null)) as Request; - - expect(async () => { - await AZURE._checkIfService(failedRequest); - }).rejects.toThrowErrorMatchingInlineSnapshot(`"Azure request failed"`); + fetchMock.mockResolvedValue({ ok: false }); + await expect(() => + azureCloudService['_checkIfService']() + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Azure request failed"`); }); }); - describe('_parseBody', () => { + describe('parseBody', () => { // it's expected that most users use the resource manager UI (which has been out for years) it('parses object in expected format', () => { const body = { @@ -119,10 +126,10 @@ describe('Azure', () => { }, }; - const response = AzureCloudService.parseBody(AZURE.getName(), body)!; + const response = azureCloudService['parseBody'](body)!; expect(response).not.toBeNull(); - expect(response.getName()).toEqual(AZURE.getName()); + expect(response.getName()).toEqual(azureCloudService.getName()); expect(response.isConfirmed()).toEqual(true); expect(response.toJSON()).toEqual({ name: 'azure', @@ -172,10 +179,10 @@ describe('Azure', () => { }, }; - const response = AzureCloudService.parseBody(AZURE.getName(), body)!; + const response = azureCloudService['parseBody'](body)!; expect(response).not.toBeNull(); - expect(response.getName()).toEqual(AZURE.getName()); + expect(response.getName()).toEqual(azureCloudService.getName()); expect(response.isConfirmed()).toEqual(true); expect(response.toJSON()).toEqual({ name: 'azure', @@ -191,13 +198,13 @@ describe('Azure', () => { it('ignores unexpected response body', () => { // @ts-expect-error - expect(AzureCloudService.parseBody(AZURE.getName(), undefined)).toBe(null); + expect(azureCloudService['parseBody'](undefined)).toBe(null); // @ts-expect-error - expect(AzureCloudService.parseBody(AZURE.getName(), null)).toBe(null); + expect(azureCloudService['parseBody'](null)).toBe(null); // @ts-expect-error - expect(AzureCloudService.parseBody(AZURE.getName(), {})).toBe(null); + expect(azureCloudService['parseBody']({})).toBe(null); // @ts-expect-error - expect(AzureCloudService.parseBody(AZURE.getName(), { privateIp: 'a.b.c.d' })).toBe(null); + expect(azureCloudService['parseBody']({ privateIp: 'a.b.c.d' })).toBe(null); }); }); }); diff --git a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/azure.ts b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/azure.ts index b846636f0ce6c..06a135960bd60 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/azure.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/azure.ts @@ -7,8 +7,8 @@ */ import { get, omit } from 'lodash'; -import { promisify } from 'util'; -import { CloudService, Request } from './cloud_service'; +import fetch from 'node-fetch'; +import { CloudService } from './cloud_service'; import { CloudServiceResponse } from './cloud_response'; // 2017-04-02 is the first GA release of this API @@ -25,6 +25,9 @@ interface AzureResponse { * @internal */ export class AzureCloudService extends CloudService { + constructor() { + super('azure'); + } /** * Parse the Azure response, if possible. * @@ -51,7 +54,8 @@ export class AzureCloudService extends CloudService { * } * } */ - static parseBody(name: string, body: AzureResponse): CloudServiceResponse | null { + private parseBody = (body: AzureResponse): CloudServiceResponse | null => { + const name = this.getName(); const compute: Record | undefined = get(body, 'compute'); const id = get, string>(compute, 'vmId'); const vmType = get, string>(compute, 'vmSize'); @@ -72,32 +76,22 @@ export class AzureCloudService extends CloudService { } return null; - } - - constructor(options = {}) { - super('azure', options); - } + }; - async _checkIfService(request: Request) { - const req = { + protected _checkIfService = async () => { + const response = await fetch(SERVICE_ENDPOINT, { method: 'GET', - uri: SERVICE_ENDPOINT, headers: { // Azure requires this header Metadata: 'true', }, - json: true, - }; - - const response = await promisify(request)(req); + }); - // Note: there is no fallback option for Azure - if (!response || response.statusCode === 404) { + if (!response.ok || response.status === 404) { throw new Error('Azure request failed'); } - return this._parseResponse(response.body, (body) => - AzureCloudService.parseBody(this.getName(), body) - ); - } + const jsonBody: AzureResponse = await response.json(); + return this._parseResponse(jsonBody, this.parseBody); + }; } diff --git a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_detector.ts b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_detector.ts index 3d093c81f8896..9930110979b87 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_detector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_detector.ts @@ -37,9 +37,9 @@ export class CloudDetector { /** * Get any cloud details that we have detected. */ - getCloudDetails() { + public getCloudDetails = () => { return this.cloudDetails; - } + }; /** * Asynchronously detect the cloud service. @@ -48,9 +48,9 @@ export class CloudDetector { * caller to trigger the lookup and then simply use it whenever we * determine it. */ - async detectCloudService() { + public detectCloudService = async () => { this.cloudDetails = await this.getCloudService(); - } + }; /** * Check every cloud service until the first one reports success from detection. diff --git a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_service.test.ts b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_service.test.ts index 0a7d5899486ab..22bef6753e9cf 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_service.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_service.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { CloudService, Response } from './cloud_service'; +import { CloudService } from './cloud_service'; import { CloudServiceResponse } from './cloud_response'; describe('CloudService', () => { @@ -30,9 +30,9 @@ describe('CloudService', () => { describe('_checkIfService', () => { it('throws an exception unless overridden', async () => { - expect(async () => { - await service._checkIfService(undefined); - }).rejects.toThrowErrorMatchingInlineSnapshot(`"not implemented"`); + await expect(() => + service._checkIfService(undefined) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"not implemented"`); }); }); @@ -88,52 +88,59 @@ describe('CloudService', () => { describe('_parseResponse', () => { const body = { some: { body: {} } }; - it('throws error upon failure to parse body as object', async () => { - expect(async () => { - await service._parseResponse(); - }).rejects.toMatchInlineSnapshot(`undefined`); - expect(async () => { - await service._parseResponse(null); - }).rejects.toMatchInlineSnapshot(`undefined`); - expect(async () => { - await service._parseResponse({}); - }).rejects.toMatchInlineSnapshot(`undefined`); - expect(async () => { - await service._parseResponse(123); - }).rejects.toMatchInlineSnapshot(`undefined`); - expect(async () => { - await service._parseResponse('raw string'); - }).rejects.toMatchInlineSnapshot(`[Error: 'raw string' is not a JSON object]`); - expect(async () => { - await service._parseResponse('{{}'); - }).rejects.toMatchInlineSnapshot(`[Error: '{{}' is not a JSON object]`); + it('throws error upon failure to parse body as object', () => { + expect(() => service._parseResponse()).toThrowErrorMatchingInlineSnapshot( + `"Unable to handle body"` + ); + expect(() => service._parseResponse(null)).toThrowErrorMatchingInlineSnapshot( + `"Unable to handle body"` + ); + expect(() => service._parseResponse({})).toThrowErrorMatchingInlineSnapshot( + `"Unable to handle body"` + ); + expect(() => service._parseResponse(123)).toThrowErrorMatchingInlineSnapshot( + `"Unable to handle body"` + ); + expect(() => service._parseResponse('raw string')).toThrowErrorMatchingInlineSnapshot( + `"'raw string' is not a JSON object"` + ); + expect(() => service._parseResponse('{{}')).toThrowErrorMatchingInlineSnapshot( + `"'{{}' is not a JSON object"` + ); }); - it('expects unusable bodies', async () => { - const parseBody = (parsedBody: Response['body']) => { - expect(parsedBody).toEqual(body); - - return null; - }; - - expect(async () => { - await service._parseResponse(JSON.stringify(body), parseBody); - }).rejects.toMatchInlineSnapshot(`undefined`); - expect(async () => { - await service._parseResponse(body, parseBody); - }).rejects.toMatchInlineSnapshot(`undefined`); + it('expects unusable bodies', () => { + const parseBody = jest.fn().mockReturnValue(null); + + expect(() => + service._parseResponse(JSON.stringify(body), parseBody) + ).toThrowErrorMatchingInlineSnapshot(`"Unable to handle body"`); + expect(parseBody).toBeCalledTimes(1); + expect(parseBody).toBeCalledWith(body); + parseBody.mockClear(); + + expect(() => service._parseResponse(body, parseBody)).toThrowErrorMatchingInlineSnapshot( + `"Unable to handle body"` + ); + expect(parseBody).toBeCalledTimes(1); + expect(parseBody).toBeCalledWith(body); }); it('uses parsed object to create response', async () => { const serviceResponse = new CloudServiceResponse('a123', true, { id: 'xyz' }); - const parseBody = (parsedBody: Response['body']) => { - expect(parsedBody).toEqual(body); - - return serviceResponse; - }; + const parseBody = jest.fn().mockReturnValue(serviceResponse); const response = await service._parseResponse(body, parseBody); + expect(parseBody).toBeCalledWith(body); + expect(response).toBe(serviceResponse); + }); + + it('parses object before passing it to parseBody to create response', async () => { + const serviceResponse = new CloudServiceResponse('a123', true, { id: 'xyz' }); + const parseBody = jest.fn().mockReturnValue(serviceResponse); + const response = await service._parseResponse(JSON.stringify(body), parseBody); + expect(parseBody).toBeCalledWith(body); expect(response).toBe(serviceResponse); }); }); diff --git a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_service.ts b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_service.ts index 768a46a457d7d..bea51437d25c4 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_service.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/cloud_service.ts @@ -6,81 +6,56 @@ * Side Public License, v 1. */ -import fs from 'fs'; -import { isObject, isString, isPlainObject } from 'lodash'; -import defaultRequest from 'request'; -import type { OptionsWithUri, Response as DefaultResponse } from 'request'; +import { isObject, isPlainObject } from 'lodash'; import { CloudServiceResponse } from './cloud_response'; -/** @internal */ -export type Request = typeof defaultRequest; - -/** @internal */ -export type RequestOptions = OptionsWithUri; - -/** @internal */ -export type Response = DefaultResponse; - -/** @internal */ -export interface CloudServiceOptions { - _request?: Request; - _fs?: typeof fs; - _isWindows?: boolean; -} - /** * CloudService provides a mechanism for cloud services to be checked for * metadata that may help to determine the best defaults and priorities. */ export abstract class CloudService { - private readonly _request: Request; protected readonly _name: string; - constructor(name: string, options: CloudServiceOptions = {}) { + constructor(name: string) { this._name = name.toLowerCase(); - - // Allow the HTTP handler to be swapped out for tests - const { _request = defaultRequest } = options; - - this._request = _request; } /** * Get the search-friendly name of the Cloud Service. */ - getName() { + public getName = () => { return this._name; - } + }; /** * Using whatever mechanism is required by the current Cloud Service, * determine if Kibana is running in it and return relevant metadata. */ - async checkIfService() { + public checkIfService = async () => { try { - return await this._checkIfService(this._request); + return await this._checkIfService(); } catch (e) { return this._createUnconfirmedResponse(); } - } + }; - _checkIfService(request: Request): Promise { + protected _checkIfService = async (): Promise => { // should always be overridden by a subclass return Promise.reject(new Error('not implemented')); - } + }; /** * Create a new CloudServiceResponse that denotes that this cloud service * is not being used by the current machine / VM. */ - _createUnconfirmedResponse() { + protected _createUnconfirmedResponse = () => { return CloudServiceResponse.unconfirmed(this._name); - } + }; /** * Strictly parse JSON. */ - _stringToJson(value: string) { + protected _stringToJson = (value: string) => { // note: this will throw an error if this is not a string value = value.trim(); @@ -94,7 +69,7 @@ export abstract class CloudService { } catch (e) { throw new Error(`'${value}' is not a JSON object`); } - } + }; /** * Convert the response to a JSON object and attempt to parse it using the @@ -103,28 +78,21 @@ export abstract class CloudService { * If the response cannot be parsed as a JSON object, or if it fails to be * useful, then parseBody should return null. */ - _parseResponse( - body: Response['body'], - parseBody?: (body: Response['body']) => CloudServiceResponse | null - ): Promise { + protected _parseResponse = ( + body: string | Body, + parseBodyFn: (body: Body) => CloudServiceResponse | null + ): CloudServiceResponse => { // parse it if necessary - if (isString(body)) { - try { - body = this._stringToJson(body); - } catch (err) { - return Promise.reject(err); - } - } - - if (isObject(body) && parseBody) { - const response = parseBody(body); + const jsonBody: Body = typeof body === 'string' ? this._stringToJson(body) : body; + if (isObject(jsonBody) && typeof parseBodyFn !== 'undefined') { + const response = parseBodyFn(jsonBody); if (response) { - return Promise.resolve(response); + return response; } } // use default handling - return Promise.reject(); - } + throw new Error('Unable to handle body'); + }; } diff --git a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/gcp.test.ts b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/gcp.test.ts index fd0b3331b4ad1..40bd0ef1fa1b1 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/gcp.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/gcp.test.ts @@ -5,136 +5,185 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - -import type { Request, RequestOptions } from './cloud_service'; +/* eslint-disable dot-notation */ +jest.mock('node-fetch'); import { GCPCloudService } from './gcp'; - -type Callback = (err: unknown, res: unknown) => void; - -const GCP = new GCPCloudService(); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const fetchMock = require('node-fetch') as jest.Mock; describe('GCP', () => { + const gcpService = new GCPCloudService(); + beforeEach(() => jest.clearAllMocks()); + it('is named "gcp"', () => { - expect(GCP.getName()).toEqual('gcp'); + expect(gcpService.getName()).toEqual('gcp'); }); describe('_checkIfService', () => { // GCP responds with the header that they expect (and request lowercases the header's name) - const headers = { 'metadata-flavor': 'Google' }; + const headers = new Map(); + headers.set('metadata-flavor', 'Google'); it('handles expected responses', async () => { + const basePath = 'http://169.254.169.254/computeMetadata/v1/instance/'; const metadata: Record = { id: 'abcdef', 'machine-type': 'projects/441331612345/machineTypes/f1-micro', zone: 'projects/441331612345/zones/us-fake4-c', }; - const request = ((req: RequestOptions, callback: Callback) => { - const basePath = 'http://169.254.169.254/computeMetadata/v1/instance/'; - - expect(req.method).toEqual('GET'); - expect((req.uri as string).startsWith(basePath)).toBe(true); - expect(req.headers!['Metadata-Flavor']).toEqual('Google'); - expect(req.json).toEqual(false); - const requestKey = (req.uri as string).substring(basePath.length); - let body = null; + fetchMock.mockImplementation((url: string) => { + const requestKey = url.substring(basePath.length); + let body: string | null = null; if (metadata[requestKey]) { body = metadata[requestKey]; } + return { + status: 200, + ok: true, + text: () => body, + headers, + }; + }); - callback(null, { statusCode: 200, body, headers }); - }) as Request; - const response = await GCP._checkIfService(request); + const response = await gcpService['_checkIfService'](); + const fetchParams = { + headers: { 'Metadata-Flavor': 'Google' }, + method: 'GET', + }; + expect(fetchMock).toBeCalledTimes(3); + expect(fetchMock).toHaveBeenNthCalledWith(1, `${basePath}id`, fetchParams); + expect(fetchMock).toHaveBeenNthCalledWith(2, `${basePath}machine-type`, fetchParams); + expect(fetchMock).toHaveBeenNthCalledWith(3, `${basePath}zone`, fetchParams); expect(response.isConfirmed()).toEqual(true); - expect(response.toJSON()).toEqual({ - name: GCP.getName(), - id: metadata.id, - region: 'us-fake4', - vm_type: 'f1-micro', - zone: 'us-fake4-c', - metadata: undefined, - }); + expect(response.toJSON()).toMatchInlineSnapshot(` + Object { + "id": "abcdef", + "metadata": undefined, + "name": "gcp", + "region": "us-fake4", + "vm_type": "f1-micro", + "zone": "us-fake4-c", + } + `); }); // NOTE: the CloudService method, checkIfService, catches the errors that follow it('handles unexpected responses', async () => { - const request = ((_req: RequestOptions, callback: Callback) => - callback(null, { statusCode: 200, headers })) as Request; + fetchMock.mockResolvedValue({ + status: 200, + ok: true, + headers, + text: () => undefined, + }); - expect(async () => { - await GCP._checkIfService(request); - }).rejects.toThrowErrorMatchingInlineSnapshot(`"unrecognized responses"`); + await expect(() => + gcpService['_checkIfService']() + ).rejects.toThrowErrorMatchingInlineSnapshot(`"unrecognized responses"`); }); it('handles unexpected responses without response header', async () => { - const body = 'xyz'; - const failedRequest = ((_req: RequestOptions, callback: Callback) => - callback(null, { statusCode: 200, body })) as Request; + fetchMock.mockResolvedValue({ + status: 200, + ok: true, + headers: new Map(), + text: () => 'xyz', + }); - expect(async () => { - await GCP._checkIfService(failedRequest); - }).rejects.toThrowErrorMatchingInlineSnapshot(`"unrecognized responses"`); + await expect(() => + gcpService['_checkIfService']() + ).rejects.toThrowErrorMatchingInlineSnapshot(`"GCP request failed"`); }); - it('handles not running on GCP with error by rethrowing it', async () => { + it('handles not running on GCP', async () => { const someError = new Error('expected: request failed'); - const failedRequest = ((_req: RequestOptions, callback: Callback) => - callback(someError, null)) as Request; + fetchMock.mockRejectedValue(someError); - expect(async () => { - await GCP._checkIfService(failedRequest); - }).rejects.toThrowError(someError); + await expect(() => + gcpService['_checkIfService']() + ).rejects.toThrowErrorMatchingInlineSnapshot(`"GCP request failed"`); }); it('handles not running on GCP with 404 response by throwing error', async () => { - const body = 'This is some random error text'; - const failedRequest = ((_req: RequestOptions, callback: Callback) => - callback(null, { statusCode: 404, headers, body })) as Request; + fetchMock.mockResolvedValue({ + status: 404, + ok: false, + headers, + text: () => 'This is some random error text', + }); - expect(async () => { - await GCP._checkIfService(failedRequest); - }).rejects.toThrowErrorMatchingInlineSnapshot(`"GCP request failed"`); + await expect(() => + gcpService['_checkIfService']() + ).rejects.toThrowErrorMatchingInlineSnapshot(`"GCP request failed"`); }); - it('handles not running on GCP with unexpected response by throwing error', async () => { - const failedRequest = ((_req: RequestOptions, callback: Callback) => - callback(null, null)) as Request; + it('handles GCP response even if some requests fail', async () => { + fetchMock + .mockResolvedValueOnce({ + status: 200, + ok: true, + headers, + text: () => 'some_id', + }) + .mockRejectedValueOnce({ + status: 500, + ok: false, + headers, + text: () => 'This is some random error text', + }) + .mockResolvedValueOnce({ + status: 404, + ok: false, + headers, + text: () => 'URI Not found', + }); + const response = await gcpService['_checkIfService'](); + + expect(fetchMock).toBeCalledTimes(3); - expect(async () => { - await GCP._checkIfService(failedRequest); - }).rejects.toThrowErrorMatchingInlineSnapshot(`"GCP request failed"`); + expect(response.isConfirmed()).toEqual(true); + expect(response.toJSON()).toMatchInlineSnapshot(` + Object { + "id": "some_id", + "metadata": undefined, + "name": "gcp", + "region": undefined, + "vm_type": undefined, + "zone": undefined, + } + `); }); }); - describe('_extractValue', () => { + describe('extractValue', () => { it('only handles strings', () => { // @ts-expect-error - expect(GCP._extractValue()).toBe(undefined); + expect(gcpService['extractValue']()).toBe(undefined); // @ts-expect-error - expect(GCP._extractValue(null, null)).toBe(undefined); + expect(gcpService['extractValue'](null, null)).toBe(undefined); // @ts-expect-error - expect(GCP._extractValue('abc', { field: 'abcxyz' })).toBe(undefined); + expect(gcpService['extractValue']('abc', { field: 'abcxyz' })).toBe(undefined); // @ts-expect-error - expect(GCP._extractValue('abc', 1234)).toBe(undefined); - expect(GCP._extractValue('abc/', 'abc/xyz')).toEqual('xyz'); + expect(gcpService['extractValue']('abc', 1234)).toBe(undefined); + expect(gcpService['extractValue']('abc/', 'abc/xyz')).toEqual('xyz'); }); it('uses the last index of the prefix to truncate', () => { - expect(GCP._extractValue('abc/', ' \n 123/abc/xyz\t \n')).toEqual('xyz'); + expect(gcpService['extractValue']('abc/', ' \n 123/abc/xyz\t \n')).toEqual('xyz'); }); }); - describe('_combineResponses', () => { + describe('combineResponses', () => { it('parses in expected format', () => { const id = '5702733457649812345'; const machineType = 'projects/441331612345/machineTypes/f1-micro'; const zone = 'projects/441331612345/zones/us-fake4-c'; - const response = GCP._combineResponses(id, machineType, zone); + const response = gcpService['combineResponses'](id, machineType, zone); - expect(response.getName()).toEqual(GCP.getName()); + expect(response.getName()).toEqual('gcp'); expect(response.isConfirmed()).toEqual(true); expect(response.toJSON()).toEqual({ name: 'gcp', @@ -152,9 +201,9 @@ describe('GCP', () => { const machineType = 'f1-micro'; const zone = 'us-fake4-c'; - const response = GCP._combineResponses(id, machineType, zone); + const response = gcpService['combineResponses'](id, machineType, zone); - expect(response.getName()).toEqual(GCP.getName()); + expect(response.getName()).toEqual('gcp'); expect(response.isConfirmed()).toEqual(true); expect(response.toJSON()).toEqual({ name: 'gcp', @@ -167,18 +216,16 @@ describe('GCP', () => { }); it('ignores unexpected response body', () => { + expect(() => gcpService['combineResponses']()).toThrow(); + expect(() => gcpService['combineResponses'](undefined, undefined, undefined)).toThrow(); // @ts-expect-error - expect(() => GCP._combineResponses()).toThrow(); - // @ts-expect-error - expect(() => GCP._combineResponses(undefined, undefined, undefined)).toThrow(); - // @ts-expect-error - expect(() => GCP._combineResponses(null, null, null)).toThrow(); + expect(() => gcpService['combineResponses'](null, null, null)).toThrow(); expect(() => // @ts-expect-error - GCP._combineResponses({ id: 'x' }, { machineType: 'a' }, { zone: 'b' }) + gcpService['combineResponses']({ id: 'x' }, { machineType: 'a' }, { zone: 'b' }) ).toThrow(); // @ts-expect-error - expect(() => GCP._combineResponses({ privateIp: 'a.b.c.d' })).toThrow(); + expect(() => gcpService['combineResponses']({ privateIp: 'a.b.c.d' })).toThrow(); }); }); }); diff --git a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/gcp.ts b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/gcp.ts index 565c07abd1d2c..2cdf3f87cfe8f 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/gcp.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/cloud/detector/gcp.ts @@ -6,14 +6,15 @@ * Side Public License, v 1. */ -import { isString } from 'lodash'; -import { promisify } from 'util'; -import { CloudService, CloudServiceOptions, Request, Response } from './cloud_service'; +import fetch, { Response } from 'node-fetch'; +import { CloudService } from './cloud_service'; import { CloudServiceResponse } from './cloud_response'; // GCP documentation shows both 'metadata.google.internal' (mostly) and '169.254.169.254' (sometimes) // To bypass potential DNS changes, the IP was used because it's shared with other cloud services const SERVICE_ENDPOINT = 'http://169.254.169.254/computeMetadata/v1/instance'; +// GCP required headers +const SERVICE_HEADERS = { 'Metadata-Flavor': 'Google' }; /** * Checks and loads the service metadata for an Google Cloud Platform VM if it is available. @@ -21,61 +22,54 @@ const SERVICE_ENDPOINT = 'http://169.254.169.254/computeMetadata/v1/instance'; * @internal */ export class GCPCloudService extends CloudService { - constructor(options: CloudServiceOptions = {}) { - super('gcp', options); + constructor() { + super('gcp'); } - _checkIfService(request: Request) { + protected _checkIfService = async () => { // we need to call GCP individually for each field we want metadata for const fields = ['id', 'machine-type', 'zone']; - const create = this._createRequestForField; - const allRequests = fields.map((field) => promisify(request)(create(field))); - return ( - Promise.all(allRequests) - // Note: there is no fallback option for GCP; - // responses are arrays containing [fullResponse, body]; - // because GCP returns plaintext, we have no way of validating - // without using the response code. - .then((responses) => { - return responses.map((response) => { - if (!response || response.statusCode === 404) { - throw new Error('GCP request failed'); - } - return this._extractBody(response, response.body); - }); - }) - .then(([id, machineType, zone]) => this._combineResponses(id, machineType, zone)) + const settledResponses = await Promise.allSettled( + fields.map(async (field) => { + return await fetch(`${SERVICE_ENDPOINT}/${field}`, { + method: 'GET', + headers: { ...SERVICE_HEADERS }, + }); + }) ); - } - _createRequestForField(field: string) { - return { - method: 'GET', - uri: `${SERVICE_ENDPOINT}/${field}`, - headers: { - // GCP requires this header - 'Metadata-Flavor': 'Google', - }, - // GCP does _not_ return JSON - json: false, - }; - } + const hasValidResponses = settledResponses.some(this.isValidResponse); - /** - * Extract the body if the response is valid and it came from GCP. - */ - _extractBody(response: Response, body?: Response['body']) { - if ( - response?.statusCode === 200 && - response.headers && - response.headers['metadata-flavor'] === 'Google' - ) { - return body; + if (!hasValidResponses) { + throw new Error('GCP request failed'); } - return null; - } + // Note: there is no fallback option for GCP; + // responses are arrays containing [fullResponse, body]; + // because GCP returns plaintext, we have no way of validating + // without using the response code. + const [id, machineType, zone] = await Promise.all( + settledResponses.map(async (settledResponse) => { + if (this.isValidResponse(settledResponse)) { + // GCP does _not_ return JSON + return await settledResponse.value.text(); + } + }) + ); + + return this.combineResponses(id, machineType, zone); + }; + + private isValidResponse = ( + settledResponse: PromiseSettledResult + ): settledResponse is PromiseFulfilledResult => { + if (settledResponse.status === 'rejected') { + return false; + } + const { value } = settledResponse; + return value.ok && value.status !== 404 && value.headers.get('metadata-flavor') === 'Google'; + }; /** * Parse the GCP responses, if possible. @@ -86,17 +80,11 @@ export class GCPCloudService extends CloudService { * machineType: 'projects/441331612345/machineTypes/f1-micro' * zone: 'projects/441331612345/zones/us-east4-c' */ - _combineResponses(id: string, machineType: string, zone: string) { - const vmId = isString(id) ? id.trim() : undefined; - const vmType = this._extractValue('machineTypes/', machineType); - const vmZone = this._extractValue('zones/', zone); - - let region; - - if (vmZone) { - // converts 'us-east4-c' into 'us-east4' - region = vmZone.substring(0, vmZone.lastIndexOf('-')); - } + private combineResponses = (id?: string, machineType?: string, zone?: string) => { + const vmId = typeof id === 'string' ? id.trim() : undefined; + const vmType = this.extractValue('machineTypes/', machineType); + const vmZone = this.extractValue('zones/', zone); + const region = vmZone ? vmZone.substring(0, vmZone.lastIndexOf('-')) : undefined; // ensure we actually have some data if (vmId || vmType || region || vmZone) { @@ -104,7 +92,7 @@ export class GCPCloudService extends CloudService { } throw new Error('unrecognized responses'); - } + }; /** * Extract the useful information returned from GCP while discarding @@ -113,15 +101,15 @@ export class GCPCloudService extends CloudService { * For example, this turns something like * 'projects/441331612345/machineTypes/f1-micro' into 'f1-micro'. */ - _extractValue(fieldPrefix: string, value: string) { - if (isString(value)) { - const index = value.lastIndexOf(fieldPrefix); - - if (index !== -1) { - return value.substring(index + fieldPrefix.length).trim(); - } + private extractValue = (fieldPrefix: string, value?: string) => { + if (typeof value !== 'string') { + return; } - return undefined; - } + const index = value.lastIndexOf(fieldPrefix); + + if (index !== -1) { + return value.substring(index + fieldPrefix.length).trim(); + } + }; } 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 68d56944d9974..237ec54e4692b 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -104,22 +104,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'keyword', _meta: { description: 'Non-default value of setting.' }, }, - 'timelion:default_rows': { - type: 'long', - _meta: { description: 'Non-default value of setting.' }, - }, - 'timelion:default_columns': { - type: 'long', - _meta: { description: 'Non-default value of setting.' }, - }, 'timelion:es.default_index': { type: 'keyword', _meta: { description: 'Non-default value of setting.' }, }, - 'timelion:showTutorial': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, 'securitySolution:timeDefaults': { 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 ed46e6b38b283..0c4b848ff3544 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -49,10 +49,7 @@ export interface UsageStats { 'timelion:max_buckets': number; 'timelion:es.timefield': string; 'timelion:min_interval': string; - 'timelion:default_rows': number; - 'timelion:default_columns': number; 'timelion:es.default_index': string; - 'timelion:showTutorial': boolean; 'securitySolution:timeDefaults': string; 'securitySolution:defaultAnomalyScore': number; 'securitySolution:refreshIntervalDefaults': string; diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts index 6097910afe22b..fc9f9a6e8c2d3 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.test.ts @@ -56,7 +56,6 @@ describe('kibana_usage', () => { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 0 }, - timelion_sheet: { total: 0 }, }); }); }); @@ -81,7 +80,6 @@ describe('getKibanaSavedObjectCounts', () => { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 0 }, - timelion_sheet: { total: 0 }, }); }); @@ -91,7 +89,6 @@ describe('getKibanaSavedObjectCounts', () => { types: { buckets: [ { key: 'dashboard', doc_count: 1 }, - { key: 'timelion-sheet', doc_count: 2 }, { key: 'index-pattern', value: 2 }, // Malformed on purpose { key: 'graph_workspace', doc_count: 3 }, // already snake_cased ], @@ -106,7 +103,6 @@ describe('getKibanaSavedObjectCounts', () => { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 3 }, - timelion_sheet: { total: 2 }, }); }); }); diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts index 1ebb61c446083..13b9d0ca2104c 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/kibana_usage_collector.ts @@ -19,21 +19,13 @@ interface KibanaSavedObjectCounts { search: { total: number }; index_pattern: { total: number }; graph_workspace: { total: number }; - timelion_sheet: { total: number }; } interface KibanaUsage extends KibanaSavedObjectCounts { index: string; } -const TYPES = [ - 'dashboard', - 'visualization', - 'search', - 'index-pattern', - 'graph-workspace', - 'timelion-sheet', -]; +const TYPES = ['dashboard', 'visualization', 'search', 'index-pattern', 'graph-workspace']; export async function getKibanaSavedObjectCounts( esClient: ElasticsearchClient, @@ -89,12 +81,6 @@ export function registerKibanaUsageCollector( _meta: { description: 'Total number of graph_workspace saved objects' }, }, }, - timelion_sheet: { - total: { - type: 'long', - _meta: { description: 'Total number of timelion_sheet saved objects' }, - }, - }, }, async fetch({ esClient }) { const { 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 fec314fc6b87e..5252ab24395aa 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 @@ -28,7 +28,6 @@ const uiMetricFromDataPluginSchema: MakeSchemaFrom = { kibana: commonSchema, // It's a forward app so we'll likely never report it management: commonSchema, short_url_redirect: commonSchema, // It's a forward app so we'll likely never report it - timelion: commonSchema, visualize: commonSchema, // X-Pack diff --git a/src/plugins/maps_ems/common/ems_defaults.ts b/src/plugins/maps_ems/common/ems_defaults.ts deleted file mode 100644 index 7eb82ac04858e..0000000000000 --- a/src/plugins/maps_ems/common/ems_defaults.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -// Default config for the elastic hosted EMS endpoints -export const DEFAULT_EMS_FILE_API_URL = 'https://vector.maps.elastic.co'; -export const DEFAULT_EMS_TILE_API_URL = 'https://tiles.maps.elastic.co'; -export const DEFAULT_EMS_LANDING_PAGE_URL = 'https://maps.elastic.co/v7.15'; -export const DEFAULT_EMS_FONT_LIBRARY_URL = - 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf'; - -export const DEFAULT_EMS_ROADMAP_ID = 'road_map'; -export const DEFAULT_EMS_ROADMAP_DESATURATED_ID = 'road_map_desaturated'; -export const DEFAULT_EMS_DARKMAP_ID = 'dark_map'; diff --git a/src/plugins/maps_ems/common/index.ts b/src/plugins/maps_ems/common/index.ts index d83a3319d3d15..f7d7ff1102e59 100644 --- a/src/plugins/maps_ems/common/index.ts +++ b/src/plugins/maps_ems/common/index.ts @@ -6,10 +6,16 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/109853 -/* eslint-disable @kbn/eslint/no_export_all */ - export const TMS_IN_YML_ID = 'TMS in config/kibana.yml'; -export * from './ems_defaults'; +export const DEFAULT_EMS_FILE_API_URL = 'https://vector.maps.elastic.co'; +export const DEFAULT_EMS_TILE_API_URL = 'https://tiles.maps.elastic.co'; +export const DEFAULT_EMS_LANDING_PAGE_URL = 'https://maps.elastic.co/v7.15'; +export const DEFAULT_EMS_FONT_LIBRARY_URL = + 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf'; + +export const DEFAULT_EMS_ROADMAP_ID = 'road_map'; +export const DEFAULT_EMS_ROADMAP_DESATURATED_ID = 'road_map_desaturated'; +export const DEFAULT_EMS_DARKMAP_ID = 'dark_map'; + export { ORIGIN } from './origin'; diff --git a/src/plugins/maps_ems/public/index.ts b/src/plugins/maps_ems/public/index.ts index 50f55dd3157bc..a4a0fc45d9164 100644 --- a/src/plugins/maps_ems/public/index.ts +++ b/src/plugins/maps_ems/public/index.ts @@ -6,9 +6,6 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/109853 -/* eslint-disable @kbn/eslint/no_export_all */ - import { PluginInitializerContext } from 'kibana/public'; import { MapsEmsPlugin } from './plugin'; import { IServiceSettings } from './service_settings'; @@ -27,9 +24,9 @@ export function plugin(initializerContext: PluginInitializerContext) { return new MapsEmsPlugin(initializerContext); } -export type { MapsEmsConfig } from '../config'; +export { TMS_IN_YML_ID } from '../common'; -export * from '../common'; +export type { MapsEmsConfig } from '../config'; export interface MapsEmsPluginSetup { config: MapsEmsConfig; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/delete_confirm_modal.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/delete_confirm_modal.test.tsx index 1dcf15bc551d4..76e85c9603cc0 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/delete_confirm_modal.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/delete_confirm_modal.test.tsx @@ -14,15 +14,21 @@ import { DeleteConfirmModal } from './delete_confirm_modal'; interface CreateObjectOptions { namespaces?: string[]; + hiddenType?: boolean; } -const createObject = ({ namespaces }: CreateObjectOptions = {}): SavedObjectWithMetadata => ({ +const createObject = ({ + namespaces, + hiddenType = false, +}: CreateObjectOptions = {}): SavedObjectWithMetadata => ({ id: 'foo', type: 'bar', attributes: {}, references: [], namespaces, - meta: {}, + meta: { + hiddenType, + }, }); describe('DeleteConfirmModal', () => { @@ -81,7 +87,7 @@ describe('DeleteConfirmModal', () => { isDeleting={false} onConfirm={onConfirm} onCancel={onCancel} - selectedObjects={[]} + selectedObjects={[createObject()]} /> ); wrapper.find('EuiButton').simulate('click'); @@ -90,6 +96,81 @@ describe('DeleteConfirmModal', () => { expect(onCancel).not.toHaveBeenCalled(); }); + describe('when trying to delete hidden objects', () => { + it('excludes the hidden objects from the table', () => { + const objs = [ + createObject({ hiddenType: true }), + createObject({ hiddenType: false }), + createObject({ hiddenType: true }), + ]; + const wrapper = mountWithIntl( + + ); + expect(wrapper.find('.euiTableRow')).toHaveLength(1); + }); + + it('displays a callout when at least one object cannot be deleted', () => { + const objs = [ + createObject({ hiddenType: false }), + createObject({ hiddenType: false }), + createObject({ hiddenType: true }), + ]; + const wrapper = mountWithIntl( + + ); + + const callout = findTestSubject(wrapper, 'cannotDeleteObjectsConfirmWarning'); + expect(callout).toHaveLength(1); + }); + + it('does not display a callout when all objects can be deleted', () => { + const objs = [ + createObject({ hiddenType: false }), + createObject({ hiddenType: false }), + createObject({ hiddenType: false }), + ]; + const wrapper = mountWithIntl( + + ); + + const callout = findTestSubject(wrapper, 'cannotDeleteObjectsConfirmWarning'); + expect(callout).toHaveLength(0); + }); + + it('disable the submit button when all objects cannot be deleted', () => { + const objs = [ + createObject({ hiddenType: true }), + createObject({ hiddenType: true }), + createObject({ hiddenType: true }), + ]; + const wrapper = mountWithIntl( + + ); + + expect(wrapper.find('EuiButton').getDOMNode()).toBeDisabled(); + }); + }); + describe('shared objects warning', () => { it('does not display a callout when no objects are shared', () => { const objs = [ diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/delete_confirm_modal.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/delete_confirm_modal.tsx index 7f1f3adc96d8b..e3ffc6d52a3ab 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/delete_confirm_modal.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/delete_confirm_modal.tsx @@ -86,7 +86,7 @@ export const DeleteConfirmModal: FC = ({ title={ } iconType="alert" @@ -95,7 +95,8 @@ export const DeleteConfirmModal: FC = ({

@@ -186,6 +187,7 @@ export const DeleteConfirmModal: FC = ({ fill color="danger" onClick={onConfirm} + disabled={deletableObjects.length === 0} data-test-subj="confirmModalConfirmButton" > createHashHistory()); - app.factory('kbnUrlStateStorage', (history) => - createKbnUrlStateStorage({ - history, - useHash: deps.core.uiSettings.get('state:storeInSessionStorage'), - ...withNotifyOnErrors(deps.core.notifications.toasts), - }) - ); - app.config(watchMultiDecorator); - - app - .controller('TimelionVisController', function ($scope) { - $scope.$on('timelionChartRendered', (event) => { - event.stopPropagation(); - $scope.renderComplete(); - }); - }) - .constant('timelionPanels', deps.timelionPanels) - .directive('chart', Chart) - .directive('timelionInterval', TimelionInterval) - .directive('timelionExpressionSuggestions', TimelionExpressionSuggestions) - .directive('timelionExpressionInput', timelionExpInput(deps)); - - initTimelionHelpDirective(app); - initInputFocusDirective(app); - initTimelionTabsDirective(app, deps); - initTimelionTDeprecationDirective(app, deps); - initTimelionTopNavDirective(app, deps); - initSavedObjectFinderDirective(app, savedSheetLoader, deps.core.uiSettings); - initSavedObjectSaveAsCheckBoxDirective(app); - initCellsDirective(app); - initFixedElementDirective(app); - initFullscreenDirective(app); - initTimelionSaveSheetDirective(app); - initTimelionLoadSheetDirective(app); - initTimelionOptionsSheetDirective(app); - - const location = 'Timelion'; - - app.directive('timelionApp', function () { - return { - restrict: 'E', - controllerAs: 'timelionApp', - controller: timelionController, - }; - }); - - function timelionController( - $http, - $route, - $routeParams, - $scope, - $timeout, - history, - kbnUrlStateStorage - ) { - // Keeping this at app scope allows us to keep the current page when the user - // switches to say, the timepicker. - $scope.page = deps.core.uiSettings.get('timelion:showTutorial', true) ? 1 : 0; - $scope.setPage = (page) => ($scope.page = page); - const timefilter = deps.plugins.data.query.timefilter.timefilter; - - timefilter.enableAutoRefreshSelector(); - timefilter.enableTimeRangeSelector(); - - deps.core.chrome.docTitle.change('Timelion - Kibana'); - - // starts syncing `_g` portion of url with query services - const { stop: stopSyncingQueryServiceStateWithUrl } = syncQueryStateWithUrl( - deps.plugins.data.query, - kbnUrlStateStorage - ); - - const savedSheet = $route.current.locals.savedSheet; - - function getStateDefaults() { - return { - sheet: savedSheet.timelion_sheet, - selected: 0, - columns: savedSheet.timelion_columns, - rows: savedSheet.timelion_rows, - interval: savedSheet.timelion_interval, - }; - } - - const { stateContainer, stopStateSync } = initTimelionAppState({ - stateDefaults: getStateDefaults(), - kbnUrlStateStorage, - }); - - $scope.state = _.cloneDeep(stateContainer.getState()); - $scope.expression = _.clone($scope.state.sheet[$scope.state.selected]); - $scope.updatedSheets = []; - - const savedVisualizations = deps.plugins.visualizations.savedVisualizationsLoader; - const timezone = visTypeTimelion.getTimezone(deps.core.uiSettings); - - const defaultExpression = '.es(*)'; - - $scope.topNavMenu = getTopNavMenu(); - - $timeout(function () { - if (deps.core.uiSettings.get('timelion:showTutorial', true)) { - $scope.toggleMenu('showHelp'); - } - }, 0); - - $scope.transient = {}; - - function getTopNavMenu() { - const newSheetAction = { - id: 'new', - label: i18n.translate('timelion.topNavMenu.newSheetButtonLabel', { - defaultMessage: 'New', - }), - description: i18n.translate('timelion.topNavMenu.newSheetButtonAriaLabel', { - defaultMessage: 'New Sheet', - }), - run: function () { - history.push('/'); - $route.reload(); - }, - testId: 'timelionNewButton', - }; - - const addSheetAction = { - id: 'add', - label: i18n.translate('timelion.topNavMenu.addChartButtonLabel', { - defaultMessage: 'Add', - }), - description: i18n.translate('timelion.topNavMenu.addChartButtonAriaLabel', { - defaultMessage: 'Add a chart', - }), - run: function () { - $scope.$evalAsync(() => $scope.newCell()); - }, - testId: 'timelionAddChartButton', - }; - - const saveSheetAction = { - id: 'save', - label: i18n.translate('timelion.topNavMenu.saveSheetButtonLabel', { - defaultMessage: 'Save', - }), - description: i18n.translate('timelion.topNavMenu.saveSheetButtonAriaLabel', { - defaultMessage: 'Save Sheet', - }), - run: () => { - $scope.$evalAsync(() => $scope.toggleMenu('showSave')); - }, - testId: 'timelionSaveButton', - }; - - const deleteSheetAction = { - id: 'delete', - label: i18n.translate('timelion.topNavMenu.deleteSheetButtonLabel', { - defaultMessage: 'Delete', - }), - description: i18n.translate('timelion.topNavMenu.deleteSheetButtonAriaLabel', { - defaultMessage: 'Delete current sheet', - }), - disableButton: function () { - return !savedSheet.id; - }, - run: function () { - const title = savedSheet.title; - function doDelete() { - savedSheet - .delete() - .then(() => { - deps.core.notifications.toasts.addSuccess( - i18n.translate('timelion.topNavMenu.delete.modal.successNotificationText', { - defaultMessage: `Deleted '{title}'`, - values: { title }, - }) - ); - history.push('/'); - }) - .catch((error) => addFatalError(deps.core.fatalErrors, error, location)); - } - - const confirmModalOptions = { - confirmButtonText: i18n.translate( - 'timelion.topNavMenu.delete.modal.confirmButtonLabel', - { - defaultMessage: 'Delete', - } - ), - title: i18n.translate('timelion.topNavMenu.delete.modalTitle', { - defaultMessage: `Delete Timelion sheet '{title}'?`, - values: { title }, - }), - }; - - $scope.$evalAsync(() => { - deps.core.overlays - .openConfirm( - i18n.translate('timelion.topNavMenu.delete.modal.warningText', { - defaultMessage: `You can't recover deleted sheets.`, - }), - confirmModalOptions - ) - .then((isConfirmed) => { - if (isConfirmed) { - doDelete(); - } - }); - }); - }, - testId: 'timelionDeleteButton', - }; - - const openSheetAction = { - id: 'open', - label: i18n.translate('timelion.topNavMenu.openSheetButtonLabel', { - defaultMessage: 'Open', - }), - description: i18n.translate('timelion.topNavMenu.openSheetButtonAriaLabel', { - defaultMessage: 'Open Sheet', - }), - run: () => { - $scope.$evalAsync(() => $scope.toggleMenu('showLoad')); - }, - testId: 'timelionOpenButton', - }; - - const optionsAction = { - id: 'options', - label: i18n.translate('timelion.topNavMenu.optionsButtonLabel', { - defaultMessage: 'Options', - }), - description: i18n.translate('timelion.topNavMenu.optionsButtonAriaLabel', { - defaultMessage: 'Options', - }), - run: () => { - $scope.$evalAsync(() => $scope.toggleMenu('showOptions')); - }, - testId: 'timelionOptionsButton', - }; - - const helpAction = { - id: 'help', - label: i18n.translate('timelion.topNavMenu.helpButtonLabel', { - defaultMessage: 'Help', - }), - description: i18n.translate('timelion.topNavMenu.helpButtonAriaLabel', { - defaultMessage: 'Help', - }), - run: () => { - $scope.$evalAsync(() => $scope.toggleMenu('showHelp')); - }, - testId: 'timelionDocsButton', - }; - - if (deps.core.application.capabilities.timelion.save) { - return [ - newSheetAction, - addSheetAction, - saveSheetAction, - deleteSheetAction, - openSheetAction, - optionsAction, - helpAction, - ]; - } - return [newSheetAction, addSheetAction, openSheetAction, optionsAction, helpAction]; - } - - let refresher; - const setRefreshData = function () { - if (refresher) $timeout.cancel(refresher); - const interval = timefilter.getRefreshInterval(); - if (interval.value > 0 && !interval.pause) { - function startRefresh() { - refresher = $timeout(function () { - if (!$scope.running) $scope.search(); - startRefresh(); - }, interval.value); - } - startRefresh(); - } - }; - - const init = function () { - $scope.running = false; - $scope.search(); - setRefreshData(); - - $scope.model = { - timeRange: timefilter.getTime(), - refreshInterval: timefilter.getRefreshInterval(), - }; - - const unsubscribeStateUpdates = stateContainer.subscribe((state) => { - const clonedState = _.cloneDeep(state); - $scope.updatedSheets.forEach((updatedSheet) => { - clonedState.sheet[updatedSheet.id] = updatedSheet.expression; - }); - $scope.state = clonedState; - $scope.opts.state = clonedState; - $scope.expression = _.clone($scope.state.sheet[$scope.state.selected]); - $scope.search(); - }); - - timefilter.getFetch$().subscribe($scope.search); - - $scope.opts = { - saveExpression: saveExpression, - saveSheet: saveSheet, - savedSheet: savedSheet, - state: _.cloneDeep(stateContainer.getState()), - search: $scope.search, - dontShowHelp: function () { - deps.core.uiSettings.set('timelion:showTutorial', false); - $scope.setPage(0); - $scope.closeMenus(); - }, - }; - - $scope.$watch('opts.state.rows', function (newRow) { - const state = stateContainer.getState(); - if (state.rows !== newRow) { - stateContainer.transitions.set('rows', newRow); - } - }); - - $scope.$watch('opts.state.columns', function (newColumn) { - const state = stateContainer.getState(); - if (state.columns !== newColumn) { - stateContainer.transitions.set('columns', newColumn); - } - }); - - $scope.menus = { - showHelp: false, - showSave: false, - showLoad: false, - showOptions: false, - }; - - $scope.toggleMenu = (menuName) => { - const curState = $scope.menus[menuName]; - $scope.closeMenus(); - $scope.menus[menuName] = !curState; - }; - - $scope.closeMenus = () => { - _.forOwn($scope.menus, function (value, key) { - $scope.menus[key] = false; - }); - }; - - $scope.$on('$destroy', () => { - stopSyncingQueryServiceStateWithUrl(); - unsubscribeStateUpdates(); - stopStateSync(); - }); - }; - - $scope.onTimeUpdate = function ({ dateRange }) { - $scope.model.timeRange = { - ...dateRange, - }; - timefilter.setTime(dateRange); - if (!$scope.running) $scope.search(); - }; - - $scope.onRefreshChange = function ({ isPaused, refreshInterval }) { - $scope.model.refreshInterval = { - pause: isPaused, - value: refreshInterval, - }; - timefilter.setRefreshInterval({ - pause: isPaused, - value: refreshInterval ? refreshInterval : $scope.refreshInterval.value, - }); - - setRefreshData(); - }; - - $scope.$watch( - function () { - return savedSheet.lastSavedTitle; - }, - function (newTitle) { - if (savedSheet.id && newTitle) { - deps.core.chrome.docTitle.change(newTitle); - } - } - ); - - $scope.$watch('expression', function (newExpression) { - const state = stateContainer.getState(); - if (state.sheet[state.selected] !== newExpression) { - const updatedSheet = $scope.updatedSheets.find( - (updatedSheet) => updatedSheet.id === state.selected - ); - if (updatedSheet) { - updatedSheet.expression = newExpression; - } else { - $scope.updatedSheets.push({ - id: state.selected, - expression: newExpression, - }); - } - } - }); - - $scope.toggle = function (property) { - $scope[property] = !$scope[property]; - }; - - $scope.changeInterval = function (interval) { - $scope.currentInterval = interval; - }; - - $scope.updateChart = function () { - const state = stateContainer.getState(); - const newSheet = _.clone(state.sheet); - if ($scope.updatedSheets.length) { - $scope.updatedSheets.forEach((updatedSheet) => { - newSheet[updatedSheet.id] = updatedSheet.expression; - }); - $scope.updatedSheets = []; - } - stateContainer.transitions.updateState({ - interval: $scope.currentInterval ? $scope.currentInterval : state.interval, - sheet: newSheet, - }); - }; - - $scope.newSheet = function () { - history.push('/'); - }; - - $scope.removeSheet = function (removedIndex) { - const state = stateContainer.getState(); - const newSheet = state.sheet.filter((el, index) => index !== removedIndex); - $scope.updatedSheets = $scope.updatedSheets.filter((el) => el.id !== removedIndex); - stateContainer.transitions.updateState({ - sheet: newSheet, - selected: removedIndex ? removedIndex - 1 : removedIndex, - }); - }; - - $scope.newCell = function () { - const state = stateContainer.getState(); - const newSheet = [...state.sheet, defaultExpression]; - stateContainer.transitions.updateState({ sheet: newSheet, selected: newSheet.length - 1 }); - }; - - $scope.setActiveCell = function (cell) { - const state = stateContainer.getState(); - if (state.selected !== cell) { - stateContainer.transitions.updateState({ sheet: $scope.state.sheet, selected: cell }); - } - }; - - $scope.search = function () { - $scope.running = true; - const state = stateContainer.getState(); - - // parse the time range client side to make sure it behaves like other charts - const timeRangeBounds = timefilter.getBounds(); - - const httpResult = $http - .post('../api/timelion/run', { - sheet: state.sheet, - time: _.assignIn( - { - from: timeRangeBounds.min, - to: timeRangeBounds.max, - }, - { - interval: state.interval, - timezone: timezone, - } - ), - }) - .then((resp) => resp.data) - .catch((resp) => { - throw resp.data; - }); - - httpResult - .then(function (resp) { - $scope.stats = resp.stats; - $scope.sheet = resp.sheet; - _.forEach(resp.sheet, function (cell) { - if (cell.exception && cell.plot !== state.selected) { - stateContainer.transitions.set('selected', cell.plot); - } - }); - $scope.running = false; - }) - .catch(function (resp) { - $scope.sheet = []; - $scope.running = false; - - const err = new Error(resp.message); - err.stack = resp.stack; - deps.core.notifications.toasts.addError(err, { - title: i18n.translate('timelion.searchErrorTitle', { - defaultMessage: 'Timelion request error', - }), - }); - }); - }; - - $scope.safeSearch = _.debounce($scope.search, 500); - - function saveSheet() { - const state = stateContainer.getState(); - savedSheet.timelion_sheet = state.sheet; - savedSheet.timelion_interval = state.interval; - savedSheet.timelion_columns = state.columns; - savedSheet.timelion_rows = state.rows; - savedSheet.save().then(function (id) { - if (id) { - deps.core.notifications.toasts.addSuccess({ - title: i18n.translate('timelion.saveSheet.successNotificationText', { - defaultMessage: `Saved sheet '{title}'`, - values: { title: savedSheet.title }, - }), - 'data-test-subj': 'timelionSaveSuccessToast', - }); - - if (savedSheet.id !== $routeParams.id) { - history.push(`/${savedSheet.id}`); - } - } - }); - } - - async function saveExpression(title) { - const vis = await deps.plugins.visualizations.createVis('timelion', { - title, - params: { - expression: $scope.state.sheet[$scope.state.selected], - interval: $scope.state.interval, - }, - }); - const state = deps.plugins.visualizations.convertFromSerializedVis(vis.serialize()); - const visSavedObject = await savedVisualizations.get(); - Object.assign(visSavedObject, state); - const id = await visSavedObject.save(); - if (id) { - deps.core.notifications.toasts.addSuccess( - i18n.translate('timelion.saveExpression.successNotificationText', { - defaultMessage: `Saved expression '{title}'`, - values: { title: state.title }, - }) - ); - } - } - - init(); - } - - app.config(function ($routeProvider) { - $routeProvider - .when('/:id?', { - template: rootTemplate, - reloadOnSearch: false, - k7Breadcrumbs: ($injector, $route) => - $injector.invoke( - $route.current.params.id ? getSavedSheetBreadcrumbs : getCreateBreadcrumbs - ), - badge: () => { - if (deps.core.application.capabilities.timelion.save) { - return undefined; - } - - return { - text: i18n.translate('timelion.badge.readOnly.text', { - defaultMessage: 'Read only', - }), - tooltip: i18n.translate('timelion.badge.readOnly.tooltip', { - defaultMessage: 'Unable to save Timelion sheets', - }), - iconType: 'glasses', - }; - }, - resolve: { - savedSheet: function (savedSheets, $route) { - return savedSheets - .get($route.current.params.id) - .then((savedSheet) => { - if ($route.current.params.id) { - deps.core.chrome.recentlyAccessed.add( - savedSheet.getFullPath(), - savedSheet.title, - savedSheet.id - ); - } - return savedSheet; - }) - .catch(); - }, - }, - }) - .otherwise('/'); - }); -} diff --git a/src/plugins/timelion/public/application.ts b/src/plugins/timelion/public/application.ts deleted file mode 100644 index 1e3cf43f62655..0000000000000 --- a/src/plugins/timelion/public/application.ts +++ /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 and the Server Side Public License, v 1; you may 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 './index.scss'; - -import { EuiIcon } from '@elastic/eui'; -import angular, { IModule } from 'angular'; -// required for `ngSanitize` angular module -import 'angular-sanitize'; -// required for ngRoute -import 'angular-route'; -import 'angular-sortable-view'; -import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; -import { - IUiSettingsClient, - CoreStart, - PluginInitializerContext, - AppMountParameters, -} from 'kibana/public'; -import { getTimeChart } from './panels/timechart/timechart'; -import { Panel } from './panels/panel'; - -import { configureAppAngularModule } from '../../kibana_legacy/public'; -import { TimelionPluginStartDependencies } from './plugin'; -import { DataPublicPluginStart } from '../../data/public'; -// @ts-ignore -import { initTimelionApp } from './app'; - -export interface RenderDeps { - pluginInitializerContext: PluginInitializerContext; - mountParams: AppMountParameters; - core: CoreStart; - plugins: TimelionPluginStartDependencies; - timelionPanels: Map; -} - -export interface TimelionVisualizationDependencies { - uiSettings: IUiSettingsClient; - timelionPanels: Map; - data: DataPublicPluginStart; - $rootScope: any; - $compile: any; -} - -let angularModuleInstance: IModule | null = null; - -export const renderApp = (deps: RenderDeps) => { - if (!angularModuleInstance) { - angularModuleInstance = createLocalAngularModule(deps); - // global routing stuff - configureAppAngularModule( - angularModuleInstance, - { core: deps.core, env: deps.pluginInitializerContext.env }, - true - ); - initTimelionApp(angularModuleInstance, deps); - } - - const $injector = mountTimelionApp(deps.mountParams.appBasePath, deps.mountParams.element, deps); - - return () => { - $injector.get('$rootScope').$destroy(); - }; -}; - -function registerPanels(dependencies: TimelionVisualizationDependencies) { - const timeChartPanel: Panel = getTimeChart(dependencies); - - dependencies.timelionPanels.set(timeChartPanel.name, timeChartPanel); -} - -const mainTemplate = (basePath: string) => `
- -
`; - -const moduleName = 'app/timelion'; - -const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'angular-sortable-view']; - -function mountTimelionApp(appBasePath: string, element: HTMLElement, deps: RenderDeps) { - const mountpoint = document.createElement('div'); - mountpoint.setAttribute('class', 'timelionAppContainer'); - // eslint-disable-next-line no-unsanitized/property - mountpoint.innerHTML = mainTemplate(appBasePath); - // bootstrap angular into detached element and attach it later to - // make angular-within-angular possible - const $injector = angular.bootstrap(mountpoint, [moduleName]); - - registerPanels({ - uiSettings: deps.core.uiSettings, - timelionPanels: deps.timelionPanels, - data: deps.plugins.data, - $rootScope: $injector.get('$rootScope'), - $compile: $injector.get('$compile'), - }); - element.appendChild(mountpoint); - return $injector; -} - -function createLocalAngularModule(deps: RenderDeps) { - createLocalI18nModule(); - createLocalIconModule(); - - const dashboardAngularModule = angular.module(moduleName, [ - ...thirdPartyAngularDependencies, - 'app/timelion/I18n', - 'app/timelion/icon', - ]); - return dashboardAngularModule; -} - -function createLocalIconModule() { - angular - .module('app/timelion/icon', ['react']) - .directive('icon', (reactDirective) => reactDirective(EuiIcon)); -} - -function createLocalI18nModule() { - angular - .module('app/timelion/I18n', []) - .provider('i18n', I18nProvider) - .filter('i18n', i18nFilter) - .directive('i18nId', i18nDirective); -} diff --git a/src/plugins/timelion/public/breadcrumbs.js b/src/plugins/timelion/public/breadcrumbs.js deleted file mode 100644 index aff173823946b..0000000000000 --- a/src/plugins/timelion/public/breadcrumbs.js +++ /dev/null @@ -1,37 +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'; - -const ROOT_BREADCRUMB = { - text: i18n.translate('timelion.breadcrumbs.root', { - defaultMessage: 'Timelion', - }), - href: '#', -}; - -export function getCreateBreadcrumbs() { - return [ - ROOT_BREADCRUMB, - { - text: i18n.translate('timelion.breadcrumbs.create', { - defaultMessage: 'Create', - }), - }, - ]; -} - -export function getSavedSheetBreadcrumbs($route) { - const { savedSheet } = $route.current.locals; - return [ - ROOT_BREADCRUMB, - { - text: savedSheet.title, - }, - ]; -} diff --git a/src/plugins/timelion/public/components/timelion_deprecation.tsx b/src/plugins/timelion/public/components/timelion_deprecation.tsx deleted file mode 100644 index 117aabed6773c..0000000000000 --- a/src/plugins/timelion/public/components/timelion_deprecation.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { FormattedMessage } from '@kbn/i18n/react'; -import { EuiSpacer, EuiCallOut, EuiLink } from '@elastic/eui'; -import React from 'react'; -import { DocLinksStart } from '../../../../core/public'; - -export const TimelionDeprecation = ({ links }: DocLinksStart) => { - const timelionDeprecationLink = links.visualize.timelionDeprecation; - return ( - <> - - - - ), - }} - /> - } - color="warning" - iconType="alert" - size="s" - /> - - - ); -}; diff --git a/src/plugins/timelion/public/components/timelion_deprecation_directive.js b/src/plugins/timelion/public/components/timelion_deprecation_directive.js deleted file mode 100644 index 2aeea00991864..0000000000000 --- a/src/plugins/timelion/public/components/timelion_deprecation_directive.js +++ /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 React from 'react'; -import { TimelionDeprecation } from './timelion_deprecation'; - -export function initTimelionTDeprecationDirective(app, deps) { - app.directive('timelionDeprecation', function (reactDirective) { - return reactDirective( - () => { - return ( - - - - ); - }, - [], - { - restrict: 'E', - scope: { - docLinks: '=', - }, - } - ); - }); -} diff --git a/src/plugins/timelion/public/components/timelion_top_nav_directive.js b/src/plugins/timelion/public/components/timelion_top_nav_directive.js deleted file mode 100644 index 4ec3b40e47c52..0000000000000 --- a/src/plugins/timelion/public/components/timelion_top_nav_directive.js +++ /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 React from 'react'; - -export function initTimelionTopNavDirective(app, deps) { - app.directive('timelionTopNav', function (reactDirective) { - return reactDirective( - (props) => { - const { TopNavMenu } = deps.plugins.navigation.ui; - return ( - - - - ); - }, - [ - ['topNavMenu', { watchDepth: 'reference' }], - ['onTimeUpdate', { watchDepth: 'reference' }], - ], - { - restrict: 'E', - scope: { - topNavMenu: '=', - onTimeUpdate: '=', - }, - } - ); - }); -} diff --git a/src/plugins/timelion/public/components/timelionhelp_tabs.js b/src/plugins/timelion/public/components/timelionhelp_tabs.js deleted file mode 100644 index 537e1bfa393b7..0000000000000 --- a/src/plugins/timelion/public/components/timelionhelp_tabs.js +++ /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 PropTypes from 'prop-types'; -import React from 'react'; - -import { EuiTabs, EuiTab } from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -function handleClick(activateTab, tabName) { - activateTab(tabName); -} - -export function TimelionHelpTabs(props) { - return ( - - handleClick(props.activateTab, 'funcref')} - > - - - handleClick(props.activateTab, 'keyboardtips')} - > - - - - ); -} - -TimelionHelpTabs.propTypes = { - activeTab: PropTypes.string, - activateTab: PropTypes.func, -}; diff --git a/src/plugins/timelion/public/components/timelionhelp_tabs_directive.js b/src/plugins/timelion/public/components/timelionhelp_tabs_directive.js deleted file mode 100644 index a88e156cb5c51..0000000000000 --- a/src/plugins/timelion/public/components/timelionhelp_tabs_directive.js +++ /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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { TimelionHelpTabs } from './timelionhelp_tabs'; - -export function initTimelionTabsDirective(app, deps) { - app.directive('timelionHelpTabs', function (reactDirective) { - return reactDirective( - (props) => { - return ( - - - - ); - }, - [['activeTab'], ['activateTab', { watchDepth: 'reference' }]], - { - restrict: 'E', - scope: { - activeTab: '=', - activateTab: '=', - }, - } - ); - }); -} diff --git a/src/plugins/timelion/public/directives/_form.scss b/src/plugins/timelion/public/directives/_form.scss deleted file mode 100644 index 37a0cc4c0f3e5..0000000000000 --- a/src/plugins/timelion/public/directives/_form.scss +++ /dev/null @@ -1,83 +0,0 @@ -.form-control { - @include euiFontSizeS; - display: block; - width: 100%; - height: $euiFormControlCompressedHeight; - padding: $euiSizeXS $euiSizeM; - border: $euiBorderThin; - background-color: $euiFormBackgroundColor; - color: $euiTextColor; - border-radius: $euiBorderRadius; - cursor: pointer; - - &:not([type='range']) { - appearance: none; - } - - &:focus { - border-color: $euiColorPrimary; - outline: none; - box-shadow: none; - } -} - -select.form-control { // stylelint-disable-line selector-no-qualifying-type - // Makes the select arrow similar to EUI's arrowDown icon - background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"%3E%3Cpath fill="#{hexToRGB($euiTextColor)}" d="M13.0688508,5.15725038 L8.38423975,9.76827428 C8.17054415,9.97861308 7.82999214,9.97914095 7.61576025,9.76827428 L2.93114915,5.15725038 C2.7181359,4.94758321 2.37277319,4.94758321 2.15975994,5.15725038 C1.94674669,5.36691756 1.94674669,5.70685522 2.15975994,5.9165224 L6.84437104,10.5275463 C7.48517424,11.1582836 8.51644979,11.1566851 9.15562896,10.5275463 L13.8402401,5.9165224 C14.0532533,5.70685522 14.0532533,5.36691756 13.8402401,5.15725038 C13.6272268,4.94758321 13.2818641,4.94758321 13.0688508,5.15725038 Z"/%3E%3C/svg%3E'); - background-size: $euiSize; - background-repeat: no-repeat; - background-position: calc(100% - #{$euiSizeS}); - padding-right: $euiSizeXL; -} - -.fullWidth { - width: 100%; -} - -.timDropdownWarning { - margin-bottom: $euiSize; - padding: $euiSizeXS $euiSizeS; - color: $euiColorDarkestShade; - border-left: solid 2px $euiColorDanger; - font-size: $euiSizeM; -} - -.timFormCheckbox { - display: flex; - align-items: center; - line-height: 1.5; - position: relative; -} - -.timFormCheckbox__input { - appearance: none; - background-color: $euiColorLightestShade; - border: 1px solid $euiColorLightShade; - border-radius: $euiSizeXS; - width: $euiSize; - height: $euiSize; - font-size: $euiSizeM; - transition: background-color .1s linear; -} - -.timFormCheckbox__input:checked { - border-color: $euiColorPrimary; - background-color: $euiColorPrimary; -} - -.timFormCheckbox__icon { - position: absolute; - top: 0; - left: 2px; -} - -.timFormTextarea { - padding: $euiSizeXS $euiSizeM; - font-size: $euiSize; - line-height: 1.5; - color: $euiColorDarkestShade; - background-color: $euiFormBackgroundColor; - border: 1px solid $euiColorLightShade; - border-radius: $euiSizeXS; - transition: border-color .1s linear; -} diff --git a/src/plugins/timelion/public/directives/_index.scss b/src/plugins/timelion/public/directives/_index.scss deleted file mode 100644 index 2a015711062a6..0000000000000 --- a/src/plugins/timelion/public/directives/_index.scss +++ /dev/null @@ -1,7 +0,0 @@ -@import './timelion_expression_input'; -@import './cells/index'; -@import './timelion_expression_suggestions/index'; -@import './timelion_help/index'; -@import './timelion_interval/index'; -@import './saved_object_finder'; -@import './form'; diff --git a/src/plugins/timelion/public/directives/_saved_object_finder.scss b/src/plugins/timelion/public/directives/_saved_object_finder.scss deleted file mode 100644 index 55882fe78e99e..0000000000000 --- a/src/plugins/timelion/public/directives/_saved_object_finder.scss +++ /dev/null @@ -1,132 +0,0 @@ -.list-group-menu { - &.select-mode a { - outline: none; - color: tintOrShade($euiColorPrimary, 10%, 10%); - } - - .list-group-menu-item { - list-style: none; - color: tintOrShade($euiColorPrimary, 10%, 10%); - - &.active { - font-weight: bold; - background-color: $euiColorLightShade; - } - - &:hover { - background-color: tintOrShade($euiColorPrimary, 90%, 90%); - } - - li { - list-style: none; - color: tintOrShade($euiColorPrimary, 10%, 10%); - } - } -} - -saved-object-finder { - - .timSearchBar { - display: flex; - align-items: center; - } - - .timSearchBar__section { - position: relative; - margin-right: $euiSize; - flex: 1; - } - - .timSearchBar__icon { - position: absolute; - top: $euiSizeS; - left: $euiSizeS; - font-size: $euiSize; - color: $euiColorDarkShade; - } - - .timSearchBar__input { - padding: $euiSizeS $euiSizeM; - color: $euiColorDarkestShade; - background-color: $euiColorEmptyShade; - border: 1px solid $euiColorLightShade; - border-radius: $euiSizeXS; - transition: border-color .1s linear; - padding-left: $euiSizeXL; - width: 100%; - font-size: $euiSize; - } - - .timSearchBar__pagecount { - font-size: $euiSize; - color: $euiColorDarkShade; - } - - .list-sort-button { - border-top-left-radius: 0; - border-top-right-radius: 0; - border: none; - padding: $euiSizeS $euiSize; - font-weight: $euiFontWeightRegular; - background-color: $euiColorLightestShade; - margin-top: $euiSize; - } - - .li-striped { - li { - border: none; - } - - li:nth-child(even) { - background-color: $euiColorLightestShade; - } - - li:nth-child(odd) { - background-color: $euiColorEmptyShade; - } - - .paginate-heading { - font-weight: $euiFontWeightRegular; - color: $euiColorDarkestShade; - } - - .list-group-item { - padding: $euiSizeS $euiSize; - - ul { - padding: 0; - display: flex; - flex-direction: row; - - .finder-type { - margin-right: $euiSizeS; - } - } - - a { - display: block; - color: $euiColorPrimary; - - i { - color: shade($euiColorPrimary, 10%); - margin-right: $euiSizeS; - } - } - - &:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - - &.list-group-no-results p { - margin-bottom: 0; - } - } - } - - paginate { - paginate-controls { - margin: $euiSize; - } - } -} diff --git a/src/plugins/timelion/public/directives/_timelion_expression_input.scss b/src/plugins/timelion/public/directives/_timelion_expression_input.scss deleted file mode 100644 index e4294d8454c7c..0000000000000 --- a/src/plugins/timelion/public/directives/_timelion_expression_input.scss +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 1. Anchor suggestions beneath input. - * 2. Allow for option of positioning suggestions absolutely. - */ - -.timExpressionInput__container { - flex: 1 1 auto; - display: flex; - flex-direction: column; /* 1 */ - position: relative; /* 2 */ -} - -.timExpressionInput { - min-height: 70px; // Matches buttons on the right with new vertical rhythm sizing -} diff --git a/src/plugins/timelion/public/directives/cells/_cells.scss b/src/plugins/timelion/public/directives/cells/_cells.scss deleted file mode 100644 index d1e5e976fc8d2..0000000000000 --- a/src/plugins/timelion/public/directives/cells/_cells.scss +++ /dev/null @@ -1,61 +0,0 @@ -.timCell { - display: inline-block; - cursor: pointer; - position: relative; - box-sizing: border-box; - border: 2px dashed transparent; - // sass-lint:disable-block no-important - padding-left: 0 !important; - padding-right: 0 !important; - margin-bottom: $euiSizeM; - - &.active { - border-color: $euiColorLightShade; - } -} - -.timCell.running { - opacity: .5; -} - -.timCell__actions { - position: absolute; - bottom: $euiSizeXS; - left: $euiSizeXS; - - > .timCell__action, - > .timCell__id { - @include euiFontSizeXS; - font-weight: $euiFontWeightBold; - color: $euiColorMediumShade; - display: inline-block; - text-align: center; - width: $euiSizeL; - height: $euiSizeL; - border-radius: $euiSizeL / 2; - border: $euiBorderThin; - background-color: $euiColorLightestShade; - z-index: $euiZLevel1; - } - - > .timCell__action { - opacity: 0; - - &:focus { - opacity: 1; - } - - &:hover, - &:focus { - color: $euiTextColor; - border-color: $euiColorMediumShade; - background-color: $euiColorLightShade; - } - } -} - -.timCell:hover { - .timCell__action { - opacity: 1; - } -} diff --git a/src/plugins/timelion/public/directives/cells/_index.scss b/src/plugins/timelion/public/directives/cells/_index.scss deleted file mode 100644 index 8611b4d8ba1d0..0000000000000 --- a/src/plugins/timelion/public/directives/cells/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './cells'; diff --git a/src/plugins/timelion/public/directives/cells/cells.html b/src/plugins/timelion/public/directives/cells/cells.html deleted file mode 100644 index f90b85abaf920..0000000000000 --- a/src/plugins/timelion/public/directives/cells/cells.html +++ /dev/null @@ -1,52 +0,0 @@ -
- -
- -
-
-
{{$index + 1}}
- - - - -
-
- -
diff --git a/src/plugins/timelion/public/directives/cells/cells.js b/src/plugins/timelion/public/directives/cells/cells.js deleted file mode 100644 index af9e315a7d944..0000000000000 --- a/src/plugins/timelion/public/directives/cells/cells.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { move } from './collection'; -import { initTimelionGridDirective } from '../timelion_grid'; - -import html from './cells.html'; - -export function initCellsDirective(app) { - initTimelionGridDirective(app); - - app.directive('timelionCells', function () { - return { - restrict: 'E', - scope: { - sheet: '=', - state: '=', - transient: '=', - onSearch: '=', - onSelect: '=', - onRemoveSheet: '=', - }, - template: html, - link: function ($scope) { - $scope.removeCell = function (index) { - $scope.onRemoveSheet(index); - }; - - $scope.dropCell = function (item, partFrom, partTo, indexFrom, indexTo) { - move($scope.sheet, indexFrom, indexTo); - $scope.onSelect(indexTo); - }; - }, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/cells/collection.ts b/src/plugins/timelion/public/directives/cells/collection.ts deleted file mode 100644 index 188f00bef16ae..0000000000000 --- a/src/plugins/timelion/public/directives/cells/collection.ts +++ /dev/null @@ -1,65 +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 _ from 'lodash'; - -/** - * move an obj either up or down in the collection by - * injecting it either before/after the prev/next obj that - * satisfied the qualifier - * - * or, just from one index to another... - * - * @param {array} objs - the list to move the object within - * @param {number|any} obj - the object that should be moved, or the index that the object is currently at - * @param {number|boolean} below - the index to move the object to, or whether it should be moved up or down - * @param {function} qualifier - a lodash-y callback, object = _.where, string = _.pluck - * @return {array} - the objs argument - */ -export function move( - objs: any[], - obj: object | number, - below: number | boolean, - qualifier?: ((object: object, index: number) => any) | Record | string -): object[] { - const origI = _.isNumber(obj) ? obj : objs.indexOf(obj); - if (origI === -1) { - return objs; - } - - if (_.isNumber(below)) { - // move to a specific index - objs.splice(below, 0, objs.splice(origI, 1)[0]); - return objs; - } - - below = !!below; - qualifier = qualifier && _.iteratee(qualifier); - - const above = !below; - const finder = below ? _.findIndex : _.findLastIndex; - - // find the index of the next/previous obj that meets the qualifications - const targetI = finder(objs, (otherAgg, otherI) => { - if (below && otherI <= origI) { - return; - } - if (above && otherI >= origI) { - return; - } - return Boolean(_.isFunction(qualifier) && qualifier(otherAgg, otherI)); - }); - - if (targetI === -1) { - return objs; - } - - // place the obj at it's new index - objs.splice(targetI, 0, objs.splice(origI, 1)[0]); - return objs; -} diff --git a/src/plugins/timelion/public/directives/chart/chart.js b/src/plugins/timelion/public/directives/chart/chart.js deleted file mode 100644 index 8f02fb70436e7..0000000000000 --- a/src/plugins/timelion/public/directives/chart/chart.js +++ /dev/null @@ -1,55 +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'; - -export function Chart(timelionPanels) { - return { - restrict: 'A', - scope: { - seriesList: '=chart', // The flot object, data, config and all - search: '=', // The function to execute to kick off a search - interval: '=', // Required for formatting x-axis ticks - rerenderTrigger: '=', - }, - link: function ($scope, $elem) { - let panelScope = $scope.$new(true); - - function render() { - panelScope.$destroy(); - - if (!$scope.seriesList) return; - - $scope.seriesList.render = $scope.seriesList.render || { - type: 'timechart', - }; - - const panelSchema = timelionPanels.get($scope.seriesList.render.type); - - if (!panelSchema) { - $elem.text( - i18n.translate('timelion.chart.seriesList.noSchemaWarning', { - defaultMessage: 'No such panel type: {renderType}', - values: { renderType: $scope.seriesList.render.type }, - }) - ); - return; - } - - panelScope = $scope.$new(true); - panelScope.seriesList = $scope.seriesList; - panelScope.interval = $scope.interval; - panelScope.search = $scope.search; - - panelSchema.render(panelScope, $elem); - } - - $scope.$watchGroup(['seriesList', 'rerenderTrigger'], render); - }, - }; -} diff --git a/src/plugins/timelion/public/directives/fixed_element.js b/src/plugins/timelion/public/directives/fixed_element.js deleted file mode 100644 index 4349161892367..0000000000000 --- a/src/plugins/timelion/public/directives/fixed_element.js +++ /dev/null @@ -1,39 +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 $ from 'jquery'; - -export function initFixedElementDirective(app) { - app.directive('fixedElementRoot', function () { - return { - restrict: 'A', - link: function ($elem) { - let fixedAt; - $(window).bind('scroll', function () { - const fixed = $('[fixed-element]', $elem); - const body = $('[fixed-element-body]', $elem); - const top = fixed.offset().top; - - if ($(window).scrollTop() > top) { - // This is a gross hack, but its better than it was. I guess - fixedAt = $(window).scrollTop(); - fixed.addClass(fixed.attr('fixed-element')); - body.addClass(fixed.attr('fixed-element-body')); - body.css({ top: fixed.height() }); - } - - if ($(window).scrollTop() < fixedAt) { - fixed.removeClass(fixed.attr('fixed-element')); - body.removeClass(fixed.attr('fixed-element-body')); - body.removeAttr('style'); - } - }); - }, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/fullscreen/fullscreen.html b/src/plugins/timelion/public/directives/fullscreen/fullscreen.html deleted file mode 100644 index 1ed6aa82ea3b9..0000000000000 --- a/src/plugins/timelion/public/directives/fullscreen/fullscreen.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
-
- -
-
diff --git a/src/plugins/timelion/public/directives/fullscreen/fullscreen.js b/src/plugins/timelion/public/directives/fullscreen/fullscreen.js deleted file mode 100644 index 8403d861a4479..0000000000000 --- a/src/plugins/timelion/public/directives/fullscreen/fullscreen.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 html from './fullscreen.html'; - -export function initFullscreenDirective(app) { - app.directive('timelionFullscreen', function () { - return { - restrict: 'E', - scope: { - expression: '=', - series: '=', - state: '=', - transient: '=', - onSearch: '=', - }, - template: html, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/input_focus.js b/src/plugins/timelion/public/directives/input_focus.js deleted file mode 100644 index 23b8c54d623c3..0000000000000 --- a/src/plugins/timelion/public/directives/input_focus.js +++ /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. - */ - -export function initInputFocusDirective(app) { - app.directive('inputFocus', function ($parse, $timeout) { - return { - restrict: 'A', - link: function ($scope, $elem, attrs) { - const isDisabled = attrs.disableInputFocus && $parse(attrs.disableInputFocus)($scope); - if (!isDisabled) { - $timeout(function () { - $elem.focus(); - if (attrs.inputFocus === 'select') $elem.select(); - }); - } - }, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/key_map.ts b/src/plugins/timelion/public/directives/key_map.ts deleted file mode 100644 index 3e28bf3d7a3d5..0000000000000 --- a/src/plugins/timelion/public/directives/key_map.ts +++ /dev/null @@ -1,110 +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 const keyMap: { [key: number]: string } = { - 8: 'backspace', - 9: 'tab', - 13: 'enter', - 16: 'shift', - 17: 'ctrl', - 18: 'alt', - 19: 'pause', - 20: 'capsLock', - 27: 'escape', - 32: 'space', - 33: 'pageUp', - 34: 'pageDown', - 35: 'end', - 36: 'home', - 37: 'left', - 38: 'up', - 39: 'right', - 40: 'down', - 45: 'insert', - 46: 'delete', - 48: '0', - 49: '1', - 50: '2', - 51: '3', - 52: '4', - 53: '5', - 54: '6', - 55: '7', - 56: '8', - 57: '9', - 65: 'a', - 66: 'b', - 67: 'c', - 68: 'd', - 69: 'e', - 70: 'f', - 71: 'g', - 72: 'h', - 73: 'i', - 74: 'j', - 75: 'k', - 76: 'l', - 77: 'm', - 78: 'n', - 79: 'o', - 80: 'p', - 81: 'q', - 82: 'r', - 83: 's', - 84: 't', - 85: 'u', - 86: 'v', - 87: 'w', - 88: 'x', - 89: 'y', - 90: 'z', - 91: 'leftWindowKey', - 92: 'rightWindowKey', - 93: 'selectKey', - 96: '0', - 97: '1', - 98: '2', - 99: '3', - 100: '4', - 101: '5', - 102: '6', - 103: '7', - 104: '8', - 105: '9', - 106: 'multiply', - 107: 'add', - 109: 'subtract', - 110: 'period', - 111: 'divide', - 112: 'f1', - 113: 'f2', - 114: 'f3', - 115: 'f4', - 116: 'f5', - 117: 'f6', - 118: 'f7', - 119: 'f8', - 120: 'f9', - 121: 'f10', - 122: 'f11', - 123: 'f12', - 144: 'numLock', - 145: 'scrollLock', - 186: 'semiColon', - 187: 'equalSign', - 188: 'comma', - 189: 'dash', - 190: 'period', - 191: 'forwardSlash', - 192: 'graveAccent', - 219: 'openBracket', - 220: 'backSlash', - 221: 'closeBracket', - 222: 'singleQuote', - 224: 'meta', -}; diff --git a/src/plugins/timelion/public/directives/saved_object_finder.html b/src/plugins/timelion/public/directives/saved_object_finder.html deleted file mode 100644 index 1ce10efe4e0a8..0000000000000 --- a/src/plugins/timelion/public/directives/saved_object_finder.html +++ /dev/null @@ -1,112 +0,0 @@ -
-
-
- - -
- -
-

-
- - - -
-
-
-
- - - - - diff --git a/src/plugins/timelion/public/directives/saved_object_finder.js b/src/plugins/timelion/public/directives/saved_object_finder.js deleted file mode 100644 index 3bd6a2d9581f4..0000000000000 --- a/src/plugins/timelion/public/directives/saved_object_finder.js +++ /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 _ from 'lodash'; -import rison from 'rison-node'; -import savedObjectFinderTemplate from './saved_object_finder.html'; -import { keyMap } from './key_map'; -import { - PaginateControlsDirectiveProvider, - PaginateDirectiveProvider, -} from '../../../kibana_legacy/public'; -import { PER_PAGE_SETTING } from '../../../saved_objects/public'; -import { VISUALIZE_ENABLE_LABS_SETTING } from '../../../visualizations/public'; - -export function initSavedObjectFinderDirective(app, savedSheetLoader, uiSettings) { - app - .directive('paginate', PaginateDirectiveProvider) - .directive('paginateControls', PaginateControlsDirectiveProvider) - .directive('savedObjectFinder', function () { - return { - restrict: 'E', - scope: { - type: '@', - // optional make-url attr, sets the userMakeUrl in our scope - userMakeUrl: '=?makeUrl', - // optional on-choose attr, sets the userOnChoose in our scope - userOnChoose: '=?onChoose', - // optional useLocalManagement attr, removes link to management section - useLocalManagement: '=?useLocalManagement', - /** - * @type {function} - an optional function. If supplied an `Add new X` button is shown - * and this function is called when clicked. - */ - onAddNew: '=', - /** - * @{type} boolean - set this to true, if you don't want the search box above the - * table to automatically gain focus once loaded - */ - disableAutoFocus: '=', - }, - template: savedObjectFinderTemplate, - controllerAs: 'finder', - controller: function ($scope, $element, $location, history) { - const self = this; - - // the text input element - const $input = $element.find('input[ng-model=filter]'); - - // The number of items to show in the list - $scope.perPage = uiSettings.get(PER_PAGE_SETTING); - - // the list that will hold the suggestions - const $list = $element.find('ul'); - - // the current filter string, used to check that returned results are still useful - let currentFilter = $scope.filter; - - // the most recently entered search/filter - let prevSearch; - - // the list of hits, used to render display - self.hits = []; - - self.service = savedSheetLoader; - self.properties = self.service.loaderProperties; - - filterResults(); - - /** - * Boolean that keeps track of whether hits are sorted ascending (true) - * or descending (false) by title - * @type {Boolean} - */ - self.isAscending = true; - - /** - * Sorts saved object finder hits either ascending or descending - * @param {Array} hits Array of saved finder object hits - * @return {Array} Array sorted either ascending or descending - */ - self.sortHits = function (hits) { - self.isAscending = !self.isAscending; - self.hits = self.isAscending - ? _.sortBy(hits, ['title']) - : _.sortBy(hits, ['title']).reverse(); - }; - - /** - * Passed the hit objects and will determine if the - * hit should have a url in the UI, returns it if so - * @return {string|null} - the url or nothing - */ - self.makeUrl = function (hit) { - if ($scope.userMakeUrl) { - return $scope.userMakeUrl(hit); - } - - if (!$scope.userOnChoose) { - return hit.url; - } - - return '#'; - }; - - self.preventClick = function ($event) { - $event.preventDefault(); - }; - - /** - * Called when a hit object is clicked, can override the - * url behavior if necessary. - */ - self.onChoose = function (hit, $event) { - if ($scope.userOnChoose) { - $scope.userOnChoose(hit, $event); - } - - const url = self.makeUrl(hit); - if (!url || url === '#' || url.charAt(0) !== '#') return; - - $event.preventDefault(); - - history.push(url.substr(1)); - }; - - $scope.$watch('filter', function (newFilter) { - // ensure that the currentFilter changes from undefined to '' - // which triggers - currentFilter = newFilter || ''; - filterResults(); - }); - - $scope.pageFirstItem = 0; - $scope.pageLastItem = 0; - $scope.onPageChanged = (page) => { - $scope.pageFirstItem = page.firstItem; - $scope.pageLastItem = page.lastItem; - }; - - //manages the state of the keyboard selector - self.selector = { - enabled: false, - index: -1, - }; - - self.getLabel = function () { - return _.words(self.properties.nouns).map(_.capitalize).join(' '); - }; - - //key handler for the filter text box - self.filterKeyDown = function ($event) { - switch (keyMap[$event.keyCode]) { - case 'enter': - if (self.hitCount !== 1) return; - const hit = self.hits[0]; - if (!hit) return; - - self.onChoose(hit, $event); - $event.preventDefault(); - break; - } - }; - - //key handler for the list items - self.hitKeyDown = function ($event, page, paginate) { - switch (keyMap[$event.keyCode]) { - case 'tab': - if (!self.selector.enabled) break; - - self.selector.index = -1; - self.selector.enabled = false; - - //if the user types shift-tab return to the textbox - //if the user types tab, set the focus to the currently selected hit. - if ($event.shiftKey) { - $input.focus(); - } else { - $list.find('li.active a').focus(); - } - - $event.preventDefault(); - break; - case 'down': - if (!self.selector.enabled) break; - - if (self.selector.index + 1 < page.length) { - self.selector.index += 1; - } - $event.preventDefault(); - break; - case 'up': - if (!self.selector.enabled) break; - - if (self.selector.index > 0) { - self.selector.index -= 1; - } - $event.preventDefault(); - break; - case 'right': - if (!self.selector.enabled) break; - - if (page.number < page.count) { - paginate.goToPage(page.number + 1); - self.selector.index = 0; - selectTopHit(); - } - $event.preventDefault(); - break; - case 'left': - if (!self.selector.enabled) break; - - if (page.number > 1) { - paginate.goToPage(page.number - 1); - self.selector.index = 0; - selectTopHit(); - } - $event.preventDefault(); - break; - case 'escape': - if (!self.selector.enabled) break; - - $input.focus(); - $event.preventDefault(); - break; - case 'enter': - if (!self.selector.enabled) break; - - const hitIndex = (page.number - 1) * paginate.perPage + self.selector.index; - const hit = self.hits[hitIndex]; - if (!hit) break; - - self.onChoose(hit, $event); - $event.preventDefault(); - break; - case 'shift': - break; - default: - $input.focus(); - break; - } - }; - - self.hitBlur = function () { - self.selector.index = -1; - self.selector.enabled = false; - }; - - self.manageObjects = function (type) { - $location.url('/management/kibana/objects?_a=' + rison.encode({ tab: type })); - }; - - self.hitCountNoun = function () { - return (self.hitCount === 1 - ? self.properties.noun - : self.properties.nouns - ).toLowerCase(); - }; - - function selectTopHit() { - setTimeout(function () { - //triggering a focus event kicks off a new angular digest cycle. - $list.find('a:first').focus(); - }, 0); - } - - function filterResults() { - if (!self.service) return; - if (!self.properties) return; - - // track the filter that we use for this search, - // but ensure that we don't search for the same - // thing twice. This is called from multiple places - // and needs to be smart about when it actually searches - const filter = currentFilter; - if (prevSearch === filter) return; - - prevSearch = filter; - - const isLabsEnabled = uiSettings.get(VISUALIZE_ENABLE_LABS_SETTING); - self.service.find(filter).then(function (hits) { - hits.hits = hits.hits.filter( - (hit) => isLabsEnabled || _.get(hit, 'type.stage') !== 'experimental' - ); - hits.total = hits.hits.length; - - // ensure that we don't display old results - // as we can't really cancel requests - if (currentFilter === filter) { - self.hitCount = hits.total; - self.hits = _.sortBy(hits.hits, ['title']); - } - }); - } - }, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/saved_object_save_as_checkbox.html b/src/plugins/timelion/public/directives/saved_object_save_as_checkbox.html deleted file mode 100644 index a001ddc751748..0000000000000 --- a/src/plugins/timelion/public/directives/saved_object_save_as_checkbox.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
- - -
- diff --git a/src/plugins/timelion/public/directives/saved_object_save_as_checkbox.js b/src/plugins/timelion/public/directives/saved_object_save_as_checkbox.js deleted file mode 100644 index 865e5ea473b85..0000000000000 --- a/src/plugins/timelion/public/directives/saved_object_save_as_checkbox.js +++ /dev/null @@ -1,22 +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 saveObjectSaveAsCheckboxTemplate from './saved_object_save_as_checkbox.html'; - -export function initSavedObjectSaveAsCheckBoxDirective(app) { - app.directive('savedObjectSaveAsCheckBox', function () { - return { - restrict: 'E', - template: saveObjectSaveAsCheckboxTemplate, - replace: true, - scope: { - savedObject: '=', - }, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/timelion_expression_input.html b/src/plugins/timelion/public/directives/timelion_expression_input.html deleted file mode 100644 index 6c115118860ba..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_expression_input.html +++ /dev/null @@ -1,41 +0,0 @@ -
- - - - -
diff --git a/src/plugins/timelion/public/directives/timelion_expression_input.js b/src/plugins/timelion/public/directives/timelion_expression_input.js deleted file mode 100644 index c29c802914987..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_expression_input.js +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -/** - * Timelion Expression Autocompleter - * - * This directive allows users to enter multiline timelion expressions. If the user has entered - * a valid expression and then types a ".", this directive will display a list of suggestions. - * - * Users can navigate suggestions using the arrow keys. When a user selects a suggestion, it's - * inserted into the expression and the caret position is updated to be inside of the newly- - * added function's parentheses. - * - * Beneath the hood, we use a PEG grammar to validate the Timelion expression and detect if - * the caret is in a position within the expression that allows functions to be suggested. - * - * NOTE: This directive doesn't work well with contenteditable divs. Challenges include: - * - You have to replace markup with newline characters and spaces when passing the expression - * to the grammar. - * - You have to do the opposite when loading a saved expression, so that it appears correctly - * within the contenteditable (i.e. replace newlines with
markup). - * - The Range and Selection APIs ignore newlines when providing caret position, so there is - * literally no way to insert suggestions into the correct place in a multiline expression - * that has more than a single consecutive newline. - */ - -import _ from 'lodash'; -import $ from 'jquery'; -import timelionExpressionInputTemplate from './timelion_expression_input.html'; -import { - SUGGESTION_TYPE, - Suggestions, - suggest, - insertAtLocation, -} from './timelion_expression_input_helpers'; -import { comboBoxKeyCodes } from '@elastic/eui'; - -export function timelionExpInput(deps) { - return ($http, $timeout) => { - return { - restrict: 'E', - scope: { - rows: '=', - sheet: '=', - updateChart: '&', - shouldPopoverSuggestions: '@', - }, - replace: true, - template: timelionExpressionInputTemplate, - link: function (scope, elem) { - const argValueSuggestions = deps.plugins.visTypeTimelion.getArgValueSuggestions(); - const expressionInput = elem.find('[data-expression-input]'); - const functionReference = {}; - let suggestibleFunctionLocation = {}; - - scope.suggestions = new Suggestions(); - - function init() { - $http.get('../api/timelion/functions').then(function (resp) { - Object.assign(functionReference, { - byName: _.keyBy(resp.data, 'name'), - list: resp.data, - }); - }); - } - - function setCaretOffset(caretOffset) { - // Wait for Angular to update the input with the new expression and *then* we can set - // the caret position. - $timeout(() => { - expressionInput.focus(); - expressionInput[0].selectionStart = expressionInput[0].selectionEnd = caretOffset; - scope.$apply(); - }, 0); - } - - function insertSuggestionIntoExpression(suggestionIndex) { - if (scope.suggestions.isEmpty()) { - return; - } - - const { min, max } = suggestibleFunctionLocation; - let insertedValue; - let insertPositionMinOffset = 0; - - switch (scope.suggestions.type) { - case SUGGESTION_TYPE.FUNCTIONS: { - // Position the caret inside of the function parentheses. - insertedValue = `${scope.suggestions.list[suggestionIndex].name}()`; - - // min advanced one to not replace function '.' - insertPositionMinOffset = 1; - break; - } - case SUGGESTION_TYPE.ARGUMENTS: { - // Position the caret after the '=' - insertedValue = `${scope.suggestions.list[suggestionIndex].name}=`; - break; - } - case SUGGESTION_TYPE.ARGUMENT_VALUE: { - // Position the caret after the argument value - insertedValue = `${scope.suggestions.list[suggestionIndex].name}`; - break; - } - } - - const updatedExpression = insertAtLocation( - insertedValue, - scope.sheet, - min + insertPositionMinOffset, - max - ); - scope.sheet = updatedExpression; - - const newCaretOffset = min + insertedValue.length; - setCaretOffset(newCaretOffset); - } - - function scrollToSuggestionAt(index) { - // We don't cache these because the list changes based on user input. - const suggestionsList = $('[data-suggestions-list]'); - const suggestionListItem = $('[data-suggestion-list-item]')[index]; - // Scroll to the position of the item relative to the list, not to the window. - suggestionsList.scrollTop(suggestionListItem.offsetTop - suggestionsList[0].offsetTop); - } - - function getCursorPosition() { - if (expressionInput.length) { - return expressionInput[0].selectionStart; - } - return null; - } - - async function getSuggestions() { - const suggestions = await suggest( - scope.sheet, - functionReference.list, - getCursorPosition(), - argValueSuggestions - ); - - // We're using ES6 Promises, not $q, so we have to wrap this in $apply. - scope.$apply(() => { - if (suggestions) { - scope.suggestions.setList(suggestions.list, suggestions.type); - scope.suggestions.show(); - suggestibleFunctionLocation = suggestions.location; - $timeout(() => { - const suggestionsList = $('[data-suggestions-list]'); - suggestionsList.scrollTop(0); - }, 0); - return; - } - - suggestibleFunctionLocation = undefined; - scope.suggestions.reset(); - }); - } - - function isNavigationalKey(keyCode) { - const keyCodes = _.values(comboBoxKeyCodes); - return keyCodes.includes(keyCode); - } - - scope.onFocusInput = () => { - // Wait for the caret position of the input to update and then we can get suggestions - // (which depends on the caret position). - $timeout(getSuggestions, 0); - }; - - scope.onBlurInput = () => { - scope.suggestions.hide(); - }; - - scope.onKeyDownInput = (e) => { - // If we've pressed any non-navigational keys, then the user has typed something and we - // can exit early without doing any navigation. The keyup handler will pull up suggestions. - if (!isNavigationalKey(e.keyCode)) { - return; - } - - switch (e.keyCode) { - case comboBoxKeyCodes.UP: - if (scope.suggestions.isVisible) { - // Up and down keys navigate through suggestions. - e.preventDefault(); - scope.suggestions.stepForward(); - scrollToSuggestionAt(scope.suggestions.index); - } - break; - - case comboBoxKeyCodes.DOWN: - if (scope.suggestions.isVisible) { - // Up and down keys navigate through suggestions. - e.preventDefault(); - scope.suggestions.stepBackward(); - scrollToSuggestionAt(scope.suggestions.index); - } - break; - - case comboBoxKeyCodes.TAB: - // If there are no suggestions or none is selected, the user tabs to the next input. - if (scope.suggestions.isEmpty() || scope.suggestions.index < 0) { - // Before letting the tab be handled to focus the next element - // we need to hide the suggestions, otherwise it will focus these - // instead of the time interval select. - scope.suggestions.hide(); - return; - } - - // If we have suggestions, complete the selected one. - e.preventDefault(); - insertSuggestionIntoExpression(scope.suggestions.index); - break; - - case comboBoxKeyCodes.ENTER: - if (e.metaKey || e.ctrlKey) { - // Re-render the chart when the user hits CMD+ENTER. - e.preventDefault(); - scope.updateChart(); - } else if (!scope.suggestions.isEmpty()) { - // If the suggestions are open, complete the expression with the suggestion. - e.preventDefault(); - insertSuggestionIntoExpression(scope.suggestions.index); - } - break; - - case comboBoxKeyCodes.ESCAPE: - e.preventDefault(); - scope.suggestions.hide(); - break; - } - }; - - scope.onKeyUpInput = (e) => { - // If the user isn't navigating, then we should update the suggestions based on their input. - if (!isNavigationalKey(e.keyCode)) { - getSuggestions(); - } - }; - - scope.onClickExpression = () => { - getSuggestions(); - }; - - scope.onClickSuggestion = (index) => { - insertSuggestionIntoExpression(index); - }; - - scope.getActiveSuggestionId = () => { - if (scope.suggestions.isVisible && scope.suggestions.index > -1) { - return `timelionSuggestion${scope.suggestions.index}`; - } - return ''; - }; - - init(); - }, - }; - }; -} diff --git a/src/plugins/timelion/public/directives/timelion_expression_input_helpers.js b/src/plugins/timelion/public/directives/timelion_expression_input_helpers.js deleted file mode 100644 index 0bc5897c49d6f..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_expression_input_helpers.js +++ /dev/null @@ -1,264 +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 _ from 'lodash'; -import { _LEGACY_ as visTypeTimelion } from '../../../vis_type_timelion/public'; - -export const SUGGESTION_TYPE = { - ARGUMENTS: 'arguments', - ARGUMENT_VALUE: 'argument_value', - FUNCTIONS: 'functions', -}; - -export class Suggestions { - constructor() { - this.reset(); - } - - reset() { - this.index = -1; - this.list = []; - this.type = null; - this.isVisible = false; - } - - setList(list, type) { - this.list = list.sort((a, b) => { - if (a.name < b.name) { - return -1; - } - if (a.name > b.name) { - return 1; - } - // names must be equal - return 0; - }); - this.type = type; - - // Only try to position index inside of list range, when it was already focused - // beforehand (i.e. not -1) - if (this.index > -1) { - // We may get a shorter list than the one we have now, so we need to make sure our index doesn't - // fall outside of the new list's range. - this.index = Math.max(0, Math.min(this.index, this.list.length - 1)); - } - } - - getCount() { - return this.list.length; - } - - isEmpty() { - return this.list.length === 0; - } - - show() { - this.isVisible = true; - } - - hide() { - this.isVisible = false; - } - - stepForward() { - if (this.index > 0) { - this.index -= 1; - } - } - - stepBackward() { - if (this.index < this.list.length - 1) { - this.index += 1; - } - } -} - -function inLocation(cursorPosition, location) { - return cursorPosition >= location.min && cursorPosition <= location.max; -} - -function getArgumentsHelp(functionHelp, functionArgs = []) { - if (!functionHelp) { - return []; - } - - // Do not provide 'inputSeries' as argument suggestion for chainable functions - const argsHelp = functionHelp.chainable ? functionHelp.args.slice(1) : functionHelp.args.slice(0); - - // ignore arguments that are already provided in function declaration - const functionArgNames = functionArgs.map((arg) => { - return arg.name; - }); - return argsHelp.filter((arg) => { - return !functionArgNames.includes(arg.name); - }); -} - -async function extractSuggestionsFromParsedResult( - result, - cursorPosition, - functionList, - argValueSuggestions -) { - const activeFunc = result.functions.find((func) => { - return cursorPosition >= func.location.min && cursorPosition < func.location.max; - }); - - if (!activeFunc) { - return; - } - - const functionHelp = functionList.find((func) => { - return func.name === activeFunc.function; - }); - - // return function suggestion when cursor is outside of parentheses - // location range includes '.', function name, and '('. - const openParen = activeFunc.location.min + activeFunc.function.length + 2; - if (cursorPosition < openParen) { - return { list: [functionHelp], location: activeFunc.location, type: SUGGESTION_TYPE.FUNCTIONS }; - } - - // return argument value suggestions when cursor is inside argument value - const activeArg = activeFunc.arguments.find((argument) => { - return inLocation(cursorPosition, argument.location); - }); - if ( - activeArg && - activeArg.type === 'namedArg' && - inLocation(cursorPosition, activeArg.value.location) - ) { - const { function: functionName, arguments: functionArgs } = activeFunc; - - const { - name: argName, - value: { text: partialInput }, - } = activeArg; - - let valueSuggestions; - if (argValueSuggestions.hasDynamicSuggestionsForArgument(functionName, argName)) { - valueSuggestions = await argValueSuggestions.getDynamicSuggestionsForArgument( - functionName, - argName, - functionArgs, - partialInput - ); - } else { - const { suggestions: staticSuggestions } = functionHelp.args.find((arg) => { - return arg.name === activeArg.name; - }); - valueSuggestions = argValueSuggestions.getStaticSuggestionsForInput( - partialInput, - staticSuggestions - ); - } - return { - list: valueSuggestions, - location: activeArg.value.location, - type: SUGGESTION_TYPE.ARGUMENT_VALUE, - }; - } - - // return argument suggestions - const argsHelp = getArgumentsHelp(functionHelp, activeFunc.arguments); - const argumentSuggestions = argsHelp.filter((arg) => { - if (_.get(activeArg, 'type') === 'namedArg') { - return _.startsWith(arg.name, activeArg.name); - } else if (activeArg) { - return _.startsWith(arg.name, activeArg.text); - } - return true; - }); - const location = activeArg ? activeArg.location : { min: cursorPosition, max: cursorPosition }; - return { list: argumentSuggestions, location: location, type: SUGGESTION_TYPE.ARGUMENTS }; -} - -export async function suggest(expression, functionList, cursorPosition, argValueSuggestions) { - try { - const result = await visTypeTimelion.parseTimelionExpressionAsync(expression); - return await extractSuggestionsFromParsedResult( - result, - cursorPosition, - functionList, - argValueSuggestions - ); - } catch (e) { - let message; - try { - // The grammar will throw an error containing a message if the expression is formatted - // correctly and is prepared to accept suggestions. If the expression is not formatted - // correctly the grammar will just throw a regular PEG SyntaxError, and this JSON.parse - // attempt will throw an error. - message = JSON.parse(e.message); - } catch (e) { - // The expression isn't correctly formatted, so JSON.parse threw an error. - return; - } - - switch (message.type) { - case 'incompleteFunction': { - let list; - if (message.function) { - // The user has start typing a function name, so we'll filter the list down to only - // possible matches. - list = functionList.filter((func) => _.startsWith(func.name, message.function)); - } else { - // The user hasn't typed anything yet, so we'll just return the entire list. - list = functionList; - } - return { list, location: message.location, type: SUGGESTION_TYPE.FUNCTIONS }; - } - case 'incompleteArgument': { - const { currentFunction: functionName, currentArgs: functionArgs } = message; - const functionHelp = functionList.find((func) => func.name === functionName); - return { - list: getArgumentsHelp(functionHelp, functionArgs), - location: message.location, - type: SUGGESTION_TYPE.ARGUMENTS, - }; - } - case 'incompleteArgumentValue': { - const { name: argName, currentFunction: functionName, currentArgs: functionArgs } = message; - let valueSuggestions = []; - if (argValueSuggestions.hasDynamicSuggestionsForArgument(functionName, argName)) { - valueSuggestions = await argValueSuggestions.getDynamicSuggestionsForArgument( - functionName, - argName, - functionArgs - ); - } else { - const functionHelp = functionList.find((func) => func.name === functionName); - if (functionHelp) { - const argHelp = functionHelp.args.find((arg) => arg.name === argName); - if (argHelp && argHelp.suggestions) { - valueSuggestions = argHelp.suggestions; - } - } - } - return { - list: valueSuggestions, - location: { min: cursorPosition, max: cursorPosition }, - type: SUGGESTION_TYPE.ARGUMENT_VALUE, - }; - } - } - } -} - -export function insertAtLocation( - valueToInsert, - destination, - replacementRangeStart, - replacementRangeEnd -) { - // Insert the value at a location caret within the destination. - const prefix = destination.slice(0, replacementRangeStart); - const suffix = destination.slice(replacementRangeEnd, destination.length); - const result = `${prefix}${valueToInsert}${suffix}`; - return result; -} diff --git a/src/plugins/timelion/public/directives/timelion_expression_suggestions/_index.scss b/src/plugins/timelion/public/directives/timelion_expression_suggestions/_index.scss deleted file mode 100644 index 6fd0098aea68e..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_expression_suggestions/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './timelion_expression_suggestions'; diff --git a/src/plugins/timelion/public/directives/timelion_expression_suggestions/_timelion_expression_suggestions.scss b/src/plugins/timelion/public/directives/timelion_expression_suggestions/_timelion_expression_suggestions.scss deleted file mode 100644 index 4bf6ba24108d2..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_expression_suggestions/_timelion_expression_suggestions.scss +++ /dev/null @@ -1,36 +0,0 @@ -.timSuggestions { - @include euiBottomShadowMedium; - background-color: $euiColorLightestShade; - color: $euiTextColor; - border: $euiBorderThin; - // sass-lint:disable-block no-important - border-radius: 0 0 $euiBorderRadius $euiBorderRadius !important; - z-index: $euiZLevel9; - max-height: $euiSizeXL * 10; - overflow-y: auto; - - &.timSuggestions-isPopover { - position: absolute; - top: 100%; - } -} - -.timSuggestions__item { - border-bottom: $euiBorderThin; - padding: $euiSizeXS $euiSizeL; - - &:hover, - &.active { - background-color: $euiColorLightShade; - } -} - -.timSuggestions__details { - background-color: $euiColorLightestShade; - padding: $euiSizeM; - border-radius: $euiBorderRadius; - - > table { - margin-bottom: 0; - } -} diff --git a/src/plugins/timelion/public/directives/timelion_expression_suggestions/timelion_expression_suggestions.html b/src/plugins/timelion/public/directives/timelion_expression_suggestions/timelion_expression_suggestions.html deleted file mode 100644 index ddb9f21615aee..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_expression_suggestions/timelion_expression_suggestions.html +++ /dev/null @@ -1,109 +0,0 @@ -
-
- -
- -
-

- .{{suggestion.name}}() - - - - -

- -
-
- - - {{arg.name}}=({{arg.types.join(' | ')}}) - , - -
- -
- - - - - - - - - - - -
{{arg.name}}{{arg.types.join(', ')}}{{arg.help}}
-
-
-
- -
-

- {{suggestion.name}}= - - {{suggestion.help}} - -

-
- Accepts: - {{suggestion.types.join(', ')}} -
-
- -
-

- {{suggestion.name}} - - {{suggestion.help}} - -

-
- -
-
-
diff --git a/src/plugins/timelion/public/directives/timelion_expression_suggestions/timelion_expression_suggestions.js b/src/plugins/timelion/public/directives/timelion_expression_suggestions/timelion_expression_suggestions.js deleted file mode 100644 index cce0f17f4ef1a..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_expression_suggestions/timelion_expression_suggestions.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 template from './timelion_expression_suggestions.html'; - -export function TimelionExpressionSuggestions() { - return { - restrict: 'E', - scope: { - suggestions: '=', - suggestionsType: '=', - selectedIndex: '=', - onClickSuggestion: '&', - shouldPopover: '=', - }, - replace: true, - template, - link: function (scope) { - // This will prevent the expression input from losing focus. - scope.onMouseDown = (e) => e.preventDefault(); - }, - }; -} diff --git a/src/plugins/timelion/public/directives/timelion_grid.js b/src/plugins/timelion/public/directives/timelion_grid.js deleted file mode 100644 index cb55dd6b8e110..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_grid.js +++ /dev/null @@ -1,57 +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 $ from 'jquery'; - -export function initTimelionGridDirective(app) { - app.directive('timelionGrid', function () { - return { - restrict: 'A', - scope: { - timelionGridRows: '=', - timelionGridColumns: '=', - }, - link: function ($scope, $elem) { - function init() { - setDimensions(); - } - - $scope.$on('$destroy', function () { - $(window).off('resize'); //remove the handler added earlier - }); - - $(window).resize(function () { - setDimensions(); - }); - - $scope.$watchMulti(['timelionGridColumns', 'timelionGridRows'], function () { - setDimensions(); - }); - - function setDimensions() { - const borderSize = 2; - const headerSize = 45 + 35 + 28 + 20 * 2; // chrome + subnav + buttons + (container padding) - const verticalPadding = 10; - - if ($scope.timelionGridColumns != null) { - $elem.width($elem.parent().width() / $scope.timelionGridColumns - borderSize * 2); - } - - if ($scope.timelionGridRows != null) { - $elem.height( - ($(window).height() - headerSize) / $scope.timelionGridRows - - (verticalPadding + borderSize * 2) - ); - } - } - - init(); - }, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/timelion_help/_index.scss b/src/plugins/timelion/public/directives/timelion_help/_index.scss deleted file mode 100644 index 4228e56180066..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_help/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './timelion_help'; diff --git a/src/plugins/timelion/public/directives/timelion_help/_timelion_help.scss b/src/plugins/timelion/public/directives/timelion_help/_timelion_help.scss deleted file mode 100644 index 1f8551116aab0..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_help/_timelion_help.scss +++ /dev/null @@ -1,33 +0,0 @@ -.timHelp { - // EUITODO: Make .euiText > code background transparent - code { - background-color: transparentize($euiTextColor, .9); - } -} - -.timHelp__buttons { - display: flex; - justify-content: space-between; -} - -.timHelp__functions { - height: $euiSizeXL * 10; - overflow-y: auto; -} - -.timHelp__links { - color: $euiColorPrimary; - - &:hover { - text-decoration: underline; - } -} - -/** - * 1. Override bootstrap .table styles. - */ -.timHelp__functionsTableRow:hover, -.timHelp__functionDetailsTable { - // sass-lint:disable-block no-important - background-color: $euiColorLightestShade !important; /* 1 */ -} diff --git a/src/plugins/timelion/public/directives/timelion_help/timelion_help.html b/src/plugins/timelion/public/directives/timelion_help/timelion_help.html deleted file mode 100644 index 4c4fdfe4faf51..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_help/timelion_help.html +++ /dev/null @@ -1,741 +0,0 @@ -
-
-
-

-

-

- - . -

-
-
- - - - - - -
-
-
-
-
-

-

-

-
-
- - - - - - - -
-
-
-
-

-

- - - -

-

-
    -
  • - -

    - - - - - - -

    -
  • -
  • - -

    -
  • -
-

-
-
- - - - - - -
-
-
-
-
-

-

-

- - - -

-

-

-

-

- - - -

-
-
- - - - - - -
-
- -
-
-

-

-

-

- - - - - - - - - - - - - - - - - -
.es(*), .es(US)
.es(*).color(#f66), .es(US).bars(1)
- .es(*).color(#f66).lines(fill=3), - .es(US).bars(1).points(radius=3, weight=1) -
(.es(*), .es(GB)).points()
-

- - . -

-
-
- - - - - - -
-
-
-
-

-

-

-

-

-

-

- - - -

-
-
- - - - - - - -
-
-
-
-

- - - - -
-
- - . -
- -
- - - - - - - - -
.{{function.name}}(){{function.help}}
-
- - - - - - - - - - - -
{{arg.name}}{{arg.types.join(', ')}}{{arg.help}}
-
- -
-
-
-
-
- -
- -
-
-
-
-
Ctrl/Cmd + Enter
-
-
- - -
-
-
- -
-
-
-
Enter/Tab
-
-
Esc
-
-
-
-
diff --git a/src/plugins/timelion/public/directives/timelion_help/timelion_help.js b/src/plugins/timelion/public/directives/timelion_help/timelion_help.js deleted file mode 100644 index ee518a8bce75c..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_help/timelion_help.js +++ /dev/null @@ -1,155 +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 template from './timelion_help.html'; -import { i18n } from '@kbn/i18n'; -import _ from 'lodash'; -import moment from 'moment'; - -export function initTimelionHelpDirective(app) { - app.directive('timelionHelp', function ($http) { - return { - restrict: 'E', - template, - controller: function ($scope) { - $scope.functions = { - list: [], - details: null, - }; - - $scope.activeTab = 'funcref'; - $scope.activateTab = function (tabName) { - $scope.activeTab = tabName; - }; - - function init() { - $scope.es = { - invalidCount: 0, - }; - - $scope.translations = { - nextButtonLabel: i18n.translate('timelion.help.nextPageButtonLabel', { - defaultMessage: 'Next', - }), - previousButtonLabel: i18n.translate('timelion.help.previousPageButtonLabel', { - defaultMessage: 'Previous', - }), - dontShowHelpButtonLabel: i18n.translate('timelion.help.dontShowHelpButtonLabel', { - defaultMessage: `Don't show this again`, - }), - strongNextText: i18n.translate('timelion.help.welcome.content.strongNextText', { - defaultMessage: 'Next', - }), - emphasizedEverythingText: i18n.translate( - 'timelion.help.welcome.content.emphasizedEverythingText', - { - defaultMessage: 'everything', - } - ), - notValidAdvancedSettingsPath: i18n.translate( - 'timelion.help.configuration.notValid.advancedSettingsPathText', - { - defaultMessage: 'Management / Kibana / Advanced Settings', - } - ), - validAdvancedSettingsPath: i18n.translate( - 'timelion.help.configuration.valid.advancedSettingsPathText', - { - defaultMessage: 'Management/Kibana/Advanced Settings', - } - ), - esAsteriskQueryDescription: i18n.translate( - 'timelion.help.querying.esAsteriskQueryDescriptionText', - { - defaultMessage: 'hey Elasticsearch, find everything in my default index', - } - ), - esIndexQueryDescription: i18n.translate( - 'timelion.help.querying.esIndexQueryDescriptionText', - { - defaultMessage: 'use * as the q (query) for the logstash-* index', - } - ), - strongAddText: i18n.translate('timelion.help.expressions.strongAddText', { - defaultMessage: 'Add', - }), - twoExpressionsDescriptionTitle: i18n.translate( - 'timelion.help.expressions.examples.twoExpressionsDescriptionTitle', - { - defaultMessage: 'Double the fun.', - } - ), - customStylingDescriptionTitle: i18n.translate( - 'timelion.help.expressions.examples.customStylingDescriptionTitle', - { - defaultMessage: 'Custom styling.', - } - ), - namedArgumentsDescriptionTitle: i18n.translate( - 'timelion.help.expressions.examples.namedArgumentsDescriptionTitle', - { - defaultMessage: 'Named arguments.', - } - ), - groupedExpressionsDescriptionTitle: i18n.translate( - 'timelion.help.expressions.examples.groupedExpressionsDescriptionTitle', - { - defaultMessage: 'Grouped expressions.', - } - ), - }; - - getFunctions(); - checkElasticsearch(); - } - - function getFunctions() { - return $http.get('../api/timelion/functions').then(function (resp) { - $scope.functions.list = resp.data; - }); - } - $scope.recheckElasticsearch = function () { - $scope.es.valid = null; - checkElasticsearch().then(function (valid) { - if (!valid) $scope.es.invalidCount++; - }); - }; - - function checkElasticsearch() { - return $http.get('../api/timelion/validate/es').then(function (resp) { - if (resp.data.ok) { - $scope.es.valid = true; - $scope.es.stats = { - min: moment(resp.data.min).format('LLL'), - max: moment(resp.data.max).format('LLL'), - field: resp.data.field, - }; - } else { - $scope.es.valid = false; - $scope.es.invalidReason = (function () { - try { - const esResp = JSON.parse(resp.data.resp.response); - return _.get(esResp, 'error.root_cause[0].reason'); - } catch (e) { - if (_.get(resp, 'data.resp.message')) return _.get(resp, 'data.resp.message'); - if (_.get(resp, 'data.resp.output.payload.message')) - return _.get(resp, 'data.resp.output.payload.message'); - return i18n.translate('timelion.help.unknownErrorMessage', { - defaultMessage: 'Unknown error', - }); - } - })(); - } - return $scope.es.valid; - }); - } - init(); - }, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/timelion_interval/_index.scss b/src/plugins/timelion/public/directives/timelion_interval/_index.scss deleted file mode 100644 index 2a201f9b35a4d..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_interval/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './timelion_interval'; diff --git a/src/plugins/timelion/public/directives/timelion_interval/_timelion_interval.scss b/src/plugins/timelion/public/directives/timelion_interval/_timelion_interval.scss deleted file mode 100644 index 7ce09155cafd8..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_interval/_timelion_interval.scss +++ /dev/null @@ -1,30 +0,0 @@ -timelion-interval { - display: flex; -} - -.timInterval__input { - width: $euiSizeXL * 2; - padding: $euiSizeXS $euiSizeM; - color: $euiColorDarkestShade; - border: 1px solid $euiColorLightShade; - border-radius: $euiSizeXS; - transition: border-color .1s linear; - font-size: 14px; -} - -.timInterval__input--compact { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.timInterval__presets { - width: $euiSizeXL * 3; -} - -.timInterval__presets--compact { - width: $euiSizeXL * 1; - padding-left: 0; - border-left: none; - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} diff --git a/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.html b/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.html deleted file mode 100644 index 49009355e49f4..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.html +++ /dev/null @@ -1,22 +0,0 @@ - diff --git a/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.js b/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.js deleted file mode 100644 index 55f50fff132eb..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import _ from 'lodash'; -import $ from 'jquery'; -import template from './timelion_interval.html'; - -export function TimelionInterval($timeout) { - return { - restrict: 'E', - scope: { - // The interval model - model: '=', - changeInterval: '=', - }, - template, - link: function ($scope, $elem) { - $scope.intervalOptions = ['auto', '1s', '1m', '1h', '1d', '1w', '1M', '1y', 'other']; - $scope.intervalLabels = { - auto: 'auto', - '1s': '1 second', - '1m': '1 minute', - '1h': '1 hour', - '1d': '1 day', - '1w': '1 week', - '1M': '1 month', - '1y': '1 year', - other: 'other', - }; - - $scope.$watch('model', function (newVal, oldVal) { - // Only run this on initialization - if (newVal !== oldVal || oldVal == null) return; - - if (_.includes($scope.intervalOptions, newVal)) { - $scope.interval = newVal; - } else { - $scope.interval = 'other'; - } - - if (newVal !== 'other') { - $scope.otherInterval = newVal; - } - }); - - $scope.$watch('interval', function (newVal, oldVal) { - if (newVal === oldVal || $scope.model === newVal) return; - - if (newVal === 'other') { - $scope.otherInterval = oldVal; - $scope.changeInterval($scope.otherInterval); - $timeout(function () { - $('input', $elem).select(); - }, 0); - } else { - $scope.otherInterval = $scope.interval; - $scope.changeInterval($scope.interval); - } - }); - - $scope.$watch('otherInterval', function (newVal, oldVal) { - if (newVal === oldVal || $scope.model === newVal) return; - $scope.changeInterval(newVal); - }); - }, - }; -} diff --git a/src/plugins/timelion/public/directives/timelion_load_sheet.js b/src/plugins/timelion/public/directives/timelion_load_sheet.js deleted file mode 100644 index 83b8b4c2e262b..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_load_sheet.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. - */ - -import template from '../partials/load_sheet.html'; - -export function initTimelionLoadSheetDirective(app) { - app.directive('timelionLoad', function () { - return { - replace: true, - restrict: 'E', - template, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/timelion_options_sheet.js b/src/plugins/timelion/public/directives/timelion_options_sheet.js deleted file mode 100644 index b3560111d4152..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_options_sheet.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. - */ - -import template from '../partials/sheet_options.html'; - -export function initTimelionOptionsSheetDirective(app) { - app.directive('timelionOptions', function () { - return { - replace: true, - restrict: 'E', - template, - }; - }); -} diff --git a/src/plugins/timelion/public/directives/timelion_save_sheet.js b/src/plugins/timelion/public/directives/timelion_save_sheet.js deleted file mode 100644 index 40289b1bb8353..0000000000000 --- a/src/plugins/timelion/public/directives/timelion_save_sheet.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. - */ - -import saveTemplate from '../partials/save_sheet.html'; - -export function initTimelionSaveSheetDirective(app) { - app.directive('timelionSave', function () { - return { - replace: true, - restrict: 'E', - template: saveTemplate, - }; - }); -} diff --git a/src/plugins/timelion/public/index.html b/src/plugins/timelion/public/index.html deleted file mode 100644 index 3fb518e81e882..0000000000000 --- a/src/plugins/timelion/public/index.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - -
- - - - - -
- -
-
- -
-
- -
- -
- - - -
-
- -
- - - -
-
-
- - diff --git a/src/plugins/timelion/public/index.scss b/src/plugins/timelion/public/index.scss deleted file mode 100644 index 7a4259b2a17c8..0000000000000 --- a/src/plugins/timelion/public/index.scss +++ /dev/null @@ -1,18 +0,0 @@ -/* Timelion plugin styles */ - -// Prefix all styles with "tim" to avoid conflicts. -// Examples -// timChart -// timChart__legend -// timChart__legend--small -// timChart__legend-isLoading - -@import './app'; -@import './base'; -@import './directives/index'; - -// these styles is needed to be loaded here explicitly if the timelion visualization was not opened in browser -// styles for timelion visualization are lazy loaded only while a vis is opened -// this will duplicate styles only if both Timelion app and timelion visualization are loaded -// could be left here as it is since the Timelion app is deprecated -@import '../../vis_type_timelion/public/legacy/timelion_vis.scss'; diff --git a/src/plugins/timelion/public/index.ts b/src/plugins/timelion/public/index.ts deleted file mode 100644 index 866d8a4ad20a2..0000000000000 --- a/src/plugins/timelion/public/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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 { PluginInitializerContext } from 'kibana/public'; -import { TimelionPlugin as Plugin } from './plugin'; - -export function plugin(initializerContext: PluginInitializerContext) { - return new Plugin(initializerContext); -} diff --git a/src/plugins/timelion/public/lib/observe_resize.js b/src/plugins/timelion/public/lib/observe_resize.js deleted file mode 100644 index d2902a59821c3..0000000000000 --- a/src/plugins/timelion/public/lib/observe_resize.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export default function ($elem, fn, frequency) { - frequency = frequency || 500; - let currentHeight = $elem.height(); - let currentWidth = $elem.width(); - - let timeout; - - function checkLoop() { - timeout = setTimeout(function () { - if (currentHeight !== $elem.height() || currentWidth !== $elem.width()) { - currentHeight = $elem.height(); - currentWidth = $elem.width(); - - if (currentWidth > 0 && currentWidth > 0) fn(); - } - checkLoop(); - }, frequency); - } - - checkLoop(); - - return function () { - clearTimeout(timeout); - }; -} diff --git a/src/plugins/timelion/public/panels/panel.ts b/src/plugins/timelion/public/panels/panel.ts deleted file mode 100644 index 333634976eba1..0000000000000 --- a/src/plugins/timelion/public/panels/panel.ts +++ /dev/null @@ -1,34 +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'; - -interface PanelConfig { - help?: string; - render?: Function; -} - -export class Panel { - name: string; - help: string; - render: Function | undefined; - - constructor(name: string, config: PanelConfig) { - this.name = name; - this.help = config.help || ''; - this.render = config.render; - - if (!config.render) { - throw new Error( - i18n.translate('timelion.panels.noRenderFunctionErrorMessage', { - defaultMessage: 'Panel must have a rendering function', - }) - ); - } - } -} diff --git a/src/plugins/timelion/public/panels/timechart/schema.ts b/src/plugins/timelion/public/panels/timechart/schema.ts deleted file mode 100644 index dc26adc6ea5f5..0000000000000 --- a/src/plugins/timelion/public/panels/timechart/schema.ts +++ /dev/null @@ -1,386 +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 _ from 'lodash'; -import $ from 'jquery'; -import moment from 'moment-timezone'; -// @ts-ignore -import observeResize from '../../lib/observe_resize'; -import { _LEGACY_ as visTypeTimelion } from '../../../../vis_type_timelion/public'; -import { TimelionVisualizationDependencies } from '../../application'; - -const DEBOUNCE_DELAY = 50; - -export function timechartFn(dependencies: TimelionVisualizationDependencies) { - const { - $rootScope, - $compile, - uiSettings, - data: { - query: { timefilter }, - }, - } = dependencies; - - return function () { - return { - help: 'Draw a timeseries chart', - render($scope: any, $elem: any) { - const template = '
'; - const formatters = visTypeTimelion.tickFormatters() as any; - const getxAxisFormatter = visTypeTimelion.xaxisFormatterProvider(uiSettings); - const generateTicks = visTypeTimelion.generateTicksProvider(); - - // TODO: I wonder if we should supply our own moment that sets this every time? - // could just use angular's injection to provide a moment service? - moment.tz.setDefault(uiSettings.get('dateFormat:tz')); - - const render = $scope.seriesList.render || {}; - - $scope.chart = $scope.seriesList.list; - $scope.interval = $scope.interval; - $scope.search = $scope.search || _.noop; - - let legendValueNumbers: any; - let legendCaption: any; - const debouncedSetLegendNumbers = _.debounce(setLegendNumbers, DEBOUNCE_DELAY, { - maxWait: DEBOUNCE_DELAY, - leading: true, - trailing: false, - }); - // ensure legend is the same height with or without a caption so legend items do not move around - const emptyCaption = '
'; - - const defaultOptions = { - xaxis: { - mode: 'time', - tickLength: 5, - timezone: 'browser', - }, - selection: { - mode: 'x', - color: '#ccc', - }, - crosshair: { - mode: 'x', - color: '#C66', - lineWidth: 2, - }, - grid: { - show: render.grid, - borderWidth: 0, - borderColor: null, - margin: 10, - hoverable: true, - autoHighlight: false, - }, - legend: { - backgroundColor: 'rgb(255,255,255,0)', - position: 'nw', - labelBoxBorderColor: 'rgb(255,255,255,0)', - labelFormatter(label: any, series: any) { - const wrapperSpan = document.createElement('span'); - const labelSpan = document.createElement('span'); - const numberSpan = document.createElement('span'); - - wrapperSpan.setAttribute('class', 'ngLegendValue'); - wrapperSpan.setAttribute('kbn-accessible-click', ''); - wrapperSpan.setAttribute('ng-click', `toggleSeries(${series._id})`); - wrapperSpan.setAttribute('ng-focus', `focusSeries(${series._id})`); - wrapperSpan.setAttribute('ng-mouseover', `highlightSeries(${series._id})`); - - labelSpan.setAttribute('ng-non-bindable', ''); - labelSpan.appendChild(document.createTextNode(label)); - numberSpan.setAttribute('class', 'ngLegendValueNumber'); - - wrapperSpan.appendChild(labelSpan); - wrapperSpan.appendChild(numberSpan); - - return wrapperSpan.outerHTML; - }, - }, - colors: [ - '#01A4A4', - '#C66', - '#D0D102', - '#616161', - '#00A1CB', - '#32742C', - '#F18D05', - '#113F8C', - '#61AE24', - '#D70060', - ], - }; - - const originalColorMap = new Map(); - $scope.chart.forEach((series: any, seriesIndex: any) => { - if (!series.color) { - const colorIndex = seriesIndex % defaultOptions.colors.length; - series.color = defaultOptions.colors[colorIndex]; - } - originalColorMap.set(series, series.color); - }); - - let highlightedSeries: any; - let focusedSeries: any; - function unhighlightSeries() { - if (highlightedSeries === null) { - return; - } - - highlightedSeries = null; - focusedSeries = null; - $scope.chart.forEach((series: any) => { - series.color = originalColorMap.get(series); // reset the colors - }); - drawPlot($scope.chart); - } - $scope.highlightSeries = _.debounce(function (id: any) { - if (highlightedSeries === id) { - return; - } - - highlightedSeries = id; - $scope.chart.forEach((series: any, seriesIndex: any) => { - if (seriesIndex !== id) { - series.color = 'rgba(128,128,128,0.1)'; // mark as grey - } else { - series.color = originalColorMap.get(series); // color it like it was - } - }); - drawPlot($scope.chart); - }, DEBOUNCE_DELAY); - $scope.focusSeries = function (id: any) { - focusedSeries = id; - $scope.highlightSeries(id); - }; - - $scope.toggleSeries = function (id: any) { - const series = $scope.chart[id]; - series._hide = !series._hide; - drawPlot($scope.chart); - }; - - const cancelResize = observeResize($elem, function () { - drawPlot($scope.chart); - }); - - $scope.$on('$destroy', function () { - cancelResize(); - $elem.off('plothover'); - $elem.off('plotselected'); - $elem.off('mouseleave'); - }); - - $elem.on('plothover', function (event: any, pos: any, item: any) { - $rootScope.$broadcast('timelionPlotHover', event, pos, item); - }); - - $elem.on('plotselected', function (event: any, ranges: any) { - timefilter.timefilter.setTime({ - from: moment(ranges.xaxis.from), - to: moment(ranges.xaxis.to), - }); - }); - - $elem.on('mouseleave', function () { - $rootScope.$broadcast('timelionPlotLeave'); - }); - - $scope.$on('timelionPlotHover', function (angularEvent: any, flotEvent: any, pos: any) { - if (!$scope.plot) return; - $scope.plot.setCrosshair(pos); - debouncedSetLegendNumbers(pos); - }); - - $scope.$on('timelionPlotLeave', function () { - if (!$scope.plot) return; - $scope.plot.clearCrosshair(); - clearLegendNumbers(); - }); - - // Shamelessly borrowed from the flotCrosshairs example - function setLegendNumbers(pos: any) { - unhighlightSeries(); - - const plot = $scope.plot; - - const axes = plot.getAxes(); - if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max) { - return; - } - - let i; - const dataset = plot.getData(); - if (legendCaption) { - legendCaption.text( - moment(pos.x).format( - _.get(dataset, '[0]._global.legend.timeFormat', visTypeTimelion.DEFAULT_TIME_FORMAT) - ) - ); - } - for (i = 0; i < dataset.length; ++i) { - const series = dataset[i]; - const useNearestPoint = series.lines.show && !series.lines.steps; - const precision = _.get(series, '_meta.precision', 2); - - if (series._hide) continue; - - const currentPoint = series.data.find((point: any, index: number) => { - if (index + 1 === series.data.length) { - return true; - } - if (useNearestPoint) { - return pos.x - point[0] < series.data[index + 1][0] - pos.x; - } else { - return pos.x < series.data[index + 1][0]; - } - }); - - const y = currentPoint[1]; - - if (y != null) { - let label = y.toFixed(precision); - if (series.yaxis.tickFormatter) { - label = series.yaxis.tickFormatter(label, series.yaxis); - } - legendValueNumbers.eq(i).text(`(${label})`); - } else { - legendValueNumbers.eq(i).empty(); - } - } - } - - function clearLegendNumbers() { - if (legendCaption) { - legendCaption.html(emptyCaption); - } - _.each(legendValueNumbers, function (num) { - $(num).empty(); - }); - } - - let legendScope = $scope.$new(); - function drawPlot(plotConfig: any) { - if (!$('.chart-canvas', $elem).length) $elem.html(template); - const canvasElem = $('.chart-canvas', $elem); - - // we can't use `$.plot` to draw the chart when the height or width is 0 - // so, we'll need another event to trigger drawPlot to actually draw it - if (canvasElem.height() === 0 || canvasElem.width() === 0) { - return; - } - - const title = _(plotConfig).map('_title').compact().last() as any; - $('.chart-top-title', $elem).text(title == null ? '' : title); - - const options = _.cloneDeep(defaultOptions) as any; - - // Get the X-axis tick format - const time = timefilter.timefilter.getBounds() as any; - const interval = visTypeTimelion.calculateInterval( - time.min.valueOf(), - time.max.valueOf(), - uiSettings.get('timelion:target_buckets') || 200, - $scope.interval, - uiSettings.get('timelion:min_interval') || '1ms' - ); - const format = getxAxisFormatter(interval); - - // Use moment to format ticks so we get timezone correction - options.xaxis.tickFormatter = function (val: any) { - return moment(val).format(format); - }; - - // Calculate how many ticks can fit on the axis - const tickLetterWidth = 7; - const tickPadding = 45; - options.xaxis.ticks = Math.floor( - $elem.width() / (format.length * tickLetterWidth + tickPadding) - ); - - const series = _.map(plotConfig, function (serie: any, index) { - serie = _.cloneDeep( - _.defaults(serie, { - shadowSize: 0, - lines: { - lineWidth: 3, - }, - }) - ); - serie._id = index; - - if (serie.color) { - const span = document.createElement('span'); - span.style.color = serie.color; - serie.color = span.style.color; - } - - if (serie._hide) { - serie.data = []; - serie.stack = false; - // serie.color = "#ddd"; - serie.label = '(hidden) ' + serie.label; - } - - if (serie._global) { - _.mergeWith(options, serie._global, function (objVal, srcVal) { - // This is kind of gross, it means that you can't replace a global value with a null - // best you can do is an empty string. Deal with it. - if (objVal == null) return srcVal; - if (srcVal == null) return objVal; - }); - } - - return serie; - }); - - if (options.yaxes) { - options.yaxes.forEach((yaxis: any) => { - if (yaxis && yaxis.units) { - yaxis.tickFormatter = formatters[yaxis.units.type]; - const byteModes = ['bytes', 'bytes/s']; - if (byteModes.includes(yaxis.units.type)) { - yaxis.tickGenerator = generateTicks; - } - } - }); - } - - // @ts-ignore - $scope.plot = $.plot(canvasElem, _.compact(series), options); - - if ($scope.plot) { - $scope.$emit('timelionChartRendered'); - } - - legendScope.$destroy(); - legendScope = $scope.$new(); - // Used to toggle the series, and for displaying values on hover - legendValueNumbers = canvasElem.find('.ngLegendValueNumber'); - _.each(canvasElem.find('.ngLegendValue'), function (elem) { - $compile(elem)(legendScope); - }); - - if (_.get($scope.plot.getData(), '[0]._global.legend.showTime', true)) { - legendCaption = $(''); - legendCaption.html(emptyCaption); - canvasElem.find('div.legend table').append(legendCaption); - - // legend has been re-created. Apply focus on legend element when previously set - if (focusedSeries || focusedSeries === 0) { - const $legendLabels = canvasElem.find('div.legend table .legendLabel>span'); - $legendLabels.get(focusedSeries).focus(); - } - } - } - $scope.$watch('chart', drawPlot); - }, - }; - }; -} diff --git a/src/plugins/timelion/public/panels/timechart/timechart.ts b/src/plugins/timelion/public/panels/timechart/timechart.ts deleted file mode 100644 index 6af7096bcb282..0000000000000 --- a/src/plugins/timelion/public/panels/timechart/timechart.ts +++ /dev/null @@ -1,17 +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 { timechartFn } from './schema'; -import { Panel } from '../panel'; -import { TimelionVisualizationDependencies } from '../../application'; - -export function getTimeChart(dependencies: TimelionVisualizationDependencies) { - // Schema is broken out so that it may be extended for use in other plugins - // Its also easier to test. - return new Panel('timechart', timechartFn(dependencies)()); -} diff --git a/src/plugins/timelion/public/partials/load_sheet.html b/src/plugins/timelion/public/partials/load_sheet.html deleted file mode 100644 index 8d8cf7193416c..0000000000000 --- a/src/plugins/timelion/public/partials/load_sheet.html +++ /dev/null @@ -1,12 +0,0 @@ -
-

- - -
diff --git a/src/plugins/timelion/public/partials/save_sheet.html b/src/plugins/timelion/public/partials/save_sheet.html deleted file mode 100644 index 7773a9d25df71..0000000000000 --- a/src/plugins/timelion/public/partials/save_sheet.html +++ /dev/null @@ -1,107 +0,0 @@ -
- - -
-
- - - - - - - -
-
- - - -
-
-
- - {{opts.state.sheet[opts.state.selected]}} -
-
- - -
-
- -
-
-
-
diff --git a/src/plugins/timelion/public/partials/sheet_options.html b/src/plugins/timelion/public/partials/sheet_options.html deleted file mode 100644 index eae5709331659..0000000000000 --- a/src/plugins/timelion/public/partials/sheet_options.html +++ /dev/null @@ -1,36 +0,0 @@ -
-

- -
-
- - -
-
- - -
-
-
diff --git a/src/plugins/timelion/public/plugin.ts b/src/plugins/timelion/public/plugin.ts deleted file mode 100644 index 63ea9a38e2795..0000000000000 --- a/src/plugins/timelion/public/plugin.ts +++ /dev/null @@ -1,140 +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 { BehaviorSubject } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { - CoreSetup, - Plugin, - PluginInitializerContext, - DEFAULT_APP_CATEGORIES, - AppMountParameters, - AppUpdater, - ScopedHistory, - AppNavLinkStatus, -} from '../../../core/public'; -import { Panel } from './panels/panel'; -import { KibanaLegacyStart } from '../../kibana_legacy/public'; -import { createKbnUrlTracker } from '../../kibana_utils/public'; -import { DataPublicPluginStart, esFilters, DataPublicPluginSetup } from '../../data/public'; -import { NavigationPublicPluginStart } from '../../navigation/public'; -import { VisualizationsStart } from '../../visualizations/public'; -import { SavedObjectsStart } from '../../saved_objects/public'; -import { - VisTypeTimelionPluginStart, - VisTypeTimelionPluginSetup, -} from '../../vis_type_timelion/public'; - -export interface TimelionPluginSetupDependencies { - data: DataPublicPluginSetup; - visTypeTimelion: VisTypeTimelionPluginSetup; -} - -export interface TimelionPluginStartDependencies { - data: DataPublicPluginStart; - navigation: NavigationPublicPluginStart; - visualizations: VisualizationsStart; - visTypeTimelion: VisTypeTimelionPluginStart; - savedObjects: SavedObjectsStart; - kibanaLegacy: KibanaLegacyStart; -} - -/** @internal */ -export class TimelionPlugin - implements Plugin { - initializerContext: PluginInitializerContext; - private appStateUpdater = new BehaviorSubject(() => ({})); - private stopUrlTracking: (() => void) | undefined = undefined; - private currentHistory: ScopedHistory | undefined = undefined; - - constructor(initializerContext: PluginInitializerContext) { - this.initializerContext = initializerContext; - } - - public setup( - core: CoreSetup, - { - data, - visTypeTimelion, - }: { data: DataPublicPluginSetup; visTypeTimelion: VisTypeTimelionPluginSetup } - ) { - const timelionPanels: Map = new Map(); - - const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({ - baseUrl: core.http.basePath.prepend('/app/timelion'), - defaultSubUrl: '#/', - storageKey: `lastUrl:${core.http.basePath.get()}:timelion`, - navLinkUpdater$: this.appStateUpdater, - toastNotifications: core.notifications.toasts, - stateParams: [ - { - kbnUrlKey: '_g', - stateUpdate$: data.query.state$.pipe( - filter( - ({ changes }) => !!(changes.globalFilters || changes.time || changes.refreshInterval) - ), - map(({ state }) => ({ - ...state, - filters: state.filters?.filter(esFilters.isFilterPinned), - })) - ), - }, - ], - getHistory: () => this.currentHistory!, - }); - - this.stopUrlTracking = () => { - stopUrlTracker(); - }; - - core.application.register({ - id: 'timelion', - title: 'Timelion', - order: 8000, - defaultPath: '#/', - euiIconType: 'logoKibana', - category: DEFAULT_APP_CATEGORIES.kibana, - navLinkStatus: - visTypeTimelion.isUiEnabled === false ? AppNavLinkStatus.hidden : AppNavLinkStatus.default, - mount: async (params: AppMountParameters) => { - const [coreStart, pluginsStart] = await core.getStartServices(); - await pluginsStart.kibanaLegacy.loadAngularBootstrap(); - this.currentHistory = params.history; - - appMounted(); - - const unlistenParentHistory = params.history.listen(() => { - window.dispatchEvent(new HashChangeEvent('hashchange')); - }); - - const { renderApp } = await import('./application'); - params.element.classList.add('timelionAppContainer'); - const unmount = renderApp({ - mountParams: params, - pluginInitializerContext: this.initializerContext, - timelionPanels, - core: coreStart, - plugins: pluginsStart, - }); - return () => { - unlistenParentHistory(); - unmount(); - appUnMounted(); - }; - }, - }); - } - - public start() {} - - public stop(): void { - if (this.stopUrlTracking) { - this.stopUrlTracking(); - } - } -} diff --git a/src/plugins/timelion/public/services/_saved_sheet.ts b/src/plugins/timelion/public/services/_saved_sheet.ts deleted file mode 100644 index a903c70aad69b..0000000000000 --- a/src/plugins/timelion/public/services/_saved_sheet.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. - */ - -import { IUiSettingsClient } from 'kibana/public'; -import { SavedObjectsStart } from '../../../saved_objects/public'; - -// Used only by the savedSheets service, usually no reason to change this -export function createSavedSheetClass(savedObjects: SavedObjectsStart, config: IUiSettingsClient) { - class SavedSheet extends savedObjects.SavedObjectClass { - static type = 'timelion-sheet'; - - // if type:sheet has no mapping, we push this mapping into ES - static mapping = { - title: 'text', - hits: 'integer', - description: 'text', - timelion_sheet: 'text', - timelion_interval: 'keyword', - timelion_other_interval: 'keyword', - timelion_chart_height: 'integer', - timelion_columns: 'integer', - timelion_rows: 'integer', - version: 'integer', - }; - - // Order these fields to the top, the rest are alphabetical - static fieldOrder = ['title', 'description']; - // SavedSheet constructor. Usually you'd interact with an instance of this. - // ID is option, without it one will be generated on save. - constructor(id: string) { - super({ - type: SavedSheet.type, - mapping: SavedSheet.mapping, - - // if this is null/undefined then the SavedObject will be assigned the defaults - id, - - // default values that will get assigned if the doc is new - defaults: { - title: 'New TimeLion Sheet', - hits: 0, - description: '', - timelion_sheet: ['.es(*)'], - timelion_interval: 'auto', - timelion_chart_height: 275, - timelion_columns: config.get('timelion:default_columns') || 2, - timelion_rows: config.get('timelion:default_rows') || 2, - version: 1, - }, - }); - this.showInRecentlyAccessed = true; - this.getFullPath = () => `/app/timelion#/${this.id}`; - } - } - - return SavedSheet as unknown; -} diff --git a/src/plugins/timelion/public/services/saved_sheets.ts b/src/plugins/timelion/public/services/saved_sheets.ts deleted file mode 100644 index 373bb895c9806..0000000000000 --- a/src/plugins/timelion/public/services/saved_sheets.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 { SavedObjectLoader } from '../../../saved_objects/public'; -import { createSavedSheetClass } from './_saved_sheet'; -import { RenderDeps } from '../application'; - -export function initSavedSheetService(app: angular.IModule, deps: RenderDeps) { - const savedObjectsClient = deps.core.savedObjects.client; - const SavedSheet = createSavedSheetClass(deps.plugins.savedObjects, deps.core.uiSettings); - - const savedSheetLoader = new SavedObjectLoader(SavedSheet, savedObjectsClient); - savedSheetLoader.urlFor = (id) => `#/${encodeURIComponent(id)}`; - // Customize loader properties since adding an 's' on type doesn't work for type 'timelion-sheet'. - savedSheetLoader.loaderProperties = { - name: 'timelion-sheet', - noun: 'Saved Sheets', - nouns: 'saved sheets', - }; - // This is the only thing that gets injected into controllers - app.service('savedSheets', function () { - return savedSheetLoader; - }); - - return savedSheetLoader; -} diff --git a/src/plugins/timelion/public/timelion_app_state.ts b/src/plugins/timelion/public/timelion_app_state.ts deleted file mode 100644 index 348a97583d37b..0000000000000 --- a/src/plugins/timelion/public/timelion_app_state.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. - */ - -import { createStateContainer, syncState, IKbnUrlStateStorage } from '../../kibana_utils/public'; - -import { TimelionAppState, TimelionAppStateTransitions } from './types'; - -const STATE_STORAGE_KEY = '_a'; - -interface Arguments { - kbnUrlStateStorage: IKbnUrlStateStorage; - stateDefaults: TimelionAppState; -} - -export function initTimelionAppState({ stateDefaults, kbnUrlStateStorage }: Arguments) { - const urlState = kbnUrlStateStorage.get(STATE_STORAGE_KEY); - const initialState = { - ...stateDefaults, - ...urlState, - }; - - /* - make sure url ('_a') matches initial state - Initializing appState does two things - first it translates the defaults into AppState, - second it updates appState based on the url (the url trumps the defaults). This means if - we update the state format at all and want to handle BWC, we must not only migrate the - data stored with saved vis, but also any old state in the url. - */ - kbnUrlStateStorage.set(STATE_STORAGE_KEY, initialState, { replace: true }); - - const stateContainer = createStateContainer( - initialState, - { - set: (state) => (prop, value) => ({ ...state, [prop]: value }), - updateState: (state) => (newValues) => ({ ...state, ...newValues }), - } - ); - - const { start: startStateSync, stop: stopStateSync } = syncState({ - storageKey: STATE_STORAGE_KEY, - stateContainer: { - ...stateContainer, - set: (state) => { - if (state) { - // syncState utils requires to handle incoming "null" value - stateContainer.set(state); - } - }, - }, - stateStorage: kbnUrlStateStorage, - }); - - // start syncing the appState with the ('_a') url - startStateSync(); - - return { stateContainer, stopStateSync }; -} diff --git a/src/plugins/timelion/public/types.ts b/src/plugins/timelion/public/types.ts deleted file mode 100644 index bfdbd3878ec23..0000000000000 --- a/src/plugins/timelion/public/types.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. - */ - -export interface TimelionAppState { - sheet: string[]; - selected: number; - columns: number; - rows: number; - interval: string; -} - -export interface TimelionAppStateTransitions { - set: ( - state: TimelionAppState - ) => (prop: T, value: TimelionAppState[T]) => TimelionAppState; - updateState: ( - state: TimelionAppState - ) => (newValues: Partial) => TimelionAppState; -} diff --git a/src/plugins/timelion/server/config.ts b/src/plugins/timelion/server/config.ts deleted file mode 100644 index d74c4c237b2b7..0000000000000 --- a/src/plugins/timelion/server/config.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may 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, TypeOf } from '@kbn/config-schema'; - -export const configSchema = { - schema: schema.object({ - graphiteUrls: schema.maybe(schema.arrayOf(schema.string())), - enabled: schema.boolean({ defaultValue: true }), - ui: schema.object({ - enabled: schema.boolean({ defaultValue: true }), - }), - }), -}; - -export type TimelionConfigType = TypeOf; diff --git a/src/plugins/timelion/server/deprecations.ts b/src/plugins/timelion/server/deprecations.ts deleted file mode 100644 index 2358dd313b74f..0000000000000 --- a/src/plugins/timelion/server/deprecations.ts +++ /dev/null @@ -1,71 +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 { - CoreStart, - SavedObjectsClient, - Logger, - GetDeprecationsContext, - DeprecationsDetails, -} from 'src/core/server'; - -export const getTimelionSheetsCount = async ( - savedObjectsClient: Pick -) => { - const { total } = await savedObjectsClient.find({ type: 'timelion-sheet', perPage: 1 }); - return total; -}; - -export const showWarningMessageIfTimelionSheetWasFound = async ( - core: CoreStart, - logger: Logger -) => { - const { savedObjects } = core; - const savedObjectsClient = savedObjects.createInternalRepository(); - const count = await getTimelionSheetsCount(savedObjectsClient); - if (count > 0) { - logger.warn( - 'Deprecated since 7.0, the Timelion app will be removed in 7.16. To continue using your Timelion worksheets, migrate them to a dashboard. See https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html.' - ); - } -}; - -/** - * Deprecated since 7.0, the Timelion app will be removed in 8.0. - * To continue using your Timelion worksheets, migrate them to a dashboard. - * - * @link https://www.elastic.co/guide/en/kibana/master/timelion.html#timelion-deprecation - **/ -export async function getDeprecations({ - savedObjectsClient, -}: GetDeprecationsContext): Promise { - const deprecations: DeprecationsDetails[] = []; - const count = await getTimelionSheetsCount(savedObjectsClient); - - if (count > 0) { - deprecations.push({ - title: 'Found Timelion worksheets', - message: `You have ${count} Timelion worksheets. The Timelion app will be removed in 7.16. To continue using your Timelion worksheets, migrate them to a dashboard.`, - documentationUrl: - 'https://www.elastic.co/guide/en/kibana/current/create-panels-with-timelion.html', - level: 'warning', - correctiveActions: { - manualSteps: [ - 'Navigate to the Kibana Dashboard and click "Create dashboard".', - 'Select Timelion from the "New Visualization" window.', - 'Open a new tab, open the Timelion app, select the chart you want to copy, then copy the chart expression.', - 'Go to Timelion, paste the chart expression in the Timelion expression field, then click Update.', - 'In the toolbar, click Save.', - 'On the Save visualization window, enter the visualization Title, then click Save and return.', - ], - }, - }); - } - - return deprecations; -} diff --git a/src/plugins/timelion/server/index.ts b/src/plugins/timelion/server/index.ts deleted file mode 100644 index fb77df100766a..0000000000000 --- a/src/plugins/timelion/server/index.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 and the Server Side Public License, v 1; you may 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 { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server'; -import { TimelionPlugin } from './plugin'; -import { configSchema, TimelionConfigType } from './config'; - -export const config: PluginConfigDescriptor = { - schema: configSchema.schema, -}; - -export const plugin = (context: PluginInitializerContext) => - new TimelionPlugin(context); diff --git a/src/plugins/timelion/server/plugin.ts b/src/plugins/timelion/server/plugin.ts deleted file mode 100644 index edbba9b565ae4..0000000000000 --- a/src/plugins/timelion/server/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 and the Server Side Public License, v 1; you may 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, PluginInitializerContext, Logger } from 'src/core/server'; -import { i18n } from '@kbn/i18n'; -import { schema } from '@kbn/config-schema'; -import { TimelionConfigType } from './config'; -import { timelionSheetSavedObjectType } from './saved_objects'; -import { getDeprecations, showWarningMessageIfTimelionSheetWasFound } from './deprecations'; - -export class TimelionPlugin implements Plugin { - private logger: Logger; - - constructor(context: PluginInitializerContext) { - this.logger = context.logger.get(); - } - - public setup(core: CoreSetup) { - core.capabilities.registerProvider(() => ({ - timelion: { - save: true, - show: true, - }, - })); - core.savedObjects.registerType(timelionSheetSavedObjectType); - - core.uiSettings.register({ - 'timelion:showTutorial': { - name: i18n.translate('timelion.uiSettings.showTutorialLabel', { - defaultMessage: 'Show tutorial', - }), - value: false, - description: i18n.translate('timelion.uiSettings.showTutorialDescription', { - defaultMessage: 'Should I show the tutorial by default when entering the timelion app?', - }), - category: ['timelion'], - schema: schema.boolean(), - }, - 'timelion:default_columns': { - name: i18n.translate('timelion.uiSettings.defaultColumnsLabel', { - defaultMessage: 'Default columns', - }), - value: 2, - description: i18n.translate('timelion.uiSettings.defaultColumnsDescription', { - defaultMessage: 'Number of columns on a timelion sheet by default', - }), - category: ['timelion'], - schema: schema.number(), - }, - 'timelion:default_rows': { - name: i18n.translate('timelion.uiSettings.defaultRowsLabel', { - defaultMessage: 'Default rows', - }), - value: 2, - description: i18n.translate('timelion.uiSettings.defaultRowsDescription', { - defaultMessage: 'Number of rows on a timelion sheet by default', - }), - category: ['timelion'], - schema: schema.number(), - }, - }); - - core.deprecations.registerDeprecations({ getDeprecations }); - } - start(core: CoreStart) { - showWarningMessageIfTimelionSheetWasFound(core, this.logger); - } - stop() {} -} diff --git a/src/plugins/timelion/server/saved_objects/index.ts b/src/plugins/timelion/server/saved_objects/index.ts deleted file mode 100644 index 0dd070958d09a..0000000000000 --- a/src/plugins/timelion/server/saved_objects/index.ts +++ /dev/null @@ -1,9 +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 { timelionSheetSavedObjectType } from './timelion_sheet'; diff --git a/src/plugins/timelion/server/saved_objects/timelion_sheet.ts b/src/plugins/timelion/server/saved_objects/timelion_sheet.ts deleted file mode 100644 index 231e049280bb1..0000000000000 --- a/src/plugins/timelion/server/saved_objects/timelion_sheet.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 { SavedObjectsType } from 'kibana/server'; - -export const timelionSheetSavedObjectType: SavedObjectsType = { - name: 'timelion-sheet', - hidden: false, - namespaceType: 'single', - management: { - icon: 'visTimelion', - defaultSearchField: 'title', - importableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getInAppUrl(obj) { - return { - path: `/app/timelion#/${encodeURIComponent(obj.id)}`, - uiCapabilitiesPath: 'timelion.show', - }; - }, - }, - mappings: { - properties: { - description: { type: 'text' }, - hits: { type: 'integer' }, - kibanaSavedObjectMeta: { - properties: { - searchSourceJSON: { type: 'text' }, - }, - }, - timelion_chart_height: { type: 'integer' }, - timelion_columns: { type: 'integer' }, - timelion_interval: { type: 'keyword' }, - timelion_other_interval: { type: 'keyword' }, - timelion_rows: { type: 'integer' }, - timelion_sheet: { type: 'text' }, - title: { type: 'text' }, - version: { type: 'integer' }, - }, - }, -}; diff --git a/src/plugins/timelion/tsconfig.json b/src/plugins/timelion/tsconfig.json deleted file mode 100644 index 594901c3cc1ed..0000000000000 --- a/src/plugins/timelion/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "public/**/*", - "server/**/*" - ], - "references": [ - { "path": "../../core/tsconfig.json" }, - { "path": "../data/tsconfig.json" }, - { "path": "../visualizations/tsconfig.json" }, - { "path": "../navigation/tsconfig.json" }, - { "path": "../vis_type_timelion/tsconfig.json" }, - { "path": "../saved_objects/tsconfig.json" }, - { "path": "../kibana_legacy/tsconfig.json" }, - ] -} diff --git a/src/plugins/vis_type_timelion/config.ts b/src/plugins/vis_type_timelion/config.ts index aa88d786af51e..cfd3d13c277e9 100644 --- a/src/plugins/vis_type_timelion/config.ts +++ b/src/plugins/vis_type_timelion/config.ts @@ -8,14 +8,9 @@ import { schema, TypeOf } from '@kbn/config-schema'; -export const configSchema = schema.object( - { - enabled: schema.boolean({ defaultValue: true }), - ui: schema.object({ enabled: schema.boolean({ defaultValue: false }) }), - graphiteUrls: schema.maybe(schema.arrayOf(schema.string())), - }, - // This option should be removed as soon as we entirely migrate config from legacy Timelion plugin. - { unknowns: 'allow' } -); +export const configSchema = schema.object({ + graphiteUrls: schema.maybe(schema.arrayOf(schema.string())), + enabled: schema.boolean({ defaultValue: true }), +}); export type ConfigSchema = TypeOf; diff --git a/src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx b/src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx index d518d9718d5e7..569ddf03c941b 100644 --- a/src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx +++ b/src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx @@ -96,7 +96,7 @@ function TimelionExpressionInput({ value, setValue }: TimelionExpressionInputPro
-
+
getTimelionVisualizationConfig(dependencies)); expressions.registerRenderer(getTimelionVisRenderer(dependencies)); visualizations.createBaseVisualization(getTimelionVisDefinition(dependencies)); - - return { - isUiEnabled: this.initializerContext.config.get().ui.enabled, - }; } public start(core: CoreStart, { data, charts }: TimelionVisStartDependencies) { diff --git a/src/plugins/vis_type_timelion/server/index.ts b/src/plugins/vis_type_timelion/server/index.ts index 35f4182a50a86..5c5cf8b481f94 100644 --- a/src/plugins/vis_type_timelion/server/index.ts +++ b/src/plugins/vis_type_timelion/server/index.ts @@ -10,19 +10,9 @@ import { PluginConfigDescriptor, PluginInitializerContext } from '../../../../sr import { configSchema, ConfigSchema } from '../config'; import { TimelionPlugin } from './plugin'; -export { PluginSetupContract } from './plugin'; - export const config: PluginConfigDescriptor = { schema: configSchema, - exposeToBrowser: { - ui: true, - }, - deprecations: ({ renameFromRoot }) => [ - renameFromRoot('timelion_vis.enabled', 'vis_type_timelion.enabled'), - renameFromRoot('timelion.enabled', 'vis_type_timelion.enabled'), - renameFromRoot('timelion.graphiteUrls', 'vis_type_timelion.graphiteUrls'), - renameFromRoot('timelion.ui.enabled', 'vis_type_timelion.ui.enabled', { silent: true }), - ], }; + export const plugin = (initializerContext: PluginInitializerContext) => new TimelionPlugin(initializerContext); diff --git a/src/plugins/vis_type_timelion/server/plugin.ts b/src/plugins/vis_type_timelion/server/plugin.ts index fc23569b351e6..5bbb5dd1819c4 100644 --- a/src/plugins/vis_type_timelion/server/plugin.ts +++ b/src/plugins/vis_type_timelion/server/plugin.ts @@ -8,8 +8,6 @@ import { i18n } from '@kbn/i18n'; import { TypeOf } from '@kbn/config-schema'; -import { RecursiveReadonly } from '@kbn/utility-types'; -import { deepFreeze } from '@kbn/std'; import type { PluginStart, DataRequestHandlerContext } from '../../../../src/plugins/data/server'; import { CoreSetup, PluginInitializerContext, Plugin } from '../../../../src/core/server'; @@ -21,13 +19,6 @@ import { runRoute } from './routes/run'; import { ConfigManager } from './lib/config_manager'; import { getUiSettings } from './ui_settings'; -/** - * Describes public Timelion plugin contract returned at the `setup` stage. - */ -export interface PluginSetupContract { - uiEnabled: boolean; -} - export interface TimelionPluginStartDeps { data: PluginStart; } @@ -35,11 +26,10 @@ export interface TimelionPluginStartDeps { /** * Represents Timelion Plugin instance that will be managed by the Kibana plugin system. */ -export class TimelionPlugin - implements Plugin, void, TimelionPluginStartDeps> { +export class TimelionPlugin implements Plugin { constructor(private readonly initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup): RecursiveReadonly { + public setup(core: CoreSetup): void { const config = this.initializerContext.config.get>(); const configManager = new ConfigManager(this.initializerContext.config); @@ -76,8 +66,6 @@ export class TimelionPlugin validateEsRoute(router); core.uiSettings.register(getUiSettings(config)); - - return deepFreeze({ uiEnabled: config.ui.enabled }); } public start() { diff --git a/src/plugins/vis_type_timeseries/common/agg_utils.test.ts b/src/plugins/vis_type_timeseries/common/agg_utils.test.ts index 3e450c789b65d..63d81e2c43d40 100644 --- a/src/plugins/vis_type_timeseries/common/agg_utils.test.ts +++ b/src/plugins/vis_type_timeseries/common/agg_utils.test.ts @@ -60,6 +60,7 @@ describe('agg utils', () => { isFieldRequired: true, isFilterRatioSupported: false, isHistogramSupported: false, + isFieldFormattingDisabled: false, hasExtendedStats: true, }; const expected = [ @@ -95,6 +96,7 @@ describe('agg utils', () => { isFieldRequired: false, isFilterRatioSupported: false, isHistogramSupported: false, + isFieldFormattingDisabled: false, hasExtendedStats: false, }; const expected = [ diff --git a/src/plugins/vis_type_timeseries/common/agg_utils.ts b/src/plugins/vis_type_timeseries/common/agg_utils.ts index 8b071cc680af3..2f0488bdc4dbe 100644 --- a/src/plugins/vis_type_timeseries/common/agg_utils.ts +++ b/src/plugins/vis_type_timeseries/common/agg_utils.ts @@ -28,6 +28,7 @@ export interface Agg { isFieldRequired: boolean; isFilterRatioSupported: boolean; isHistogramSupported: boolean; + isFieldFormattingDisabled: boolean; hasExtendedStats: boolean; }; } @@ -37,6 +38,7 @@ const aggDefaultMeta = { isFieldRequired: true, isFilterRatioSupported: false, isHistogramSupported: false, + isFieldFormattingDisabled: false, hasExtendedStats: false, }; @@ -201,6 +203,7 @@ export const aggs: Agg[] = [ id: TSVB_METRIC_TYPES.CALCULATION, meta: { ...aggDefaultMeta, + isFieldFormattingDisabled: true, type: AGG_TYPE.PARENT_PIPELINE, label: i18n.translate('visTypeTimeseries.aggUtils.bucketScriptLabel', { defaultMessage: 'Bucket Script', @@ -342,6 +345,7 @@ export const aggs: Agg[] = [ id: TSVB_METRIC_TYPES.MATH, meta: { ...aggDefaultMeta, + isFieldFormattingDisabled: true, type: AGG_TYPE.SPECIAL, label: i18n.translate('visTypeTimeseries.aggUtils.mathLabel', { defaultMessage: 'Math' }), }, diff --git a/src/plugins/vis_type_timeseries/common/calculate_label.ts b/src/plugins/vis_type_timeseries/common/calculate_label.ts index 7ea035eef9234..d054698536b5b 100644 --- a/src/plugins/vis_type_timeseries/common/calculate_label.ts +++ b/src/plugins/vis_type_timeseries/common/calculate_label.ts @@ -82,7 +82,7 @@ export const calculateLabel = ( if (includes(paths, metric.type)) { const targetMetric = metrics.find((m) => startsWith(metric.field!, m.id)); - const targetLabel = calculateLabel(targetMetric!, metrics, fields); + const targetLabel = calculateLabel(targetMetric!, metrics, fields, isThrowErrorOnFieldNotFound); // For percentiles we need to parse the field id to extract the percentile // the user configured in the percentile aggregation and specified in the diff --git a/src/plugins/vis_type_timeseries/common/enums/index.ts b/src/plugins/vis_type_timeseries/common/enums/index.ts index 506abeea247c9..8a4d9a21f09a1 100644 --- a/src/plugins/vis_type_timeseries/common/enums/index.ts +++ b/src/plugins/vis_type_timeseries/common/enums/index.ts @@ -20,3 +20,12 @@ export enum TOOLTIP_MODES { SHOW_ALL = 'show_all', SHOW_FOCUSED = 'show_focused', } + +export enum DATA_FORMATTERS { + BYTES = 'bytes', + CUSTOM = 'custom', + DEFAULT = 'default', + DURATION = 'duration', + NUMBER = 'number', + PERCENT = 'percent', +} diff --git a/src/plugins/vis_type_timeseries/common/types/vis_data.ts b/src/plugins/vis_type_timeseries/common/types/vis_data.ts index fb3e0db82f188..1a7be0b467004 100644 --- a/src/plugins/vis_type_timeseries/common/types/vis_data.ts +++ b/src/plugins/vis_type_timeseries/common/types/vis_data.ts @@ -39,6 +39,7 @@ export interface PanelSeries { export interface PanelData { id: string; label: string; + labelFormatted?: string; data: PanelDataArray[]; seriesId: string; splitByLabel: string; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx index 17af812ae5ce3..3c68cb02dd07e 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/agg.tsx @@ -6,33 +6,39 @@ * Side Public License, v 1. */ -import React, { HTMLAttributes } from 'react'; +import React, { useMemo, useEffect, HTMLAttributes } from 'react'; // @ts-ignore import { aggToComponent } from '../lib/agg_to_component'; // @ts-ignore import { isMetricEnabled } from '../../lib/check_ui_restrictions'; +// @ts-expect-error not typed yet +import { seriesChangeHandler } from '../lib/series_change_handler'; +import { checkIfNumericMetric } from '../lib/check_if_numeric_metric'; +import { getFormatterType } from '../lib/get_formatter_type'; import { UnsupportedAgg } from './unsupported_agg'; import { TemporaryUnsupportedAgg } from './temporary_unsupported_agg'; +import { DATA_FORMATTERS } from '../../../../common/enums'; import type { Metric, Panel, Series, SanitizedFieldType } from '../../../../common/types'; -import { DragHandleProps } from '../../../types'; -import { TimeseriesUIRestrictions } from '../../../../common/ui_restrictions'; +import type { DragHandleProps } from '../../../types'; +import type { TimeseriesUIRestrictions } from '../../../../common/ui_restrictions'; interface AggProps extends HTMLAttributes { disableDelete: boolean; fields: Record; + name: string; model: Metric; panel: Panel; series: Series; siblings: Metric[]; uiRestrictions: TimeseriesUIRestrictions; dragHandleProps: DragHandleProps; + onChange: (part: Partial) => void; onAdd: () => void; - onChange: () => void; onDelete: () => void; } export function Agg(props: AggProps) { - const { model, uiRestrictions } = props; + const { model, uiRestrictions, series, name, onChange, fields, siblings } = props; let Component = aggToComponent[model.type]; @@ -50,6 +56,34 @@ export function Agg(props: AggProps) { const indexPattern = props.series.override_index_pattern ? props.series.series_index_pattern : props.panel.index_pattern; + const isKibanaIndexPattern = props.panel.use_kibana_indexes || indexPattern === ''; + + const onAggChange = useMemo( + () => seriesChangeHandler({ name, model: series, onChange }, siblings), + [name, onChange, siblings, series] + ); + + useEffect(() => { + // formatter is based on the last agg, i.e. active or resulting one as pipeline + if (siblings[siblings.length - 1]?.id === model.id) { + const formatterType = getFormatterType(series.formatter); + const isNumericMetric = checkIfNumericMetric(model, fields, indexPattern); + const isNumberFormatter = ![DATA_FORMATTERS.DEFAULT, DATA_FORMATTERS.CUSTOM].includes( + formatterType + ); + + if (isNumberFormatter && !isNumericMetric) { + onChange({ formatter: DATA_FORMATTERS.DEFAULT }); + } + // in case of string index pattern mode, change default formatter depending on metric type + // "number" formatter for numeric metric and "" as custom formatter for any other type + if (formatterType === DATA_FORMATTERS.DEFAULT && !isKibanaIndexPattern) { + onChange({ + formatter: isNumericMetric ? DATA_FORMATTERS.NUMBER : '', + }); + } + } + }, [indexPattern, model, onChange, fields, series.formatter, isKibanaIndexPattern, siblings]); return (
@@ -58,7 +92,7 @@ export function Agg(props: AggProps) { disableDelete={props.disableDelete} model={props.model} onAdd={props.onAdd} - onChange={props.onChange} + onChange={onAggChange} onDelete={props.onDelete} panel={props.panel} series={props.series} diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/aggs.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/aggs.tsx index 0edd8b9c3feb5..516e3551fb010 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/aggs.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/aggs.tsx @@ -12,7 +12,6 @@ import { EuiDraggable, EuiDroppable } from '@elastic/eui'; import { Agg } from './agg'; // @ts-ignore -import { seriesChangeHandler } from '../lib/series_change_handler'; import { handleAdd, handleDelete } from '../lib/collection_actions'; import { newMetricAggFn } from '../lib/new_metric_agg_fn'; import type { Panel, Series, SanitizedFieldType } from '../../../../common/types'; @@ -26,16 +25,14 @@ export interface AggsProps { model: Series; fields: Record; uiRestrictions: TimeseriesUIRestrictions; - onChange(): void; + onChange(part: Partial): void; } export class Aggs extends PureComponent { render() { - const { panel, model, fields, uiRestrictions } = this.props; + const { panel, model, fields, name, uiRestrictions, onChange } = this.props; const list = model.metrics; - const onChange = seriesChangeHandler(this.props, list); - return ( {list.map((row, idx) => ( @@ -51,6 +48,7 @@ export class Aggs extends PureComponent { key={row.id} disableDelete={list.length < 2} fields={fields} + name={name} model={row} onAdd={() => handleAdd(this.props, newMetricAggFn)} onChange={onChange} diff --git a/src/plugins/vis_type_timeseries/public/application/components/data_format_picker.js b/src/plugins/vis_type_timeseries/public/application/components/data_format_picker.js deleted file mode 100644 index 12428b7fbe6a0..0000000000000 --- a/src/plugins/vis_type_timeseries/public/application/components/data_format_picker.js +++ /dev/null @@ -1,298 +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 PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import _ from 'lodash'; -import { - htmlIdGenerator, - EuiComboBox, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiFieldText, - EuiLink, -} from '@elastic/eui'; -import { durationOutputOptions, durationInputOptions, isDuration } from './lib/durations'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; - -const DEFAULT_OUTPUT_PRECISION = '2'; - -class DataFormatPickerUI extends Component { - constructor(props) { - super(props); - - let from; - let to; - let decimals; - - if (isDuration(props.value)) { - [from, to, decimals] = props.value.split(','); - } - - this.state = { - from: from || 'ms', - to: to || 'ms', - decimals: decimals || '', - }; - } - - handleCustomChange = () => { - this.props.onChange([{ value: (this.custom && this.custom.value) || '' }]); - }; - - handleChange = (selectedOptions) => { - if (selectedOptions.length < 1) { - return; - } - - if (selectedOptions[0].value === 'custom') { - this.handleCustomChange(); - } else if (selectedOptions[0].value === 'duration') { - const { from, to, decimals } = this.state; - this.props.onChange([ - { - value: `${from},${to},${decimals}`, - }, - ]); - } else { - this.props.onChange(selectedOptions); - } - }; - - handleDurationChange(name) { - return (selectedOptions) => { - if (selectedOptions.length < 1) { - return; - } - - let newValue; - if (name === 'decimals') { - newValue = this.decimals.value; - } else { - newValue = selectedOptions[0].value; - } - - this.setState( - { - [name]: newValue, - }, - () => { - const { from, to, decimals } = this.state; - this.props.onChange([ - { - value: `${from},${to},${decimals}`, - }, - ]); - } - ); - }; - } - - render() { - const htmlId = htmlIdGenerator(); - const value = this.props.value || ''; - let defaultValue = value; - if (!_.includes(['bytes', 'number', 'percent'], value)) { - defaultValue = 'custom'; - } - if (isDuration(value)) { - defaultValue = 'duration'; - } - const { intl } = this.props; - const options = [ - { - label: intl.formatMessage({ - id: 'visTypeTimeseries.dataFormatPicker.bytesLabel', - defaultMessage: 'Bytes', - }), - value: 'bytes', - }, - { - label: intl.formatMessage({ - id: 'visTypeTimeseries.dataFormatPicker.numberLabel', - defaultMessage: 'Number', - }), - value: 'number', - }, - { - label: intl.formatMessage({ - id: 'visTypeTimeseries.dataFormatPicker.percentLabel', - defaultMessage: 'Percent', - }), - value: 'percent', - }, - { - label: intl.formatMessage({ - id: 'visTypeTimeseries.dataFormatPicker.durationLabel', - defaultMessage: 'Duration', - }), - value: 'duration', - }, - { - label: intl.formatMessage({ - id: 'visTypeTimeseries.dataFormatPicker.customLabel', - defaultMessage: 'Custom', - }), - value: 'custom', - }, - ]; - const selectedOption = options.find((option) => { - return defaultValue === option.value; - }); - - let custom; - if (defaultValue === 'duration') { - const [from, to, decimals] = value.split(','); - const selectedFrom = durationInputOptions.find((option) => from === option.value); - const selectedTo = durationOutputOptions.find((option) => to === option.value); - - return ( - - - - - - - - - } - > - - - - - - } - > - - - - - {selectedTo && selectedTo.value !== 'humanize' && ( - - - } - > - (this.decimals = el)} - placeholder={DEFAULT_OUTPUT_PRECISION} - onChange={this.handleDurationChange('decimals')} - /> - - - )} - - ); - } - if (defaultValue === 'custom') { - custom = ( - - - } - helpText={ - - - Numeral.js - - ), - }} - /> - - } - > - (this.custom = el)} - onChange={this.handleCustomChange} - /> - - - ); - } - return ( - - - - - - - {custom} - - ); - } -} - -DataFormatPickerUI.defaultProps = { - label: i18n.translate('visTypeTimeseries.defaultDataFormatterLabel', { - defaultMessage: 'Data Formatter', - }), -}; - -DataFormatPickerUI.propTypes = { - value: PropTypes.string, - label: PropTypes.string, - onChange: PropTypes.func, -}; - -export const DataFormatPicker = injectI18n(DataFormatPickerUI); diff --git a/src/plugins/vis_type_timeseries/public/application/components/data_format_picker.tsx b/src/plugins/vis_type_timeseries/public/application/components/data_format_picker.tsx new file mode 100644 index 0000000000000..fa76f8534f852 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/data_format_picker.tsx @@ -0,0 +1,310 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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, { useEffect, useMemo, useCallback, useState, ChangeEvent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { + htmlIdGenerator, + EuiComboBox, + EuiFieldText, + EuiFlexItem, + EuiFormRow, + EuiLink, + EuiSuperSelect, + EuiText, + EuiCode, +} from '@elastic/eui'; +import { DATA_FORMATTERS } from '../../../common/enums'; +import { getFormatterType } from './lib/get_formatter_type'; +import { durationInputOptions, durationOutputOptions, getDurationParams } from './lib/durations'; + +const DEFAULT_OUTPUT_PRECISION = '2'; +const DEFAULT_CUSTOM_FORMAT_PATTERN = '0,0.[000]'; + +const defaultOptionLabel = i18n.translate('visTypeTimeseries.dataFormatPicker.defaultLabel', { + defaultMessage: 'Default', +}); + +const getDataFormatPickerOptions = ( + shouldIncludeDefaultOption: boolean, + shouldIncludeNumberOptions: boolean +) => { + const additionalOptions = []; + + if (shouldIncludeDefaultOption) { + additionalOptions.push({ + value: DATA_FORMATTERS.DEFAULT, + inputDisplay: defaultOptionLabel, + dropdownDisplay: ( + <> + {defaultOptionLabel} + +

+ {i18n.translate('visTypeTimeseries.dataFormatPicker.defaultLabelDescription', { + defaultMessage: 'Applies common formatting', + })} +

+
+ + ), + 'data-test-subj': `tsvbDataFormatPicker-${DATA_FORMATTERS.DEFAULT}`, + }); + } + + if (shouldIncludeNumberOptions) { + additionalOptions.push( + { + value: DATA_FORMATTERS.NUMBER, + inputDisplay: i18n.translate('visTypeTimeseries.dataFormatPicker.numberLabel', { + defaultMessage: 'Number', + }), + 'data-test-subj': `tsvbDataFormatPicker-${DATA_FORMATTERS.NUMBER}`, + }, + { + value: DATA_FORMATTERS.BYTES, + inputDisplay: i18n.translate('visTypeTimeseries.dataFormatPicker.bytesLabel', { + defaultMessage: 'Bytes', + }), + 'data-test-subj': `tsvbDataFormatPicker-${DATA_FORMATTERS.BYTES}`, + }, + { + value: DATA_FORMATTERS.PERCENT, + inputDisplay: i18n.translate('visTypeTimeseries.dataFormatPicker.percentLabel', { + defaultMessage: 'Percent', + }), + 'data-test-subj': `tsvbDataFormatPicker-${DATA_FORMATTERS.PERCENT}`, + }, + { + value: DATA_FORMATTERS.DURATION, + inputDisplay: i18n.translate('visTypeTimeseries.dataFormatPicker.durationLabel', { + defaultMessage: 'Duration', + }), + 'data-test-subj': `tsvbDataFormatPicker-${DATA_FORMATTERS.DURATION}`, + } + ); + } + + return [ + ...additionalOptions, + { + value: DATA_FORMATTERS.CUSTOM, + inputDisplay: i18n.translate('visTypeTimeseries.dataFormatPicker.customLabel', { + defaultMessage: 'Custom', + }), + 'data-test-subj': `tsvbDataFormatPicker-${DATA_FORMATTERS.CUSTOM}`, + }, + ]; +}; + +interface DataFormatPickerProps { + formatterValue: string; + changeModelFormatter: (formatter: string) => void; + shouldIncludeDefaultOption: boolean; + shouldIncludeNumberOptions: boolean; +} + +const htmlId = htmlIdGenerator(); + +export const DataFormatPicker = ({ + formatterValue, + changeModelFormatter, + shouldIncludeDefaultOption, + shouldIncludeNumberOptions, +}: DataFormatPickerProps) => { + const options = useMemo( + () => getDataFormatPickerOptions(shouldIncludeDefaultOption, shouldIncludeNumberOptions), + [shouldIncludeDefaultOption, shouldIncludeNumberOptions] + ); + const [selectedFormatter, setSelectedFormatter] = useState(getFormatterType(formatterValue)); + const [customFormatPattern, setCustomFormatPattern] = useState( + selectedFormatter === DATA_FORMATTERS.CUSTOM ? formatterValue : '' + ); + const [durationParams, setDurationParams] = useState( + getDurationParams(selectedFormatter === DATA_FORMATTERS.DURATION ? formatterValue : 'ms,ms,') + ); + + useEffect(() => { + // formatter value is set to the first option in case options do not include selected formatter + if (!options.find(({ value }) => value === selectedFormatter)) { + const [{ value: firstOptionValue }] = options; + setSelectedFormatter(firstOptionValue); + changeModelFormatter(firstOptionValue); + } + }, [options, selectedFormatter, changeModelFormatter]); + + const handleChange = useCallback( + (selectedOption: DATA_FORMATTERS) => { + setSelectedFormatter(selectedOption); + if (selectedOption === DATA_FORMATTERS.DURATION) { + const { from, to, decimals } = durationParams; + changeModelFormatter(`${from},${to},${decimals}`); + } else if (selectedOption === DATA_FORMATTERS.CUSTOM) { + changeModelFormatter(customFormatPattern); + } else { + changeModelFormatter(selectedOption); + } + }, + [changeModelFormatter, customFormatPattern, durationParams] + ); + + const handleCustomFormatStringChange = useCallback( + (event: ChangeEvent) => { + const stringPattern = event.target.value; + changeModelFormatter(stringPattern); + setCustomFormatPattern(stringPattern); + }, + [changeModelFormatter] + ); + + const handleDurationParamsChange = useCallback( + (paramName: string, paramValue: string) => { + const newDurationParams = { ...durationParams, [paramName]: paramValue }; + setDurationParams(newDurationParams); + const { from, to, decimals } = newDurationParams; + changeModelFormatter(`${from},${to},${decimals}`); + }, + [changeModelFormatter, durationParams] + ); + + const handleDurationChange = useCallback( + (optionName: 'from' | 'to') => { + return ([{ value }]: Array>) => + handleDurationParamsChange(optionName, value!); + }, + [handleDurationParamsChange] + ); + + const handleDecimalsChange = useCallback( + (event: ChangeEvent) => + handleDurationParamsChange('decimals', event.target.value), + [handleDurationParamsChange] + ); + + let duration; + if (selectedFormatter === DATA_FORMATTERS.DURATION) { + const { from, to, decimals = DEFAULT_OUTPUT_PRECISION } = durationParams; + const selectedFrom = durationInputOptions.find(({ value }) => value === from); + const selectedTo = durationOutputOptions.find(({ value }) => value === to); + + duration = ( + <> + + + + + + + + + + + + {selectedTo?.value !== 'humanize' && ( + + + + + + )} + + ); + } + + let custom; + if (selectedFormatter === DATA_FORMATTERS.CUSTOM && shouldIncludeNumberOptions) { + custom = ( + + {DEFAULT_CUSTOM_FORMAT_PATTERN} }} + /> + } + helpText={ + + + + + + } + > + + + + ); + } + + return ( + <> + + + + + + {selectedFormatter === DATA_FORMATTERS.DURATION && duration} + {selectedFormatter === DATA_FORMATTERS.CUSTOM && custom} + + ); +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.test.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.test.ts new file mode 100644 index 0000000000000..17827275f86d8 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.test.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { METRIC_TYPES } from '../../../../../data/common'; +import { TSVB_METRIC_TYPES } from '../../../../common/enums'; +import { checkIfNumericMetric } from './check_if_numeric_metric'; + +import type { Metric } from '../../../../common/types'; + +describe('checkIfNumericMetric(metric, fields, indexPattern)', () => { + const indexPattern = { id: 'some_id' }; + const fields = { + some_id: [ + { name: 'number field', type: 'number' }, + { name: 'string field', type: 'string' }, + { name: 'date field', type: 'date' }, + ], + }; + + it('should return true for Count metric', () => { + const metric = { type: METRIC_TYPES.COUNT } as Metric; + + const actual = checkIfNumericMetric(metric, fields, indexPattern); + expect(actual).toBe(true); + }); + + it('should return true for Average metric', () => { + const metric = { field: 'number field', type: METRIC_TYPES.AVG } as Metric; + + const actual = checkIfNumericMetric(metric, fields, indexPattern); + expect(actual).toBe(true); + }); + + it('should return true for Top Hit metric with numeric field', () => { + const metric = { field: 'number field', type: TSVB_METRIC_TYPES.TOP_HIT } as Metric; + + const actual = checkIfNumericMetric(metric, fields, indexPattern); + expect(actual).toBe(true); + }); + + it('should return false for Top Hit metric with string field', () => { + const metric = { field: 'string field', type: TSVB_METRIC_TYPES.TOP_HIT } as Metric; + + const actual = checkIfNumericMetric(metric, fields, indexPattern); + expect(actual).toBe(false); + }); + + it('should return false for Top Hit metric with date field', () => { + const metric = { field: 'date field', type: TSVB_METRIC_TYPES.TOP_HIT } as Metric; + + const actual = checkIfNumericMetric(metric, fields, indexPattern); + expect(actual).toBe(false); + }); +}); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.ts new file mode 100644 index 0000000000000..a70abaeac9f82 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_numeric_metric.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { getIndexPatternKey } from '../../../../common/index_patterns_utils'; +import { TSVB_METRIC_TYPES } from '../../../../common/enums'; +import { KBN_FIELD_TYPES } from '../../../../../data/public'; + +import type { Metric, IndexPatternValue } from '../../../../common/types'; +import type { VisFields } from '../../lib/fetch_fields'; + +// this function checks if metric has numeric value result +export const checkIfNumericMetric = ( + metric: Metric, + fields: VisFields, + indexPattern: IndexPatternValue +) => { + // currently only Top Hit could have not numeric value result + if (metric?.type === TSVB_METRIC_TYPES.TOP_HIT) { + const selectedField = fields[getIndexPatternKey(indexPattern)]?.find( + ({ name }) => name === metric?.field + ); + return selectedField?.type === KBN_FIELD_TYPES.NUMBER; + } + return true; +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts new file mode 100644 index 0000000000000..71aed8c7315e2 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.test.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { checkIfSeriesHaveSameFormatters } from './check_if_series_have_same_formatters'; +import { DATA_FORMATTERS } from '../../../../common/enums'; +import type { Series } from '../../../../common/types'; + +describe('checkIfSeriesHaveSameFormatters(seriesModel, fieldFormatMap)', () => { + const fieldFormatMap = { + someField: { id: 'string', params: { transform: 'upper' } }, + anotherField: { id: 'number', params: { pattern: '$0,0.[00]' } }, + }; + + it('should return true for the same series formatters', () => { + const seriesModel = [ + { formatter: DATA_FORMATTERS.BYTES, metrics: [{ field: 'someField' }] }, + { formatter: DATA_FORMATTERS.BYTES, metrics: [{ field: 'anotherField' }] }, + ] as Series[]; + const result = checkIfSeriesHaveSameFormatters(seriesModel, fieldFormatMap); + + expect(result).toBe(true); + }); + + it('should return false for the different value_template series formatters', () => { + const seriesModel = [ + { + formatter: DATA_FORMATTERS.PERCENT, + value_template: '{{value}} first', + }, + { + formatter: DATA_FORMATTERS.PERCENT, + value_template: '{{value}} second', + }, + ] as Series[]; + const result = checkIfSeriesHaveSameFormatters(seriesModel, fieldFormatMap); + + expect(result).toBe(false); + }); + + it('should return true for the same field formatters', () => { + const seriesModel = [ + { formatter: DATA_FORMATTERS.DEFAULT, metrics: [{ field: 'someField' }] }, + { formatter: DATA_FORMATTERS.DEFAULT, metrics: [{ field: 'someField' }] }, + ] as Series[]; + const result = checkIfSeriesHaveSameFormatters(seriesModel, fieldFormatMap); + + expect(result).toBe(true); + }); + + it('should return false for the different field formatters', () => { + const seriesModel = [ + { formatter: DATA_FORMATTERS.DEFAULT, metrics: [{ field: 'someField' }] }, + { + formatter: DATA_FORMATTERS.DEFAULT, + + metrics: [{ field: 'anotherField' }], + }, + ] as Series[]; + const result = checkIfSeriesHaveSameFormatters(seriesModel, fieldFormatMap); + + expect(result).toBe(false); + }); + + it('should return false for when there is no custom formatter for a field', () => { + const seriesModel = [ + { + formatter: DATA_FORMATTERS.DEFAULT, + + metrics: [{ field: 'someField' }, { field: 'field' }], + }, + { formatter: DATA_FORMATTERS.DEFAULT, metrics: [{ field: 'someField' }] }, + ] as Series[]; + const result = checkIfSeriesHaveSameFormatters(seriesModel, fieldFormatMap); + + expect(result).toBe(false); + }); +}); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.ts new file mode 100644 index 0000000000000..afa1216406ab0 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/check_if_series_have_same_formatters.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 { last, isEqual } from 'lodash'; +import { DATA_FORMATTERS } from '../../../../common/enums'; +import type { Series } from '../../../../common/types'; +import type { FieldFormatMap } from '../../../../../data/common'; + +export const checkIfSeriesHaveSameFormatters = ( + seriesModel: Series[], + fieldFormatMap?: FieldFormatMap +) => { + const allSeriesHaveDefaultFormatting = seriesModel.every( + (seriesGroup) => seriesGroup.formatter === DATA_FORMATTERS.DEFAULT + ); + + return allSeriesHaveDefaultFormatting && fieldFormatMap + ? seriesModel + .map(({ metrics }) => fieldFormatMap[last(metrics)?.field ?? '']) + .every((fieldFormat, index, [firstSeriesFieldFormat]) => + isEqual(fieldFormat, firstSeriesFieldFormat) + ) + : seriesModel.every( + (series) => + series.formatter === seriesModel[0].formatter && + series.value_template === seriesModel[0].value_template + ); +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js b/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js index 816bce5dac75b..867ba673cf1dd 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/convert_series_to_vars.js @@ -7,32 +7,35 @@ */ import { set } from '@elastic/safer-lodash-set'; -import _ from 'lodash'; +import { startsWith, snakeCase } from 'lodash'; +import { BUCKET_TYPES, DATA_FORMATTERS } from '../../../../common/enums'; import { getLastValue } from '../../../../common/last_value_utils'; import { getValueOrEmpty, emptyLabel } from '../../../../common/empty_label'; import { createTickFormatter } from './tick_formatter'; +import { getMetricsField } from './get_metrics_field'; +import { createFieldFormatter } from './create_field_formatter'; import { labelDateFormatter } from './label_date_formatter'; import moment from 'moment'; -export const convertSeriesToVars = (series, model, dateFormat = 'lll', getConfig = null) => { +export const convertSeriesToVars = (series, model, getConfig = null, fieldFormatMap) => { const variables = {}; + const dateFormat = getConfig?.('dateFormat') ?? 'lll'; model.series.forEach((seriesModel) => { series - .filter((row) => _.startsWith(row.id, seriesModel.id)) + .filter((row) => startsWith(row.id, seriesModel.id)) .forEach((row) => { let label = getValueOrEmpty(row.label); if (label !== emptyLabel) { - label = _.snakeCase(label); + label = snakeCase(label); } - const varName = [label, _.snakeCase(seriesModel.var_name)].filter((v) => v).join('.'); + const varName = [label, snakeCase(seriesModel.var_name)].filter((v) => v).join('.'); - const formatter = createTickFormatter( - seriesModel.formatter, - seriesModel.value_template, - getConfig - ); + const formatter = + seriesModel.formatter === DATA_FORMATTERS.DEFAULT + ? createFieldFormatter(getMetricsField(seriesModel.metrics), fieldFormatMap) + : createTickFormatter(seriesModel.formatter, seriesModel.value_template, getConfig); const lastValue = getLastValue(row.data); const data = { @@ -47,8 +50,12 @@ export const convertSeriesToVars = (series, model, dateFormat = 'lll', getConfig }), }, }; + const rowLabel = + seriesModel.split_mode === BUCKET_TYPES.TERMS + ? createFieldFormatter(seriesModel.terms_field, fieldFormatMap)(row.label) + : row.label; set(variables, varName, data); - set(variables, `${label}.label`, row.label); + set(variables, `${label}.label`, rowLabel); /** * Handle the case when a field has "key_as_string" value. diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.test.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.test.ts new file mode 100644 index 0000000000000..0173ca4db15ae --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.test.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { createFieldFormatter } from './create_field_formatter'; +import { getFieldFormatsRegistry } from '../../../../../data/public/test_utils'; +import { setFieldFormats } from '../../../services'; +import { FORMATS_UI_SETTINGS } from 'src/plugins/field_formats/common'; +import type { CoreSetup } from 'kibana/public'; + +const mockUiSettings = ({ + get: jest.fn((item: keyof typeof mockUiSettings) => mockUiSettings[item]), + [FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0,0.[000]b', + [FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: '0,0.[000]', +} as unknown) as CoreSetup['uiSettings']; + +describe('createFieldFormatter(fieldName, fieldFormatMap?, contextType?, hasColorRules)', () => { + setFieldFormats( + getFieldFormatsRegistry(({ + uiSettings: mockUiSettings, + } as unknown) as CoreSetup) + ); + const value = 1234567890; + const stringValue = 'some string'; + const fieldFormatMap = { + bytesField: { + id: 'bytes', + }, + stringField: { + id: 'string', + params: { + transform: 'base64', + }, + }, + colorField: { + id: 'color', + params: { + fieldType: 'number', + colors: [ + { + range: '-Infinity:Infinity', + regex: '', + text: '#D36086', + background: '#ffffff', + }, + ], + }, + }, + urlField: { + id: 'url', + params: { + urlTemplate: 'https://{{value}}', + labelTemplate: '{{value}}', + }, + }, + }; + + it('should return byte formatted value for bytesField', () => { + const formatter = createFieldFormatter('bytesField', fieldFormatMap); + + expect(formatter(value)).toBe('1.15GB'); + }); + + it('should return base64 formatted value for stringField', () => { + const formatter = createFieldFormatter('stringField', fieldFormatMap); + + expect(formatter(value)).toBe('×møç®ü÷'); + }); + + it('should return color formatted value for colorField', () => { + const formatter = createFieldFormatter('colorField', fieldFormatMap, 'html'); + + expect(formatter(value)).toBe( + '1234567890' + ); + }); + + it('should return number formatted value wrapped in span for colorField when color rules are applied', () => { + const formatter = createFieldFormatter('colorField', fieldFormatMap, 'html', true); + + expect(formatter(value)).toBe('1,234,567,890'); + }); + + it('should return not formatted string value for colorField when color rules are applied', () => { + const formatter = createFieldFormatter('colorField', fieldFormatMap, 'html', true); + + expect(formatter(stringValue)).toBe(stringValue); + }); + + it('should return url formatted value for urlField', () => { + const formatter = createFieldFormatter('urlField', fieldFormatMap, 'html'); + + expect(formatter(value)).toBe( + '1234567890' + ); + }); + + it('should return "-" for null value when field has format', () => { + const formatter = createFieldFormatter('bytesField', fieldFormatMap); + + expect(formatter(null)).toBe('-'); + }); + + it('should return "-" for null value when field that has no format', () => { + const formatter = createFieldFormatter('urlField', fieldFormatMap); + + expect(formatter(null)).toBe('-'); + }); + + it('should return number formatted value for number when field has no format', () => { + const formatter = createFieldFormatter('noSuchField', fieldFormatMap); + + expect(formatter(value)).toBe('1,234,567,890'); + }); + + it('should not format string value when field has no format', () => { + const formatter = createFieldFormatter('noSuchField', fieldFormatMap); + + expect(formatter(stringValue)).toBe(stringValue); + }); +}); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.ts new file mode 100644 index 0000000000000..5cba549220f2c --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/create_field_formatter.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { isNumber } from 'lodash'; +import { getFieldFormats } from '../../../services'; +import { isEmptyValue, DISPLAY_EMPTY_VALUE } from '../../../../common/last_value_utils'; +import { FIELD_FORMAT_IDS } from '../../../../../field_formats/common'; +import type { FieldFormatMap } from '../../../../../data/common'; +import type { FieldFormatsContentType } from '../../../../../field_formats/common'; + +const DEFAULT_FIELD_FORMAT = { id: 'number' }; + +export const createFieldFormatter = ( + fieldName: string = '', + fieldFormatMap?: FieldFormatMap, + contextType?: FieldFormatsContentType, + hasColorRules: boolean = false +) => { + const serializedFieldFormat = fieldFormatMap?.[fieldName]; + // field formatting should be skipped either there's no such field format in fieldFormatMap + // or it's color formatting and color rules are already applied + const shouldSkipFormatting = + !serializedFieldFormat || + (hasColorRules && serializedFieldFormat?.id === FIELD_FORMAT_IDS.COLOR); + + const fieldFormat = getFieldFormats().deserialize( + shouldSkipFormatting ? DEFAULT_FIELD_FORMAT : serializedFieldFormat + ); + + return (value: unknown) => { + if (isEmptyValue(value)) { + return DISPLAY_EMPTY_VALUE; + } + return isNumber(value) || !shouldSkipFormatting + ? fieldFormat.convert(value, contextType) + : value; + }; +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/durations.test.js b/src/plugins/vis_type_timeseries/public/application/components/lib/durations.test.ts similarity index 100% rename from src/plugins/vis_type_timeseries/public/application/components/lib/durations.test.js rename to src/plugins/vis_type_timeseries/public/application/components/lib/durations.test.ts diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/durations.js b/src/plugins/vis_type_timeseries/public/application/components/lib/durations.ts similarity index 86% rename from src/plugins/vis_type_timeseries/public/application/components/lib/durations.js rename to src/plugins/vis_type_timeseries/public/application/components/lib/durations.ts index ac1eb76e7063d..df84c5d6781da 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/durations.js +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/durations.ts @@ -104,6 +104,7 @@ export const inputFormats = { M: 'months', Y: 'years', }; +type InputFormat = keyof typeof inputFormats; export const outputFormats = { humanize: 'humanize', @@ -116,10 +117,24 @@ export const outputFormats = { M: 'asMonths', Y: 'asYears', }; +type OutputFormat = keyof typeof outputFormats; -export const isDuration = (format) => { +export const getDurationParams = (format: string) => { + const [from, to, decimals] = format.split(','); + + return { + from, + to, + decimals, + }; +}; + +export const isDuration = (format: string) => { const splittedFormat = format.split(','); const [input, output] = splittedFormat; - return Boolean(inputFormats[input] && outputFormats[output]) && splittedFormat.length === 3; + return ( + Boolean(inputFormats[input as InputFormat] && outputFormats[output as OutputFormat]) && + splittedFormat.length === 3 + ); }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.test.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.test.ts new file mode 100644 index 0000000000000..59b778a084902 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.test.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { DATA_FORMATTERS } from '../../../../common/enums'; +import { getFormatterType } from './get_formatter_type'; + +describe('getFormatterType(formatter)', () => { + it('should return bytes formatter for "bytes"', () => { + const actual = getFormatterType(DATA_FORMATTERS.BYTES); + + expect(actual).toBe(DATA_FORMATTERS.BYTES); + }); + + it('should return duration formatter for duration format string', () => { + const actual = getFormatterType('ns,ms,2'); + + expect(actual).toBe(DATA_FORMATTERS.DURATION); + }); + + it('should return custom formatter for Numeral.js pattern', () => { + const actual = getFormatterType('$ 0.00'); + + expect(actual).toBe(DATA_FORMATTERS.CUSTOM); + }); +}); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.ts new file mode 100644 index 0000000000000..eb6b2c40f31a5 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/get_formatter_type.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { DATA_FORMATTERS } from '../../../../common/enums'; +import { isDuration } from './durations'; + +export const getFormatterType = (formatter: string) => { + if ( + [ + DATA_FORMATTERS.NUMBER, + DATA_FORMATTERS.BYTES, + DATA_FORMATTERS.PERCENT, + DATA_FORMATTERS.DEFAULT, + ].includes(formatter as DATA_FORMATTERS) + ) { + return formatter as DATA_FORMATTERS; + } + + return formatter && isDuration(formatter) ? DATA_FORMATTERS.DURATION : DATA_FORMATTERS.CUSTOM; +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.test.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.test.ts new file mode 100644 index 0000000000000..88d671af2f1ae --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getMetricsField } from './get_metrics_field'; +import type { Metric } from '../../../../common/types'; + +describe('getMetricsField(metrics)', () => { + it('should return last metric field', () => { + const metrics = [ + { id: 'some-id', type: 'avg', field: 'some field' }, + { id: 'another-id', type: 'sum_bucket', field: 'some-id' }, + { id: 'one-more-id', type: 'top_hit', field: 'one more field' }, + ] as Metric[]; + + const field = getMetricsField(metrics); + expect(field).toBe('one more field'); + }); + + it('should return undefined when last metric has no field', () => { + const metrics = [ + { id: 'some-id', type: 'avg', field: 'some field' }, + { id: 'another-id', type: 'count' }, + ] as Metric[]; + + const field = getMetricsField(metrics); + expect(field).toBeUndefined(); + }); + + it('should return field of basic aggregation', () => { + const metrics = [ + { id: 'some-id', type: 'avg', field: 'some field' }, + { id: 'another-id', type: 'sum_bucket', field: 'some-id' }, + ] as Metric[]; + + const field = getMetricsField(metrics); + expect(field).toBe('some field'); + }); + + it('should return undefined when basic aggregation has no field', () => { + const metrics = [ + { id: 'some-id', type: 'filter_ratio' }, + { id: 'another-id', type: 'max_bucket', field: 'some-id' }, + ] as Metric[]; + + const field = getMetricsField(metrics); + expect(field).toBeUndefined(); + }); +}); diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.ts b/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.ts new file mode 100644 index 0000000000000..c61f147c388f3 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/get_metrics_field.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { last } from 'lodash'; +import { Metric } from '../../../../common/types'; +import { getAggByPredicate, isBasicAgg } from '../../../../common/agg_utils'; + +export const getMetricsField = (metrics: Metric[]) => { + const selectedMetric = last(metrics); + + if (selectedMetric) { + const { isFieldRequired, isFieldFormattingDisabled } = getAggByPredicate( + selectedMetric.type + )?.meta; + + if (isFieldRequired && !isFieldFormattingDisabled) { + return isBasicAgg(selectedMetric) + ? selectedMetric.field + : metrics.find(({ id }) => selectedMetric.field === id)?.field; + } + } +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/new_series_fn.js b/src/plugins/vis_type_timeseries/public/application/components/lib/new_series_fn.js index 9064cd1afc3f4..ad12302473059 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/new_series_fn.js +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/new_series_fn.js @@ -24,7 +24,7 @@ export const newSeriesFn = (obj = {}) => { metrics: [newMetricAggFn()], separate_axis: 0, axis_position: 'right', - formatter: 'number', + formatter: 'default', chart_type: 'line', line_width: 1, point_size: 1, diff --git a/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js b/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js index 27622e29c2061..046b1c5799836 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js +++ b/src/plugins/vis_type_timeseries/public/application/components/markdown_editor.js @@ -20,8 +20,15 @@ import { CodeEditor, MarkdownLang } from '../../../../kibana_react/public'; import { EuiText, EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { getDataStart } from '../../services'; +import { fetchIndexPattern } from '../../../common/index_patterns_utils'; export class MarkdownEditor extends Component { + constructor(props) { + super(props); + this.state = { fieldFormatMap: undefined }; + } + handleChange = (value) => { this.props.onChange({ markdown: value }); }; @@ -38,17 +45,22 @@ export class MarkdownEditor extends Component { } }; + async componentDidMount() { + const { indexPatterns } = getDataStart(); + const { indexPattern } = await fetchIndexPattern(this.props.model.index_pattern, indexPatterns); + this.setState({ fieldFormatMap: indexPattern?.fieldFormatMap }); + } + render() { const { visData, model, getConfig } = this.props; if (!visData) { return null; } - const dateFormat = getConfig('dateFormat'); const series = _.get(visData, `${model.id}.series`, []); - const variables = convertSeriesToVars(series, model, dateFormat, this.props.getConfig); + const variables = convertSeriesToVars(series, model, getConfig, this.state.fieldFormatMap); const rows = []; - const rawFormatter = createTickFormatter('0.[0000]', null, this.props.getConfig); + const rawFormatter = createTickFormatter('0.[0000]', null, getConfig); const createPrimitiveRow = (key) => { const snippet = `{{ ${key} }}`; diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_config.js b/src/plugins/vis_type_timeseries/public/application/components/series_config.js index 86781c9922e46..b4907d4eaa5c2 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/series_config.js +++ b/src/plugins/vis_type_timeseries/public/application/components/series_config.js @@ -6,12 +6,13 @@ * Side Public License, v 1. */ import { i18n } from '@kbn/i18n'; +import { last } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useMemo, useCallback } from 'react'; import { DataFormatPicker } from './data_format_picker'; -import { createSelectHandler } from './lib/create_select_handler'; import { createTextHandler } from './lib/create_text_handler'; +import { checkIfNumericMetric } from './lib/check_if_numeric_metric'; import { YesNo } from './yes_no'; import { IndexPattern } from './index_pattern'; import { @@ -24,34 +25,34 @@ import { EuiHorizontalRule, } from '@elastic/eui'; import { SeriesConfigQueryBarWithIgnoreGlobalFilter } from './series_config_query_bar_with_ignore_global_filter'; +import { DATA_FORMATTERS } from '../../../common/enums'; export const SeriesConfig = (props) => { - const defaults = { offset_time: '', value_template: '' }; + const defaults = { offset_time: '', value_template: '{{value}}' }; const model = { ...defaults, ...props.model }; - const handleSelectChange = createSelectHandler(props.onChange); const handleTextChange = createTextHandler(props.onChange); const htmlId = htmlIdGenerator(); const seriesIndexPattern = props.model.override_index_pattern ? props.model.series_index_pattern : props.indexPatternForQuery; + const changeModelFormatter = useCallback((formatter) => props.onChange({ formatter }), [props]); + const isNumericMetric = useMemo( + () => checkIfNumericMetric(last(model.metrics), props.fields, seriesIndexPattern), + [model.metrics, props.fields, seriesIndexPattern] + ); + const isKibanaIndexPattern = props.panel.use_kibana_indexes || seriesIndexPattern === ''; + return (
- - - - - - - - - + + { + + + + + + + + + { - const indexPatternValue = model.index_pattern || ''; - const { indexPatterns } = getDataStart(); - const { indexPattern } = await fetchIndexPattern(indexPatternValue, indexPatterns); let event; // trigger applyFilter if no index pattern found, url drilldowns are supported only // for the index pattern mode @@ -98,15 +94,11 @@ function TimeseriesVisualization({ handlers.event(event); }, - [handlers, model] + [handlers, indexPattern, model] ); const handleFilterClick = useCallback( async (series: PanelData[], points: Array<[GeometryValue, XYChartSeriesIdentifier]>) => { - const indexPatternValue = model.index_pattern || ''; - const { indexPatterns } = getDataStart(); - const { indexPattern } = await fetchIndexPattern(indexPatternValue, indexPatterns); - // it should work only if index pattern is found if (!indexPattern) return; @@ -129,7 +121,7 @@ function TimeseriesVisualization({ handlers.event(event); }, - [handlers, model] + [handlers, indexPattern, model] ); const handleUiState = useCallback( @@ -152,17 +144,16 @@ function TimeseriesVisualization({ const shouldDisplayLastValueIndicator = isLastValueMode && !model.hide_last_value_indicator && model.type !== PANEL_TYPES.TIMESERIES; + const [firstSeries] = + (isVisTableData(visData) ? visData.series : visData[model.id]?.series) ?? []; + if (VisComponent) { return ( {shouldDisplayLastValueIndicator && ( diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/vis.js index 6140726975cbd..dbe86961db24f 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/vis.js @@ -9,10 +9,13 @@ import PropTypes from 'prop-types'; import React from 'react'; import { visWithSplits } from '../../vis_with_splits'; +import { getMetricsField } from '../../lib/get_metrics_field'; import { createTickFormatter } from '../../lib/tick_formatter'; +import { createFieldFormatter } from '../../lib/create_field_formatter'; import { get, isUndefined, assign, includes } from 'lodash'; import { Gauge } from '../../../visualizations/views/gauge'; import { getLastValue } from '../../../../../common/last_value_utils'; +import { DATA_FORMATTERS } from '../../../../../common/enums'; import { getOperator, shouldOperate } from '../../../../../common/operators_utils'; function getColors(props) { @@ -35,7 +38,7 @@ function getColors(props) { } function GaugeVisualization(props) { - const { backgroundColor, model, visData } = props; + const { backgroundColor, model, visData, fieldFormatMap, getConfig } = props; const colors = getColors(props); const series = get(visData, `${model.id}.series`, []) @@ -44,11 +47,16 @@ function GaugeVisualization(props) { const seriesDef = model.series.find((s) => includes(row.id, s.id)); const newProps = {}; if (seriesDef) { - newProps.formatter = createTickFormatter( - seriesDef.formatter, - seriesDef.value_template, - props.getConfig - ); + const hasTextColorRules = model.gauge_color_rules.some(({ text }) => text); + newProps.formatter = + seriesDef.formatter === DATA_FORMATTERS.DEFAULT + ? createFieldFormatter( + getMetricsField(seriesDef.metrics), + fieldFormatMap, + 'html', + hasTextColorRules + ) + : createTickFormatter(seriesDef.formatter, seriesDef.value_template, getConfig); } if (i === 0 && colors.gauge) newProps.color = colors.gauge; return assign({}, row, newProps); diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts b/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts index 544e2bf49690a..b2e40940b8001 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/index.ts @@ -14,6 +14,7 @@ import { PaletteRegistry } from 'src/plugins/charts/public'; import { TimeseriesVisParams } from '../../../types'; import type { TimeseriesVisData, PanelData } from '../../../../common/types'; +import type { FieldFormatMap } from '../../../../../data/common'; /** * Lazy load each visualization type, since the only one is presented on the screen at the same time. @@ -61,4 +62,5 @@ export interface TimeseriesVisProps { getConfig: IUiSettingsClient['get']; syncColors: boolean; palettesService: PaletteRegistry; + fieldFormatMap?: FieldFormatMap; } diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js index ef6b30be30a30..fc7019bd38293 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/markdown/vis.js @@ -21,9 +21,9 @@ import { isBackgroundInverted } from '../../../lib/set_is_reversed'; const getMarkdownId = (id) => `markdown-${id}`; function MarkdownVisualization(props) { - const { backgroundColor, model, visData, getConfig } = props; + const { backgroundColor, model, visData, getConfig, fieldFormatMap } = props; const series = get(visData, `${model.id}.series`, []); - const variables = convertSeriesToVars(series, model, getConfig('dateFormat'), props.getConfig); + const variables = convertSeriesToVars(series, model, getConfig, fieldFormatMap); const markdownElementId = getMarkdownId(uuid.v1()); const panelBackgroundColor = model.background_color || backgroundColor; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/vis.js index b35ee977d3e44..90e2a57d925a6 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/metric/vis.js @@ -9,9 +9,12 @@ import PropTypes from 'prop-types'; import React from 'react'; import { visWithSplits } from '../../vis_with_splits'; +import { getMetricsField } from '../../lib/get_metrics_field'; import { createTickFormatter } from '../../lib/tick_formatter'; +import { createFieldFormatter } from '../../lib/create_field_formatter'; import { get, isUndefined, assign, includes, pick } from 'lodash'; import { Metric } from '../../../visualizations/views/metric'; +import { DATA_FORMATTERS } from '../../../../../common/enums'; import { getLastValue } from '../../../../../common/last_value_utils'; import { isBackgroundInverted } from '../../../lib/set_is_reversed'; import { getOperator, shouldOperate } from '../../../../../common/operators_utils'; @@ -36,7 +39,7 @@ function getColors(props) { } function MetricVisualization(props) { - const { backgroundColor, model, visData } = props; + const { backgroundColor, model, visData, fieldFormatMap, getConfig } = props; const colors = getColors(props); const series = get(visData, `${model.id}.series`, []) .filter((row) => row) @@ -44,11 +47,15 @@ function MetricVisualization(props) { const seriesDef = model.series.find((s) => includes(row.id, s.id)); const newProps = {}; if (seriesDef) { - newProps.formatter = createTickFormatter( - seriesDef.formatter, - seriesDef.value_template, - props.getConfig - ); + newProps.formatter = + seriesDef.formatter === DATA_FORMATTERS.DEFAULT + ? createFieldFormatter( + getMetricsField(seriesDef.metrics), + fieldFormatMap, + 'html', + colors.color + ) + : createTickFormatter(seriesDef.formatter, seriesDef.value_template, getConfig); } if (i === 0 && colors.color) newProps.color = colors.color; return assign({}, pick(row, ['label', 'data']), newProps); diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/config.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/config.js index 094c33f131fd9..e7d13e1497f5c 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/config.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/config.js @@ -10,6 +10,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import uuid from 'uuid'; import { i18n } from '@kbn/i18n'; +import { last } from 'lodash'; import { DataFormatPicker } from '../../data_format_picker'; import { createSelectHandler } from '../../lib/create_select_handler'; @@ -31,7 +32,9 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { getDefaultQueryLanguage } from '../../lib/get_default_query_language'; +import { checkIfNumericMetric } from '../../lib/check_if_numeric_metric'; import { QueryBarWrapper } from '../../query_bar_wrapper'; +import { DATA_FORMATTERS } from '../../../../../common/enums'; export class TableSeriesConfig extends Component { UNSAFE_componentWillMount() { @@ -43,8 +46,10 @@ export class TableSeriesConfig extends Component { } } + changeModelFormatter = (formatter) => this.props.onChange({ formatter }); + render() { - const defaults = { offset_time: '', value_template: '' }; + const defaults = { offset_time: '', value_template: '{{value}}' }; const model = { ...defaults, ...this.props.model }; const handleSelectChange = createSelectHandler(this.props.onChange); const handleTextChange = createTextHandler(this.props.onChange); @@ -110,13 +115,24 @@ export class TableSeriesConfig extends Component { return model.aggregate_function === option.value; }); + const isNumericMetric = checkIfNumericMetric( + last(model.metrics), + this.props.fields, + this.props.indexPatternForQuery + ); + const isKibanaIndexPattern = + this.props.panel.use_kibana_indexes || this.props.indexPatternForQuery === ''; + return (
- - - - + + diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js index ba235a20b97ce..21d7de9f1d880 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js @@ -11,13 +11,16 @@ import React, { Component } from 'react'; import { parse as parseUrl } from 'url'; import PropTypes from 'prop-types'; import { RedirectAppLinks } from '../../../../../../kibana_react/public'; +import { getMetricsField } from '../../lib/get_metrics_field'; import { createTickFormatter } from '../../lib/tick_formatter'; +import { createFieldFormatter } from '../../lib/create_field_formatter'; import { isSortable } from './is_sortable'; import { EuiToolTip, EuiIcon } from '@elastic/eui'; import { replaceVars } from '../../lib/replace_vars'; import { FIELD_FORMAT_IDS } from '../../../../../../../plugins/field_formats/common'; import { FormattedMessage } from '@kbn/i18n/react'; import { getFieldFormats, getCoreStart } from '../../../../services'; +import { DATA_FORMATTERS } from '../../../../../common/enums'; import { getValueOrEmpty } from '../../../../../common/empty_label'; function getColor(rules, colorKey, value) { @@ -57,26 +60,40 @@ class TableVis extends Component { } renderRow = (row) => { - const { model } = this.props; + const { model, fieldFormatMap, getConfig } = this.props; let rowDisplay = getValueOrEmpty( model.pivot_type === 'date' ? this.dateFormatter.convert(row.key) : row.key ); + // we should skip url field formatting for key if tsvb have drilldown_url + if (fieldFormatMap?.[model.pivot_id]?.id !== FIELD_FORMAT_IDS.URL || !model.drilldown_url) { + const formatter = createFieldFormatter(model?.pivot_id, fieldFormatMap, 'html'); + rowDisplay = ; // eslint-disable-line react/no-danger + } + if (model.drilldown_url) { const url = replaceVars(model.drilldown_url, {}, { key: row.key }); rowDisplay = {rowDisplay}; } + const columns = row.series .filter((item) => item) .map((item) => { const column = this.visibleSeries.find((c) => c.id === item.id); if (!column) return null; - const formatter = createTickFormatter( - column.formatter, - column.value_template, - this.props.getConfig + const hasColorRules = column.color_rules?.some( + ({ value, operator, text }) => value || operator || text ); + const formatter = + column.formatter === DATA_FORMATTERS.DEFAULT + ? createFieldFormatter( + getMetricsField(column.metrics), + fieldFormatMap, + 'html', + hasColorRules + ) + : createTickFormatter(column.formatter, column.value_template, getConfig); const value = formatter(item.last); let trend; if (column.trend_arrows) { @@ -95,7 +112,8 @@ class TableVis extends Component { className="eui-textRight" style={style} > - {value} + {/* eslint-disable-next-line react/no-danger */} + {trend} ); diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js index 01ba8b6e28114..4257c35a6d4c2 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js @@ -8,7 +8,9 @@ import { i18n } from '@kbn/i18n'; import PropTypes from 'prop-types'; -import React, { useState, useEffect } from 'react'; +import { last } from 'lodash'; +import React, { useMemo, useState, useEffect, useCallback } from 'react'; +import { DATA_FORMATTERS } from '../../../../../common/enums'; import { DataFormatPicker } from '../../data_format_picker'; import { createSelectHandler } from '../../lib/create_select_handler'; import { YesNo } from '../../yes_no'; @@ -29,6 +31,7 @@ import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { SeriesConfigQueryBarWithIgnoreGlobalFilter } from '../../series_config_query_bar_with_ignore_global_filter'; import { PalettePicker } from '../../palette_picker'; import { getCharts } from '../../../../services'; +import { checkIfNumericMetric } from '../../lib/check_if_numeric_metric'; import { isPercentDisabled } from '../../lib/stacked'; import { STACKED_OPTIONS } from '../../../visualizations/constants/chart'; @@ -328,6 +331,13 @@ export const TimeseriesConfig = injectI18n(function (props) { ? props.model.series_index_pattern : props.indexPatternForQuery; + const changeModelFormatter = useCallback((formatter) => props.onChange({ formatter }), [props]); + const isNumericMetric = useMemo( + () => checkIfNumericMetric(last(model.metrics), props.fields, seriesIndexPattern), + [model.metrics, props.fields, seriesIndexPattern] + ); + const isKibanaIndexPattern = props.panel.use_kibana_indexes || seriesIndexPattern === ''; + const initialPalette = model.palette ?? { type: 'palette', name: 'default', @@ -344,10 +354,13 @@ export const TimeseriesConfig = injectI18n(function (props) { return (
- - - - + + diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js index d9440804701b2..fed295fef9d30 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js @@ -13,7 +13,10 @@ import { startsWith, get, cloneDeep, map } from 'lodash'; import { htmlIdGenerator } from '@elastic/eui'; import { ScaleType } from '@elastic/charts'; +import { getMetricsField } from '../../lib/get_metrics_field'; import { createTickFormatter } from '../../lib/tick_formatter'; +import { createFieldFormatter } from '../../lib/create_field_formatter'; +import { checkIfSeriesHaveSameFormatters } from '../../lib/check_if_series_have_same_formatters'; import { TimeSeries } from '../../../visualizations/views/timeseries'; import { MarkdownSimple } from '../../../../../../../plugins/kibana_react/public'; import { replaceVars } from '../../lib/replace_vars'; @@ -21,6 +24,7 @@ import { getInterval } from '../../lib/get_interval'; import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter'; import { STACKED_OPTIONS } from '../../../visualizations/constants'; import { getCoreStart } from '../../../../services'; +import { DATA_FORMATTERS } from '../../../../../common/enums'; class TimeseriesVisualization extends Component { static propTypes = { @@ -51,6 +55,16 @@ class TimeseriesVisualization extends Component { }; applyDocTo = (template) => (doc) => { + const { fieldFormatMap } = this.props; + + // formatting each doc value with custom field formatter if fieldFormatMap contains that doc field name + Object.keys(doc).forEach((fieldName) => { + if (fieldFormatMap?.[fieldName]) { + const valueFieldFormatter = createFieldFormatter(fieldName, fieldFormatMap); + doc[fieldName] = valueFieldFormatter(doc[fieldName]); + } + }); + const vars = replaceVars(template, null, doc, { noEscape: true, }); @@ -139,7 +153,16 @@ class TimeseriesVisualization extends Component { }; render() { - const { model, visData, onBrush, onFilterClick, syncColors, palettesService } = this.props; + const { + model, + visData, + onBrush, + onFilterClick, + syncColors, + palettesService, + fieldFormatMap, + getConfig, + } = this.props; const series = get(visData, `${model.id}.series`, []); const interval = getInterval(visData, model); const yAxisIdGenerator = htmlIdGenerator('yaxis'); @@ -152,10 +175,6 @@ class TimeseriesVisualization extends Component { const yAxis = []; let mainDomainAdded = false; - const allSeriesHaveSameFormatters = seriesModel.every( - (seriesGroup) => seriesGroup.formatter === seriesModel[0].formatter - ); - this.showToastNotification = null; seriesModel.forEach((seriesGroup) => { @@ -166,10 +185,12 @@ class TimeseriesVisualization extends Component { ? TimeseriesVisualization.getYAxisDomain(seriesGroup) : undefined; const isCustomDomain = groupId !== mainAxisGroupId; - const seriesGroupTickFormatter = TimeseriesVisualization.getTickFormatter( - seriesGroup, - this.props.getConfig - ); + + const seriesGroupTickFormatter = + seriesGroup.formatter === DATA_FORMATTERS.DEFAULT + ? createFieldFormatter(getMetricsField(seriesGroup.metrics), fieldFormatMap) + : TimeseriesVisualization.getTickFormatter(seriesGroup, getConfig); + const palette = { ...seriesGroup.palette, name: @@ -214,8 +235,12 @@ class TimeseriesVisualization extends Component { : seriesGroupTickFormatter, }); } else if (!mainDomainAdded) { + const tickFormatter = checkIfSeriesHaveSameFormatters(seriesModel, fieldFormatMap) + ? seriesGroupTickFormatter + : (val) => val; + TimeseriesVisualization.addYAxis(yAxis, { - tickFormatter: allSeriesHaveSameFormatters ? seriesGroupTickFormatter : (val) => val, + tickFormatter, id: yAxisIdGenerator('main'), groupId: mainAxisGroupId, position: model.axis_position, diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js index fd155623d5da7..d6e7484e903bf 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js @@ -11,9 +11,15 @@ import { shallow } from 'enzyme'; import { TimeSeries } from '../../../visualizations/views/timeseries'; import TimeseriesVisualization from './vis'; import { setFieldFormats } from '../../../../services'; +import { createFieldFormatter } from '../../lib/create_field_formatter'; import { FORMATS_UI_SETTINGS } from '../../../../../../field_formats/common'; +import { METRIC_TYPES } from '../../../../../../data/common'; import { getFieldFormatsRegistry } from '../../../../../../data/public/test_utils'; +jest.mock('../../../../../../data/public/services', () => ({ + getUiSettings: () => ({ get: jest.fn() }), +})); + describe('TimeseriesVisualization', () => { describe('TimeSeries Y-Axis formatted value', () => { const config = { @@ -29,19 +35,34 @@ describe('TimeseriesVisualization', () => { }) ); - const setupTimeSeriesPropsWithFormatters = (...formatters) => { - const series = formatters.map((formatter) => ({ - id, + const setupTimeSeriesProps = (formatters, valueTemplates) => { + const series = formatters.map((formatter, index) => ({ + id: id + index, formatter, + value_template: valueTemplates?.[index], data: [], + metrics: [ + { + type: METRIC_TYPES.AVG, + field: `field${index}`, + }, + ], })); + const fieldFormatMap = { + field0: { id: 'duration', params: { inputFormat: 'years' } }, + field1: { id: 'duration', params: { inputFormat: 'years' } }, + field2: { id: 'duration', params: { inputFormat: 'months' } }, + field3: { id: 'number', params: { pattern: '$0,0.[00]' } }, + }; + const timeSeriesVisualization = shallow( config[key]} model={{ id, series, + use_kibana_indexes: true, }} visData={{ [id]: { @@ -49,56 +70,69 @@ describe('TimeseriesVisualization', () => { series, }, }} + fieldFormatMap={fieldFormatMap} + createCustomFieldFormatter={createFieldFormatter} /> ); return timeSeriesVisualization.find(TimeSeries).props(); }; - test('should be byte for single byte series', () => { - const timeSeriesProps = setupTimeSeriesPropsWithFormatters('byte'); + test('should return byte formatted value from yAxis formatter for single byte series', () => { + const timeSeriesProps = setupTimeSeriesProps(['byte']); const yAxisFormattedValue = timeSeriesProps.yAxis[0].tickFormatter(value); expect(yAxisFormattedValue).toBe('500B'); }); - test('should have custom format for single series', () => { - const timeSeriesProps = setupTimeSeriesPropsWithFormatters('0.00bitd'); + test('should return custom formatted value from yAxis formatter for single series with custom formatter', () => { + const timeSeriesProps = setupTimeSeriesProps(['0.00bitd']); const yAxisFormattedValue = timeSeriesProps.yAxis[0].tickFormatter(value); expect(yAxisFormattedValue).toBe('500.00bit'); }); - test('should be the same number for byte and percent series', () => { - const timeSeriesProps = setupTimeSeriesPropsWithFormatters('byte', 'percent'); + test('should return the same number from yAxis formatter for byte and percent series', () => { + const timeSeriesProps = setupTimeSeriesProps(['byte', 'percent']); const yAxisFormattedValue = timeSeriesProps.yAxis[0].tickFormatter(value); expect(yAxisFormattedValue).toBe(value); }); - test('should be the same stringified number for byte and percent series', () => { - const timeSeriesProps = setupTimeSeriesPropsWithFormatters('byte', 'percent'); + test('should return the same stringified number from yAxis formatter for byte and percent series', () => { + const timeSeriesProps = setupTimeSeriesProps(['byte', 'percent']); const yAxisFormattedValue = timeSeriesProps.yAxis[0].tickFormatter(value.toString()); expect(yAxisFormattedValue).toBe('500'); }); - test('should be byte for two byte formatted series', () => { - const timeSeriesProps = setupTimeSeriesPropsWithFormatters('byte', 'byte'); + test('should return byte formatted value from yAxis formatter and from two byte formatted series with the same value templates', () => { + const timeSeriesProps = setupTimeSeriesProps(['byte', 'byte']); + const { series, yAxis } = timeSeriesProps; - const yAxisFormattedValue = timeSeriesProps.yAxis[0].tickFormatter(value); - const firstSeriesFormattedValue = timeSeriesProps.series[0].tickFormat(value); + expect(series[0].tickFormat(value)).toBe('500B'); + expect(series[1].tickFormat(value)).toBe('500B'); + expect(yAxis[0].tickFormatter(value)).toBe('500B'); + }); - expect(firstSeriesFormattedValue).toBe('500B'); - expect(yAxisFormattedValue).toBe(firstSeriesFormattedValue); + test('should return simple number from yAxis formatter and different values from the same byte formatters, but with different value templates', () => { + const timeSeriesProps = setupTimeSeriesProps( + ['byte', 'byte'], + ['{{value}}', '{{value}} value'] + ); + const { series, yAxis } = timeSeriesProps; + + expect(series[0].tickFormat(value)).toBe('500B'); + expect(series[1].tickFormat(value)).toBe('500B value'); + expect(yAxis[0].tickFormatter(value)).toBe(value); }); - test('should be percent for three percent formatted series', () => { - const timeSeriesProps = setupTimeSeriesPropsWithFormatters('percent', 'percent', 'percent'); + test('should return percent formatted value from yAxis formatter and three percent formatted series with the same value templates', () => { + const timeSeriesProps = setupTimeSeriesProps(['percent', 'percent', 'percent']); const yAxisFormattedValue = timeSeriesProps.yAxis[0].tickFormatter(value); const firstSeriesFormattedValue = timeSeriesProps.series[0].tickFormat(value); @@ -106,5 +140,56 @@ describe('TimeseriesVisualization', () => { expect(firstSeriesFormattedValue).toBe('50000%'); expect(yAxisFormattedValue).toBe(firstSeriesFormattedValue); }); + + test('should return simple number from yAxis formatter and different values for the same value templates, but with different formatters', () => { + const timeSeriesProps = setupTimeSeriesProps( + ['number', 'byte'], + ['{{value}} template', '{{value}} template'] + ); + const { series, yAxis } = timeSeriesProps; + + expect(series[0].tickFormat(value)).toBe('500 template'); + expect(series[1].tickFormat(value)).toBe('500B template'); + expect(yAxis[0].tickFormatter(value)).toBe(value); + }); + + test('should return field formatted value for yAxis and single series with default formatter', () => { + const timeSeriesProps = setupTimeSeriesProps(['default']); + const { series, yAxis } = timeSeriesProps; + + expect(series[0].tickFormat(value)).toBe('500 years'); + expect(yAxis[0].tickFormatter(value)).toBe('500 years'); + }); + + test('should return custom field formatted value for yAxis and both series having same fieldFormats', () => { + const timeSeriesProps = setupTimeSeriesProps(['default', 'default']); + const { series, yAxis } = timeSeriesProps; + + expect(series[0].tickFormat(value)).toBe('500 years'); + expect(series[1].tickFormat(value)).toBe('500 years'); + expect(yAxis[0].tickFormatter(value)).toBe('500 years'); + }); + + test('should return simple number from yAxis formatter and default formatted values for series', () => { + const timeSeriesProps = setupTimeSeriesProps(['default', 'default', 'default', 'default']); + const { series, yAxis } = timeSeriesProps; + + expect(series[0].tickFormat(value)).toBe('500 years'); + expect(series[1].tickFormat(value)).toBe('500 years'); + expect(series[2].tickFormat(value)).toBe('42 years'); + expect(series[3].tickFormat(value)).toBe('$500'); + expect(yAxis[0].tickFormatter(value)).toBe(value); + }); + + test('should return simple number from yAxis formatter and correctly formatted series values', () => { + const timeSeriesProps = setupTimeSeriesProps(['default', 'byte', 'percent', 'default']); + const { series, yAxis } = timeSeriesProps; + + expect(series[0].tickFormat(value)).toBe('500 years'); + expect(series[1].tickFormat(value)).toBe('500B'); + expect(series[2].tickFormat(value)).toBe('50000%'); + expect(series[3].tickFormat(value)).toBe('$500'); + expect(yAxis[0].tickFormatter(value)).toBe(value); + }); }); }); diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/vis.js index 0b3a24615c0e3..8176f6ece2805 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/top_n/vis.js @@ -7,7 +7,9 @@ */ import { getCoreStart } from '../../../../services'; +import { getMetricsField } from '../../lib/get_metrics_field'; import { createTickFormatter } from '../../lib/tick_formatter'; +import { createFieldFormatter } from '../../lib/create_field_formatter'; import { TopN } from '../../../visualizations/views/top_n'; import { getLastValue } from '../../../../../common/last_value_utils'; import { isBackgroundInverted } from '../../../lib/set_is_reversed'; @@ -15,6 +17,7 @@ import { replaceVars } from '../../lib/replace_vars'; import PropTypes from 'prop-types'; import React from 'react'; import { sortBy, first, get } from 'lodash'; +import { DATA_FORMATTERS } from '../../../../../common/enums'; import { getOperator, shouldOperate } from '../../../../../common/operators_utils'; function sortByDirection(data, direction, fn) { @@ -38,17 +41,17 @@ function sortSeries(visData, model) { } function TopNVisualization(props) { - const { backgroundColor, model, visData } = props; + const { backgroundColor, model, visData, fieldFormatMap, getConfig } = props; const series = sortSeries(visData, model).map((item) => { const id = first(item.id.split(/:/)); const seriesConfig = model.series.find((s) => s.id === id); if (seriesConfig) { - const tickFormatter = createTickFormatter( - seriesConfig.formatter, - seriesConfig.value_template, - props.getConfig - ); + const tickFormatter = + seriesConfig.formatter === DATA_FORMATTERS.DEFAULT + ? createFieldFormatter(getMetricsField(seriesConfig.metrics), fieldFormatMap, 'html') + : createTickFormatter(seriesConfig.formatter, seriesConfig.value_template, getConfig); + const value = getLastValue(item.data); let color = item.color || seriesConfig.color; if (model.bar_color_rules) { diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js b/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js index 945a7ac986d3e..86c0af1c97980 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_with_splits.js @@ -15,7 +15,7 @@ import { getSplitByTermsColor } from '../lib/get_split_by_terms_color'; export function visWithSplits(WrappedComponent) { function SplitVisComponent(props) { - const { model, visData, syncColors, palettesService } = props; + const { model, visData, syncColors, palettesService, fieldFormatMap } = props; const getSeriesColor = useCallback( (seriesName, seriesId, baseColor) => { @@ -34,10 +34,11 @@ export function visWithSplits(WrappedComponent) { seriesPalette: palette, palettesRegistry: palettesService, syncColors, + fieldFormatMap, }; return getSplitByTermsColor(props) || null; }, - [model, palettesService, syncColors, visData] + [fieldFormatMap, model.id, model.series, palettesService, syncColors, visData] ); if (!model || !visData || !visData[model.id] || visData[model.id].series.length === 1) @@ -114,6 +115,7 @@ export function visWithSplits(WrappedComponent) { additionalLabel={getValueOrEmpty(additionalLabel)} backgroundColor={props.backgroundColor} getConfig={props.getConfig} + fieldFormatMap={props.fieldFormatMap} />
); diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_gauge.scss b/src/plugins/vis_type_timeseries/public/application/visualizations/views/_gauge.scss index 7f3c049a131d2..fdab7f02957e0 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/_gauge.scss +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/_gauge.scss @@ -47,6 +47,8 @@ font-size: .9em; /* 1 */ line-height: 1em; /* 1 */ text-align: center; + // make gauge value the target for pointer-events + pointer-events: all; .tvbVisGauge--reversed & { color: $tvbValueColorReversed; @@ -71,4 +73,6 @@ display: flex; flex-direction: column; flex: 1 0 auto; + // disable gauge container pointer-events as it shouldn't be event target + pointer-events: none; } diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js index 723a054baeeae..ca5021a882932 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/gauge.js @@ -117,7 +117,8 @@ export class Gauge extends Component { ref="label" data-test-subj="gaugeValue" > - {formatter(value)} + {/* eslint-disable-next-line react/no-danger */} +
{additionalLabel}
@@ -135,7 +136,8 @@ export class Gauge extends Component { ref="label" data-test-subj="gaugeValue" > - {formatter(value)} + {/* eslint-disable-next-line react/no-danger */} +
{title} diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js index bc4230d0a15ef..0ceb2daa831be 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/metric.js @@ -101,7 +101,8 @@ export class Metric extends Component {
{secondaryLabel}
- {secondaryValue} + {/* eslint-disable-next-line react/no-danger */} +
); @@ -132,7 +133,8 @@ export class Metric extends Component { data-test-subj="tsvbMetricValue" className="tvbVisMetric__value--primary" > - {primaryValue} + {/* eslint-disable-next-line react/no-danger */} +
{secondarySnippet} diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js index 72b2c7ce34fd8..aaec701a42eea 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js @@ -139,7 +139,8 @@ export class TopN extends Component {
- {formatter(lastValue)} + {/* eslint-disable-next-line react/no-danger */} + ); diff --git a/src/plugins/vis_type_timeseries/public/metrics_type.ts b/src/plugins/vis_type_timeseries/public/metrics_type.ts index 855d56169ef35..5d4a61c1edb82 100644 --- a/src/plugins/vis_type_timeseries/public/metrics_type.ts +++ b/src/plugins/vis_type_timeseries/public/metrics_type.ts @@ -77,7 +77,7 @@ export const metricsVisDefinition: VisTypeDefinition< ], separate_axis: 0, axis_position: 'right', - formatter: 'number', + formatter: 'default', chart_type: 'line', line_width: 1, point_size: 1, diff --git a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx b/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx index 3f324fcfc2f20..9a19ddc285ebb 100644 --- a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx +++ b/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx @@ -13,11 +13,12 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; import { IUiSettingsClient } from 'kibana/public'; +import { fetchIndexPattern } from '../common/index_patterns_utils'; import { VisualizationContainer, PersistedState } from '../../visualizations/public'; import type { TimeseriesVisData } from '../common/types'; import { isVisTableData } from '../common/vis_data_utils'; -import { getCharts } from './services'; +import { getCharts, getDataStart } from './services'; import type { TimeseriesVisParams } from './types'; import type { ExpressionRenderDefinition } from '../../expressions/common'; @@ -49,9 +50,15 @@ export const getTimeseriesVisRenderer: (deps: { handlers.onDestroy(() => { unmountComponentAtNode(domNode); }); + const { visParams: model, visData, syncColors } = config; const { palettes } = getCharts(); - const showNoResult = !checkIfDataExists(config.visData, config.visParams); - const palettesService = await palettes.getPalettes(); + const { indexPatterns } = getDataStart(); + + const showNoResult = !checkIfDataExists(visData, model); + const [palettesService, { indexPattern }] = await Promise.all([ + palettes.getPalettes(), + fetchIndexPattern(model.index_pattern, indexPatterns), + ]); render( @@ -59,15 +66,16 @@ export const getTimeseriesVisRenderer: (deps: { data-test-subj="timeseriesVis" handlers={handlers} showNoResult={showNoResult} - error={get(config.visData, [config.visParams.id, 'error'])} + error={get(visData, [model.id, 'error'])} > diff --git a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts index 817812a88ca98..bc4fbf9159a00 100644 --- a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts @@ -30,6 +30,7 @@ export async function getVisData( ): Promise { const uiSettings = requestContext.core.uiSettings.client; const esShardTimeout = await framework.getEsShardTimeout(); + const fieldFormatService = await framework.getFieldFormatsService(uiSettings); const indexPatternsService = await framework.getIndexPatternsService(requestContext); const esQueryConfig = await getEsQueryConfig(uiSettings); @@ -40,6 +41,7 @@ export async function getVisData( const services: VisTypeTimeseriesRequestServices = { esQueryConfig, esShardTimeout, + fieldFormatService, indexPatternsService, uiSettings, cachedIndexPatternFetcher, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts index 8d495d68eb625..12fe95ccc50ca 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.ts @@ -27,13 +27,16 @@ export async function getSeriesData( panel: Panel, services: VisTypeTimeseriesRequestServices ) { - const panelIndex = await services.cachedIndexPatternFetcher(panel.index_pattern); + const { + cachedIndexPatternFetcher, + searchStrategyRegistry, + indexPatternsService, + fieldFormatService, + } = services; - const strategy = await services.searchStrategyRegistry.getViableStrategy( - requestContext, - req, - panelIndex - ); + const panelIndex = await cachedIndexPatternFetcher(panel.index_pattern); + + const strategy = await searchStrategyRegistry.getViableStrategy(requestContext, req, panelIndex); if (!strategy) { throw new Error( @@ -56,15 +59,22 @@ export async function getSeriesData( getSeriesRequestParams(req, panel, panelIndex, series, capabilities, services) ); - const searches = await Promise.all(bodiesPromises); - const data = await searchStrategy.search(requestContext, req, searches); - - const handleResponseBodyFn = handleResponseBody(panel, req, { - indexPatternsService: services.indexPatternsService, - cachedIndexPatternFetcher: services.cachedIndexPatternFetcher, + const fieldFetchServices = { + indexPatternsService, + cachedIndexPatternFetcher, searchStrategy, capabilities, - }); + }; + + const handleResponseBodyFn = handleResponseBody( + panel, + req, + fieldFetchServices, + fieldFormatService + ); + + const searches = await Promise.all(bodiesPromises); + const data = await searchStrategy.search(requestContext, req, searches); const series = await Promise.all( data.map( diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/format_label.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/format_label.ts new file mode 100644 index 0000000000000..7908cbccb9845 --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/format_label.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { BUCKET_TYPES, PANEL_TYPES } from '../../../../../common/enums'; +import type { Panel, PanelData, Series } from '../../../../../common/types'; +import type { FieldFormatsRegistry } from '../../../../../../field_formats/common'; +import type { createFieldsFetcher } from '../../../search_strategies/lib/fields_fetcher'; +import type { CachedIndexPatternFetcher } from '../../../search_strategies/lib/cached_index_pattern_fetcher'; + +export function formatLabel( + resp: unknown, + panel: Panel, + series: Series, + meta: any, + extractFields: ReturnType, + fieldFormatService: FieldFormatsRegistry, + cachedIndexPatternFetcher: CachedIndexPatternFetcher +) { + return (next: (results: PanelData[]) => unknown) => async (results: PanelData[]) => { + const { terms_field: termsField, split_mode: splitMode } = series; + + const isKibanaIndexPattern = panel.use_kibana_indexes || panel.index_pattern === ''; + // no need to format labels for markdown as they also used there as variables keys + const shouldFormatLabels = + isKibanaIndexPattern && + termsField && + splitMode === BUCKET_TYPES.TERMS && + panel.type !== PANEL_TYPES.MARKDOWN; + + if (shouldFormatLabels) { + const { indexPattern } = await cachedIndexPatternFetcher({ id: meta.index }); + const getFieldFormatByName = (fieldName: string) => + fieldFormatService.deserialize(indexPattern?.fieldFormatMap?.[fieldName]); + + results + .filter(({ seriesId }) => series.id === seriesId) + .forEach((item) => { + const formattedLabel = getFieldFormatByName(termsField!).convert(item.label); + item.label = formattedLabel; + const termsFieldType = indexPattern?.fields.find(({ name }) => name === termsField)?.type; + if (termsFieldType === KBN_FIELD_TYPES.DATE) { + item.labelFormatted = formattedLabel; + } + }); + } + + return next(results); + }; +} diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/index.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/index.js index 71c3bdf5e5c23..68385bb5cbbe4 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/index.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/index.js @@ -17,6 +17,7 @@ import { stdSibling } from './std_sibling'; import { timeShift } from './time_shift'; import { dropLastBucket } from './drop_last_bucket'; import { mathAgg } from './math'; +import { formatLabel } from './format_label'; export const processors = [ percentile, @@ -29,4 +30,5 @@ export const processors = [ seriesAgg, timeShift, dropLastBucket, + formatLabel, ]; diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts index 6642fd8f5d79e..78e9f971a61dd 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/series/handle_response_body.ts @@ -17,11 +17,13 @@ import { FieldsFetcherServices, } from '../../search_strategies/lib/fields_fetcher'; import { VisTypeTimeseriesVisDataRequest } from '../../../types'; +import type { FieldFormatsRegistry } from '../../../../../field_formats/common'; export function handleResponseBody( panel: Panel, req: VisTypeTimeseriesVisDataRequest, - services: FieldsFetcherServices + services: FieldsFetcherServices, + fieldFormatService: FieldFormatsRegistry ) { return async (resp: any) => { if (resp.error) { @@ -55,7 +57,9 @@ export function handleResponseBody( panel, series, meta, - extractFields + extractFields, + fieldFormatService, + services.cachedIndexPatternFetcher ); return await processor([]); diff --git a/src/plugins/vis_type_timeseries/server/plugin.ts b/src/plugins/vis_type_timeseries/server/plugin.ts index 58cd58c812e4d..d2ecb07c0273d 100644 --- a/src/plugins/vis_type_timeseries/server/plugin.ts +++ b/src/plugins/vis_type_timeseries/server/plugin.ts @@ -13,6 +13,7 @@ import { Plugin, Logger, KibanaRequest, + IUiSettingsClient, } from 'src/core/server'; import { Observable } from 'rxjs'; import { Server } from '@hapi/hapi'; @@ -29,6 +30,7 @@ import type { VisTypeTimeseriesRequestHandlerContext, VisTypeTimeseriesVisDataRequest, } from './types'; +import type { FieldFormatsRegistry } from '../../field_formats/common'; import { SearchStrategyRegistry, @@ -70,6 +72,7 @@ export interface Framework { getIndexPatternsService: ( requestContext: VisTypeTimeseriesRequestHandlerContext ) => Promise; + getFieldFormatsService: (uiSettings: IUiSettingsClient) => Promise; getEsShardTimeout: () => Promise; } @@ -111,6 +114,11 @@ export class VisTypeTimeseriesPlugin implements Plugin { requestContext.core.elasticsearch.client.asCurrentUser ); }, + getFieldFormatsService: async (uiSettings) => { + const [, { data }] = await core.getStartServices(); + + return data.fieldFormats.fieldFormatServiceFactory(uiSettings); + }, }; searchStrategyRegistry.addStrategy(new DefaultSearchStrategy()); diff --git a/src/plugins/vis_type_timeseries/server/types.ts b/src/plugins/vis_type_timeseries/server/types.ts index 11131f33e4a1c..40ced72933012 100644 --- a/src/plugins/vis_type_timeseries/server/types.ts +++ b/src/plugins/vis_type_timeseries/server/types.ts @@ -11,6 +11,7 @@ import { EsQueryConfig } from '@kbn/es-query'; import { SharedGlobalConfig } from 'kibana/server'; import type { IRouter, IUiSettingsClient, KibanaRequest } from 'src/core/server'; import type { DataRequestHandlerContext, IndexPatternsService } from '../../data/server'; +import type { FieldFormatsRegistry } from '../../field_formats/common'; import type { Series, VisPayload } from '../common/types'; import type { SearchStrategyRegistry } from './lib/search_strategies'; import type { CachedIndexPatternFetcher } from './lib/search_strategies/lib/cached_index_pattern_fetcher'; @@ -33,6 +34,7 @@ export interface VisTypeTimeseriesRequestServices { indexPatternsService: IndexPatternsService; searchStrategyRegistry: SearchStrategyRegistry; cachedIndexPatternFetcher: CachedIndexPatternFetcher; + fieldFormatService: FieldFormatsRegistry; buildSeriesMetaParams: ( index: FetchedIndexPattern, useKibanaIndexes: boolean, diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json b/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json index f44bb7463e9eb..619e300016043 100644 --- a/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json +++ b/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json @@ -29,7 +29,6 @@ "search": "db2c00e39b36f40930a3b9fc71c823e1", "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -373,47 +372,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/search/data.json b/test/api_integration/fixtures/es_archiver/management/saved_objects/search/data.json index 6402a255afd37..05116741dbe5c 100644 --- a/test/api_integration/fixtures/es_archiver/management/saved_objects/search/data.json +++ b/test/api_integration/fixtures/es_archiver/management/saved_objects/search/data.json @@ -1,32 +1,3 @@ -{ - "type": "doc", - "value": { - "id": "timelion-sheet:190f3e90-2ec3-11e8-ba48-69fc4e41e1f6", - "index": ".kibana", - "source": { - "coreMigrationVersion": "7.14.0", - "references": [ - ], - "timelion-sheet": { - "description": "", - "hits": 0, - "timelion_chart_height": 275, - "timelion_columns": 2, - "timelion_interval": "auto", - "timelion_rows": 2, - "timelion_sheet": [ - ".es(*)" - ], - "title": "New TimeLion Sheet", - "version": 1 - }, - "type": "timelion-sheet", - "updated_at": "2018-03-23T17:53:30.872Z" - }, - "type": "_doc" - } -} - { "type": "doc", "value": { diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json b/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json index 7699a72ff7120..e9ec4e680d74e 100644 --- a/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json +++ b/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json @@ -29,7 +29,6 @@ "search": "db2c00e39b36f40930a3b9fc71c823e1", "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -377,47 +376,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/api_integration/fixtures/es_archiver/saved_objects/ui_counters/mappings.json b/test/api_integration/fixtures/es_archiver/saved_objects/ui_counters/mappings.json index 99f2f999db988..34b4ba98b3ee8 100644 --- a/test/api_integration/fixtures/es_archiver/saved_objects/ui_counters/mappings.json +++ b/test/api_integration/fixtures/es_archiver/saved_objects/ui_counters/mappings.json @@ -165,47 +165,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "namespace": { "type": "keyword" }, diff --git a/test/api_integration/fixtures/es_archiver/saved_objects/usage_counters/mappings.json b/test/api_integration/fixtures/es_archiver/saved_objects/usage_counters/mappings.json index c2ec5c8881087..e818c2f8cbe20 100644 --- a/test/api_integration/fixtures/es_archiver/saved_objects/usage_counters/mappings.json +++ b/test/api_integration/fixtures/es_archiver/saved_objects/usage_counters/mappings.json @@ -158,47 +158,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "namespace": { "type": "keyword" }, diff --git a/test/api_integration/fixtures/es_archiver/search/count/mappings.json b/test/api_integration/fixtures/es_archiver/search/count/mappings.json index 41d5c07e93239..8a46d3fc66c85 100644 --- a/test/api_integration/fixtures/es_archiver/search/count/mappings.json +++ b/test/api_integration/fixtures/es_archiver/search/count/mappings.json @@ -39,48 +39,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "visualization": { "dynamic": "strict", "properties": { diff --git a/test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json b/test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json index da2241952ca37..5152303854b75 100644 --- a/test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json +++ b/test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json @@ -1,25 +1,3 @@ -{ - "attributes": { - "description": "", - "hits": 0, - "timelion_chart_height": 275, - "timelion_columns": 2, - "timelion_interval": "auto", - "timelion_rows": 2, - "timelion_sheet": [ - ".es(*)" - ], - "title": "New TimeLion Sheet", - "version": 1 - }, - "coreMigrationVersion": "8.0.0", - "id": "190f3e90-2ec3-11e8-ba48-69fc4e41e1f6", - "references": [], - "type": "timelion-sheet", - "updated_at": "2018-03-23T17:53:30.872Z", - "version": "WzgsMl0=" -} - { "attributes": { "fields": "[{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", diff --git a/test/functional/apps/visualize/_markdown_vis.ts b/test/functional/apps/dashboard_elements/_markdown_vis.ts similarity index 100% rename from test/functional/apps/visualize/_markdown_vis.ts rename to test/functional/apps/dashboard_elements/_markdown_vis.ts diff --git a/test/functional/apps/dashboard_elements/index.ts b/test/functional/apps/dashboard_elements/index.ts new file mode 100644 index 0000000000000..4866754c3907b --- /dev/null +++ b/test/functional/apps/dashboard_elements/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + + describe('dashboard elements', () => { + before(async () => { + log.debug('Starting before method'); + await browser.setWindowSize(1280, 800); + await esArchiver.load('test/functional/fixtures/es_archiver/empty_kibana'); + + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/long_window_logstash'); + }); + + describe('dashboard elements ciGroup10', function () { + this.tags('ciGroup10'); + + loadTestFile(require.resolve('./input_control_vis')); + loadTestFile(require.resolve('./_markdown_vis')); + }); + }); +} diff --git a/test/functional/apps/visualize/input_control_vis/chained_controls.ts b/test/functional/apps/dashboard_elements/input_control_vis/chained_controls.ts similarity index 100% rename from test/functional/apps/visualize/input_control_vis/chained_controls.ts rename to test/functional/apps/dashboard_elements/input_control_vis/chained_controls.ts diff --git a/test/functional/apps/visualize/input_control_vis/dynamic_options.ts b/test/functional/apps/dashboard_elements/input_control_vis/dynamic_options.ts similarity index 100% rename from test/functional/apps/visualize/input_control_vis/dynamic_options.ts rename to test/functional/apps/dashboard_elements/input_control_vis/dynamic_options.ts diff --git a/test/functional/apps/visualize/input_control_vis/index.ts b/test/functional/apps/dashboard_elements/input_control_vis/index.ts similarity index 100% rename from test/functional/apps/visualize/input_control_vis/index.ts rename to test/functional/apps/dashboard_elements/input_control_vis/index.ts diff --git a/test/functional/apps/visualize/input_control_vis/input_control_options.ts b/test/functional/apps/dashboard_elements/input_control_vis/input_control_options.ts similarity index 100% rename from test/functional/apps/visualize/input_control_vis/input_control_options.ts rename to test/functional/apps/dashboard_elements/input_control_vis/input_control_options.ts diff --git a/test/functional/apps/visualize/input_control_vis/input_control_range.ts b/test/functional/apps/dashboard_elements/input_control_vis/input_control_range.ts similarity index 100% rename from test/functional/apps/visualize/input_control_vis/input_control_range.ts rename to test/functional/apps/dashboard_elements/input_control_vis/input_control_range.ts diff --git a/test/functional/apps/discover/_data_grid_doc_table.ts b/test/functional/apps/discover/_data_grid_doc_table.ts index 2efb1ba51811f..be569a2ffa25f 100644 --- a/test/functional/apps/discover/_data_grid_doc_table.ts +++ b/test/functional/apps/discover/_data_grid_doc_table.ts @@ -105,6 +105,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); + it('should allow paginating docs in the flyout by clicking in the doc table', async function () { + await retry.try(async function () { + await dataGrid.clickRowToggle({ rowIndex: rowToInspect - 1 }); + await testSubjects.exists(`dscDocNavigationPage0`); + await dataGrid.clickRowToggle({ rowIndex: rowToInspect }); + await testSubjects.exists(`dscDocNavigationPage1`); + await dataGrid.closeFlyout(); + }); + }); + it('should show allow adding columns from the detail panel', async function () { await retry.try(async function () { await dataGrid.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); diff --git a/test/functional/apps/timelion/_expression_typeahead.js b/test/functional/apps/timelion/_expression_typeahead.js deleted file mode 100644 index 3b29e9a44a77b..0000000000000 --- a/test/functional/apps/timelion/_expression_typeahead.js +++ /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 and the Server Side Public License, v 1; you may 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'; - -export default function ({ getPageObjects }) { - const PageObjects = getPageObjects(['common', 'timelion', 'settings', 'timePicker']); - - describe('expression typeahead', () => { - before(async () => { - await PageObjects.timelion.initTests(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - }); - - it('should show argument suggestions when function suggestion is selected', async () => { - await PageObjects.timelion.setExpression('.es'); - await PageObjects.timelion.clickSuggestion(); - const suggestions = await PageObjects.timelion.getSuggestionItemsText(); - expect(suggestions.length).to.eql(9); - expect(suggestions[0].includes('fit=')).to.eql(true); - }); - - it('should show argument value suggestions when argument is selected', async () => { - await PageObjects.timelion.setExpression('.legend'); - await PageObjects.timelion.clickSuggestion(); - const argumentSuggestions = await PageObjects.timelion.getSuggestionItemsText(); - expect(argumentSuggestions.length).to.eql(4); - expect(argumentSuggestions[1].includes('position=')).to.eql(true); - await PageObjects.timelion.clickSuggestion(1); - const valueSuggestions = await PageObjects.timelion.getSuggestionItemsText(); - expect(valueSuggestions.length).to.eql(5); - expect(valueSuggestions[0].includes('disable legend')).to.eql(true); - expect(valueSuggestions[1].includes('place legend in north east corner')).to.eql(true); - }); - - it('should display function suggestions filtered by function name', async () => { - await PageObjects.timelion.setExpression('.e'); - const suggestions = await PageObjects.timelion.getSuggestionItemsText(); - expect(suggestions.length).to.eql(2); - expect(suggestions[0].includes('.elasticsearch()')).to.eql(true); - expect(suggestions[1].includes('.es()')).to.eql(true); - }); - - describe('dynamic suggestions for argument values', () => { - describe('.es()', () => { - before(async () => { - await PageObjects.timelion.setExpression('.es'); - await PageObjects.timelion.clickSuggestion(); - }); - - it('should show index pattern suggestions for index argument', async () => { - await PageObjects.timelion.updateExpression('index='); - const suggestions = await PageObjects.timelion.getSuggestionItemsText(); - expect(suggestions.length).to.eql(1); - expect(suggestions[0].includes('logstash-*')).to.eql(true); - await PageObjects.timelion.clickSuggestion(); - }); - - it('should show field suggestions for timefield argument when index pattern set', async () => { - await PageObjects.timelion.updateExpression(',timefield='); - const suggestions = await PageObjects.timelion.getSuggestionItemsText(); - expect(suggestions.length).to.eql(4); - expect(suggestions[0].includes('@timestamp')).to.eql(true); - await PageObjects.timelion.clickSuggestion(); - }); - - it('should show field suggestions for split argument when index pattern set', async () => { - await PageObjects.timelion.updateExpression(',split='); - const suggestions = await PageObjects.timelion.getSuggestionItemsText(); - expect(suggestions.length).not.to.eql(0); - expect(suggestions[0].includes('@message.raw')).to.eql(true); - await PageObjects.timelion.clickSuggestion(10); - }); - - it('should show field suggestions for metric argument when index pattern set', async () => { - await PageObjects.timelion.updateExpression(',metric='); - await PageObjects.timelion.updateExpression('avg:'); - await PageObjects.timelion.clickSuggestion(0); - const suggestions = await PageObjects.timelion.getSuggestionItemsText(); - expect(suggestions.length).not.to.eql(0); - expect(suggestions[0].includes('avg:bytes')).to.eql(true); - }); - }); - }); - }); -} diff --git a/test/functional/apps/timelion/index.js b/test/functional/apps/timelion/index.js deleted file mode 100644 index b81a0e70d8a6d..0000000000000 --- a/test/functional/apps/timelion/index.js +++ /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 and the Server Side Public License, v 1; you may 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 default function ({ getService, loadTestFile }) { - const browser = getService('browser'); - const log = getService('log'); - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - - describe('timelion app', function () { - this.tags('ciGroup1'); - - before(async function () { - log.debug('Starting timelion before method'); - await browser.setWindowSize(1280, 800); - await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); - }); - - loadTestFile(require.resolve('./_expression_typeahead')); - }); -} diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/_timelion.ts index a3f2c87424244..ea8cb8b13ba49 100644 --- a/test/functional/apps/visualize/_timelion.ts +++ b/test/functional/apps/visualize/_timelion.ts @@ -10,11 +10,13 @@ import expect from '@kbn/expect'; import type { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { - const { timePicker, visChart, visEditor, visualize } = getPageObjects([ + const { timePicker, visChart, visEditor, visualize, timelion, common } = getPageObjects([ 'timePicker', 'visChart', 'visEditor', 'visualize', + 'timelion', + 'common', ]); const monacoEditor = getService('monacoEditor'); const kibanaServer = getService('kibanaServer'); @@ -230,6 +232,72 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); + describe('expression typeahead', () => { + it('should display function suggestions', async () => { + await monacoEditor.setCodeEditorValue(''); + await monacoEditor.typeCodeEditorValue('.e', 'timelionCodeEditor'); + // wait for monaco editor model will be updated with new value + await common.sleep(300); + let value = await monacoEditor.getCodeEditorValue(0); + expect(value).to.eql('.e'); + const suggestions = await timelion.getSuggestionItemsText(); + expect(suggestions.length).to.eql(2); + expect(suggestions[0].includes('es')).to.eql(true); + expect(suggestions[1].includes('elasticsearch')).to.eql(true); + await timelion.clickSuggestion(0); + // wait for monaco editor model will be updated with new value + await common.sleep(300); + value = await monacoEditor.getCodeEditorValue(0); + expect(value).to.eql('.es()'); + }); + + describe('dynamic suggestions for argument values', () => { + describe('.es()', () => { + it('should show index pattern suggestions for index argument', async () => { + await monacoEditor.setCodeEditorValue(''); + await monacoEditor.typeCodeEditorValue('.es(index=', 'timelionCodeEditor'); + // wait for index patterns will be loaded + await common.sleep(500); + const suggestions = await timelion.getSuggestionItemsText(); + expect(suggestions.length).not.to.eql(0); + expect(suggestions[0].includes('log')).to.eql(true); + }); + + it('should show field suggestions for timefield argument when index pattern set', async () => { + await monacoEditor.setCodeEditorValue(''); + await monacoEditor.typeCodeEditorValue( + '.es(index=logstash-*, timefield=', + 'timelionCodeEditor' + ); + const suggestions = await timelion.getSuggestionItemsText(); + expect(suggestions.length).to.eql(4); + expect(suggestions[0].includes('@timestamp')).to.eql(true); + }); + + it('should show field suggestions for split argument when index pattern set', async () => { + await monacoEditor.setCodeEditorValue(''); + await monacoEditor.typeCodeEditorValue( + '.es(index=logstash-*, timefield=@timestamp ,split=', + 'timelionCodeEditor' + ); + const suggestions = await timelion.getSuggestionItemsText(); + expect(suggestions.length).not.to.eql(0); + expect(suggestions[0].includes('@message.raw')).to.eql(true); + }); + + it('should show field suggestions for metric argument when index pattern set', async () => { + await monacoEditor.typeCodeEditorValue( + '.es(index=logstash-*, timefield=@timestamp ,metric=avg:', + 'timelionCodeEditor' + ); + const suggestions = await timelion.getSuggestionItemsText(); + expect(suggestions.length).not.to.eql(0); + expect(suggestions[0].includes('avg:bytes')).to.eql(true); + }); + }); + }); + }); + after( async () => await kibanaServer.uiSettings.update({ diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index d6862487196f0..c7f228e9aa05c 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -17,11 +17,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const security = getService('security'); - const { timePicker, visChart, visualBuilder, visualize } = getPageObjects([ + const { timePicker, visChart, visualBuilder, visualize, settings } = getPageObjects([ 'timePicker', 'visChart', 'visualBuilder', 'visualize', + 'settings', ]); describe('visual builder', function describeIndexTests() { @@ -174,6 +175,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should display correct data for max aggregation with entire time range mode', async () => { await visualBuilder.selectAggType('Max'); await visualBuilder.setFieldForAggregation('bytes'); + await visualBuilder.clickSeriesOption(); + await visualBuilder.changeDataFormatter('number'); const gaugeLabel = await visualBuilder.getGaugeLabel(); const gaugeCount = await visualBuilder.getGaugeCount(); @@ -269,6 +272,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should display correct data for sum of squares aggregation with entire time range mode', async () => { await visualBuilder.selectAggType('Sum of squares'); await visualBuilder.setFieldForAggregation('bytes'); + await visualBuilder.clickSeriesOption(); + await visualBuilder.changeDataFormatter('number'); await visualBuilder.clickPanelOptions('topN'); await visualBuilder.setMetricsDataTimerangeMode('Entire time range'); @@ -452,5 +457,118 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(legendItems3).to.eql(finalLegendItems); }); }); + + describe('applying field formats from Advanced Settings', () => { + const toggleSetFormatForMachineOsRaw = async () => { + log.debug( + 'Navigate to Advanced Settings Index Patterns and toggle Set Format for machine.os.raw' + ); + await settings.navigateTo(); + await settings.clickKibanaIndexPatterns(); + await settings.clickIndexPatternLogstash(); + await settings.openControlsByName('machine.os.raw'); + await settings.toggleRow('formatRow'); + }; + + before(async () => { + log.debug('Toggle on Set Format for machine.os.raw and set it to the title case'); + await toggleSetFormatForMachineOsRaw(); + await settings.setFieldFormat('string'); + await settings.setScriptedFieldStringTransform('title'); + await settings.controlChangeSave(); + }); + + beforeEach(async () => { + await visualBuilder.resetPage(); + await visualBuilder.selectAggType('Average'); + await visualBuilder.setFieldForAggregation('bytes'); + await visualBuilder.setMetricsGroupByTerms('machine.os.raw'); + await visChart.waitForVisualizationRenderingStabilized(); + }); + + it('should display title field formatted labels with byte field formatted values by default', async () => { + const expectedLegendItems = [ + 'Win 8: 4.968KB', + 'Win Xp: 4.23KB', + 'Win 7: 6.181KB', + 'Ios: 5.84KB', + 'Osx: 5.928KB', + ]; + + const legendItems = await visualBuilder.getLegendItemsContent(); + expect(legendItems).to.eql(expectedLegendItems); + }); + + it('should display title field formatted labels with raw values', async () => { + const expectedLegendItems = [ + 'Win 8: 5,087.5', + 'Win Xp: 4,332', + 'Win 7: 6,328.938', + 'Ios: 5,980', + 'Osx: 6,070', + ]; + await visualBuilder.clickSeriesOption(); + await visualBuilder.changeDataFormatter('number'); + const legendItems = await visualBuilder.getLegendItemsContent(); + + expect(legendItems).to.eql(expectedLegendItems); + }); + + it('should display title field formatted labels with TSVB formatted values', async () => { + const expectedLegendItems = [ + 'Win 8: 5,087.5 format', + 'Win Xp: 4,332 format', + 'Win 7: 6,328.938 format', + 'Ios: 5,980 format', + 'Osx: 6,070 format', + ]; + + await visualBuilder.clickSeriesOption(); + await visualBuilder.changeDataFormatter('number'); + await visualBuilder.enterSeriesTemplate('{{value}} format'); + await visChart.waitForVisualizationRenderingStabilized(); + + const legendItems = await visualBuilder.getLegendItemsContent(); + expect(legendItems).to.eql(expectedLegendItems); + }); + + describe('formatting values for Metric, TopN and Gauge', () => { + it('should display field formatted value for Metric', async () => { + await visualBuilder.clickMetric(); + await visualBuilder.checkMetricTabIsPresent(); + + const metricValue = await visualBuilder.getMetricValue(); + expect(metricValue).to.eql('5.514KB'); + }); + + it('should display field formatted label and value for TopN', async () => { + await visualBuilder.clickTopN(); + await visualBuilder.checkTopNTabIsPresent(); + + const topNLabel = await visualBuilder.getTopNLabel(); + const topNCount = await visualBuilder.getTopNCount(); + + expect(topNLabel).to.eql('Win 7'); + expect(topNCount).to.eql('5.664KB'); + }); + + it('should display field formatted label and value for Gauge', async () => { + await visualBuilder.clickGauge(); + await visualBuilder.checkGaugeTabIsPresent(); + + const gaugeLabel = await visualBuilder.getGaugeLabel(); + const gaugeCount = await visualBuilder.getGaugeCount(); + + expect(gaugeLabel).to.eql('Average of bytes'); + expect(gaugeCount).to.eql('5.514KB'); + }); + }); + + after(async () => { + log.debug('Toggle off Set Format for machine.os.raw'); + await toggleSetFormatForMachineOsRaw(); + await settings.controlChangeSave(); + }); + }); }); } diff --git a/test/functional/apps/visualize/_tsvb_markdown.ts b/test/functional/apps/visualize/_tsvb_markdown.ts index b8b74d5cd7bf3..98ed05d854f0c 100644 --- a/test/functional/apps/visualize/_tsvb_markdown.ts +++ b/test/functional/apps/visualize/_tsvb_markdown.ts @@ -146,6 +146,31 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(aggregationLength).to.be.equal(2); }); }); + + describe('applying field formats from Advanced Settings for values', () => { + before(async () => { + await visualBuilder.resetPage(); + await visualBuilder.clickMarkdown(); + await visualBuilder.markdownSwitchSubTab('markdown'); + await visualBuilder.enterMarkdown('{{ average_of_bytes.last.formatted }}'); + await visualBuilder.markdownSwitchSubTab('data'); + await visualBuilder.selectAggType('Average'); + await visualBuilder.setFieldForAggregation('bytes'); + await visualBuilder.clickSeriesOption(); + }); + + it('should apply field formatting by default', async () => { + const text = await visualBuilder.getMarkdownText(); + expect(text).to.be('5.588KB'); + }); + + it('should apply TSVB formatting', async () => { + await visualBuilder.changeDataFormatter('percent'); + + const text = await visualBuilder.getMarkdownText(); + expect(text).to.be('572,241.265%'); + }); + }); }); }); } diff --git a/test/functional/apps/visualize/_tsvb_table.ts b/test/functional/apps/visualize/_tsvb_table.ts index 7c093b5a9640a..ed668e4bca8e5 100644 --- a/test/functional/apps/visualize/_tsvb_table.ts +++ b/test/functional/apps/visualize/_tsvb_table.ts @@ -11,10 +11,11 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getPageObjects, getService }: FtrProviderContext) { - const { visualBuilder, visualize, visChart } = getPageObjects([ + const { visualBuilder, visualize, visChart, settings } = getPageObjects([ 'visualBuilder', 'visualize', 'visChart', + 'settings', ]); const findService = getService('find'); const retry = getService('retry'); @@ -45,6 +46,19 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(tableData).to.be(EXPECTED); }); + it('should display drilldown urls', async () => { + const baseURL = 'http://elastic.co/foo/'; + + await visualBuilder.clickPanelOptions('table'); + await visualBuilder.setDrilldownUrl(`${baseURL}{{key}}`); + + await retry.try(async () => { + const links = await findService.allByCssSelector(`a[href="${baseURL}ios"]`); + + expect(links.length).to.be(1); + }); + }); + it('should display correct values on changing metrics aggregation', async () => { const EXPECTED = 'OS Cardinality\nwin 8 12\nwin xp 9\nwin 7 8\nios 5\nosx 3'; @@ -71,6 +85,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'OS Variance of bytes\nwin 8 2,707,941.822\nwin xp 2,595,612.24\nwin 7 16,055,541.306\nios 6,505,206.56\nosx 1,016,620.667'; await visualBuilder.selectAggType('Variance'); await visualBuilder.setFieldForAggregation('bytes'); + await visualBuilder.clickSeriesOption(); + await visualBuilder.changeDataFormatter('number'); const tableData = await visualBuilder.getViewTable(); expect(tableData).to.be(EXPECTED); @@ -122,6 +138,63 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(tableData).to.be(EXPECTED); }); + describe('applying field formats from Advanced Settings', () => { + const toggleSetFormatForMachineOsRaw = async () => { + await settings.navigateTo(); + await settings.clickKibanaIndexPatterns(); + await settings.clickIndexPatternLogstash(); + await settings.openControlsByName('machine.os.raw'); + await settings.toggleRow('formatRow'); + }; + + before(async () => { + await toggleSetFormatForMachineOsRaw(); + await settings.setFieldFormat('string'); + await settings.setScriptedFieldStringTransform('upper'); + await settings.controlChangeSave(); + }); + + beforeEach(async () => { + await visualBuilder.selectAggType('Average'); + await visualBuilder.setFieldForAggregation('bytes'); + }); + + it('should display field formatted row labels with field formatted data by default', async () => { + const expected = + 'OS Average of bytes\nWIN 8 6.786KB\nWIN XP 3.804KB\nWIN 7 6.596KB\nIOS 4.844KB\nOSX 3.06KB'; + + const tableData = await visualBuilder.getViewTable(); + expect(tableData).to.be(expected); + }); + + it('should display field formatted row labels with raw data', async () => { + const expected = + 'OS Average of bytes\nWIN 8 6,948.846\nWIN XP 3,895.6\nWIN 7 6,753.833\nIOS 4,960.2\nOSX 3,133'; + + await visualBuilder.clickSeriesOption(); + await visualBuilder.changeDataFormatter('number'); + + const tableData = await visualBuilder.getViewTable(); + expect(tableData).to.be(expected); + }); + + it('should display field formatted row labels with TSVB formatted data', async () => { + const expected = + 'OS Average of bytes\nWIN 8 694,884.615%\nWIN XP 389,560%\nWIN 7 675,383.333%\nIOS 496,020%\nOSX 313,300%'; + + await visualBuilder.clickSeriesOption(); + await visualBuilder.changeDataFormatter('percent'); + + const tableData = await visualBuilder.getViewTable(); + expect(tableData).to.be(expected); + }); + + after(async () => { + await toggleSetFormatForMachineOsRaw(); + await settings.controlChangeSave(); + }); + }); + it('should display drilldown urls', async () => { const baseURL = 'http://elastic.co/foo/'; diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index 4733efade69e1..21bee2d16442f 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/_tsvb_time_series.ts @@ -89,6 +89,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const expectedLegendValue = '$ 156'; await visualBuilder.clickSeriesOption(); + await visualBuilder.changeDataFormatter('number'); await visualBuilder.enterSeriesTemplate('$ {{value}}'); await retry.try(async () => { const actualCount = await visualBuilder.getRhythmChartLegendValue(); @@ -100,7 +101,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const expectedLegendValue = '15,600%'; await visualBuilder.clickSeriesOption(); - await visualBuilder.changeDataFormatter('Percent'); + await visualBuilder.changeDataFormatter('percent'); const actualCount = await visualBuilder.getRhythmChartLegendValue(); expect(actualCount).to.be(expectedLegendValue); }); @@ -109,14 +110,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const expectedLegendValue = '156B'; await visualBuilder.clickSeriesOption(); - await visualBuilder.changeDataFormatter('Bytes'); + await visualBuilder.changeDataFormatter('bytes'); const actualCount = await visualBuilder.getRhythmChartLegendValue(); expect(actualCount).to.be(expectedLegendValue); }); it('should show the correct count in the legend with "Human readable" duration formatter', async () => { await visualBuilder.clickSeriesOption(); - await visualBuilder.changeDataFormatter('Duration'); + await visualBuilder.changeDataFormatter('duration'); await visualBuilder.setDurationFormatterSettings({ to: 'Human readable' }); const actualCountDefault = await visualBuilder.getRhythmChartLegendValue(); expect(actualCountDefault).to.be('a few seconds'); diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index 112b6a9afd264..dff57e6b96265 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -70,7 +70,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_experimental_vis')); loadTestFile(require.resolve('./_gauge_chart')); loadTestFile(require.resolve('./_heatmap_chart')); - loadTestFile(require.resolve('./input_control_vis')); loadTestFile(require.resolve('./_histogram_request_start')); loadTestFile(require.resolve('./_metric_chart')); }); @@ -79,7 +78,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { this.tags('ciGroup4'); loadTestFile(require.resolve('./_pie_chart')); - loadTestFile(require.resolve('./_markdown_vis')); loadTestFile(require.resolve('./_shared_item')); loadTestFile(require.resolve('./_lab_mode')); loadTestFile(require.resolve('./_linked_saved_searches')); diff --git a/test/functional/config.js b/test/functional/config.js index 221c2e6c1f1c1..97b3d85a8e243 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -19,12 +19,12 @@ export default async function ({ readConfigFile }) { require.resolve('./apps/console'), require.resolve('./apps/context'), require.resolve('./apps/dashboard'), + require.resolve('./apps/dashboard_elements'), require.resolve('./apps/discover'), require.resolve('./apps/getting_started'), require.resolve('./apps/home'), require.resolve('./apps/management'), require.resolve('./apps/saved_objects_management'), - require.resolve('./apps/timelion'), require.resolve('./apps/visualize'), ], pageObjects, @@ -90,9 +90,6 @@ export default async function ({ readConfigFile }) { settings: { pathname: '/app/management', }, - timelion: { - pathname: '/app/timelion', - }, console: { pathname: '/app/dev_tools', hash: '/console', diff --git a/test/functional/fixtures/es_archiver/dashboard/current/kibana/mappings.json b/test/functional/fixtures/es_archiver/dashboard/current/kibana/mappings.json index b6e225951c545..161d733e868a8 100644 --- a/test/functional/fixtures/es_archiver/dashboard/current/kibana/mappings.json +++ b/test/functional/fixtures/es_archiver/dashboard/current/kibana/mappings.json @@ -29,7 +29,6 @@ "search": "db2c00e39b36f40930a3b9fc71c823e1", "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -373,47 +372,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "dynamic": "false", "type": "object" diff --git a/test/functional/fixtures/es_archiver/dashboard/legacy/mappings.json b/test/functional/fixtures/es_archiver/dashboard/legacy/mappings.json index 45b2508d38033..451369d85acd8 100644 --- a/test/functional/fixtures/es_archiver/dashboard/legacy/mappings.json +++ b/test/functional/fixtures/es_archiver/dashboard/legacy/mappings.json @@ -137,48 +137,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/deprecations_service/mappings.json b/test/functional/fixtures/es_archiver/deprecations_service/mappings.json index 5159b946e082f..2d86b863d47dc 100644 --- a/test/functional/fixtures/es_archiver/deprecations_service/mappings.json +++ b/test/functional/fixtures/es_archiver/deprecations_service/mappings.json @@ -29,7 +29,6 @@ "search": "db2c00e39b36f40930a3b9fc71c823e1", "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -331,47 +330,6 @@ "dynamic": "false", "type": "object" }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/discover/mappings.json b/test/functional/fixtures/es_archiver/discover/mappings.json index 93d724aa55603..33bc746c84c8c 100644 --- a/test/functional/fixtures/es_archiver/discover/mappings.json +++ b/test/functional/fixtures/es_archiver/discover/mappings.json @@ -29,7 +29,6 @@ "search": "db2c00e39b36f40930a3b9fc71c823e1", "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -346,47 +345,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/empty_kibana/mappings.json b/test/functional/fixtures/es_archiver/empty_kibana/mappings.json index 264096beb11ee..7082f43f45e99 100644 --- a/test/functional/fixtures/es_archiver/empty_kibana/mappings.json +++ b/test/functional/fixtures/es_archiver/empty_kibana/mappings.json @@ -146,48 +146,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/invalid_scripted_field/mappings.json b/test/functional/fixtures/es_archiver/invalid_scripted_field/mappings.json index 63cc283f96d32..0d41e0ce86c14 100644 --- a/test/functional/fixtures/es_archiver/invalid_scripted_field/mappings.json +++ b/test/functional/fixtures/es_archiver/invalid_scripted_field/mappings.json @@ -143,47 +143,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/kibana_sample_data_flights_index_pattern/mappings.json b/test/functional/fixtures/es_archiver/kibana_sample_data_flights_index_pattern/mappings.json index caa1a9d8ddc11..f980596200b25 100644 --- a/test/functional/fixtures/es_archiver/kibana_sample_data_flights_index_pattern/mappings.json +++ b/test/functional/fixtures/es_archiver/kibana_sample_data_flights_index_pattern/mappings.json @@ -19,7 +19,6 @@ "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "server": "ec97f1c5da1a19609a60874e5af1100c", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", @@ -245,47 +244,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/management/mappings.json b/test/functional/fixtures/es_archiver/management/mappings.json index 45b2508d38033..451369d85acd8 100644 --- a/test/functional/fixtures/es_archiver/management/mappings.json +++ b/test/functional/fixtures/es_archiver/management/mappings.json @@ -137,48 +137,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/mgmt/mappings.json b/test/functional/fixtures/es_archiver/mgmt/mappings.json index aefbd9d0ccc8a..f4962f9c47668 100644 --- a/test/functional/fixtures/es_archiver/mgmt/mappings.json +++ b/test/functional/fixtures/es_archiver/mgmt/mappings.json @@ -172,47 +172,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/saved_objects_imports/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_imports/mappings.json index 45b2508d38033..451369d85acd8 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_imports/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_imports/mappings.json @@ -137,48 +137,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json index 05ca4d8e8307e..bb863dc24c585 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json @@ -358,47 +358,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/export_exclusion/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/export_exclusion/mappings.json index abec2eeb77492..d0101f16f85d0 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/export_exclusion/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/export_exclusion/mappings.json @@ -387,47 +387,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json index b2385a281dd23..9562b381a40f8 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json @@ -29,7 +29,6 @@ "search": "db2c00e39b36f40930a3b9fc71c823e1", "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -370,47 +369,6 @@ "dynamic": "false", "type": "object" }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json index d59f3b00d4818..780fdda5f7cbe 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json @@ -29,7 +29,6 @@ "search": "db2c00e39b36f40930a3b9fc71c823e1", "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -378,47 +377,6 @@ "dynamic": "false", "type": "object" }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json index 61763f55c1b6a..adcf4164668d6 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json @@ -389,47 +389,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json index b2385a281dd23..9562b381a40f8 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json @@ -29,7 +29,6 @@ "search": "db2c00e39b36f40930a3b9fc71c823e1", "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -370,47 +369,6 @@ "dynamic": "false", "type": "object" }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json index aba581867bb8a..0679717650af9 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json @@ -372,47 +372,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/timelion/mappings.json b/test/functional/fixtures/es_archiver/timelion/mappings.json index 45b2508d38033..451369d85acd8 100644 --- a/test/functional/fixtures/es_archiver/timelion/mappings.json +++ b/test/functional/fixtures/es_archiver/timelion/mappings.json @@ -137,48 +137,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/visualize_embedding/mappings.json b/test/functional/fixtures/es_archiver/visualize_embedding/mappings.json index 45b2508d38033..451369d85acd8 100644 --- a/test/functional/fixtures/es_archiver/visualize_embedding/mappings.json +++ b/test/functional/fixtures/es_archiver/visualize_embedding/mappings.json @@ -137,48 +137,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/visualize_source-filters/mappings.json b/test/functional/fixtures/es_archiver/visualize_source-filters/mappings.json index 45b2508d38033..451369d85acd8 100644 --- a/test/functional/fixtures/es_archiver/visualize_source-filters/mappings.json +++ b/test/functional/fixtures/es_archiver/visualize_source-filters/mappings.json @@ -137,48 +137,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/fixtures/es_archiver/visualize_source_filters/mappings.json b/test/functional/fixtures/es_archiver/visualize_source_filters/mappings.json index 5ac113e7e4b74..ec6a9ce7f13a1 100644 --- a/test/functional/fixtures/es_archiver/visualize_source_filters/mappings.json +++ b/test/functional/fixtures/es_archiver/visualize_source_filters/mappings.json @@ -155,48 +155,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/test/functional/page_objects/timelion_page.ts b/test/functional/page_objects/timelion_page.ts index 65584d98022be..bdfde3c8145e5 100644 --- a/test/functional/page_objects/timelion_page.ts +++ b/test/functional/page_objects/timelion_page.ts @@ -10,71 +10,21 @@ import { FtrService } from '../ftr_provider_context'; export class TimelionPageObject extends FtrService { private readonly testSubjects = this.ctx.getService('testSubjects'); - private readonly log = this.ctx.getService('log'); - private readonly common = this.ctx.getPageObject('common'); - private readonly esArchiver = this.ctx.getService('esArchiver'); - private readonly kibanaServer = this.ctx.getService('kibanaServer'); - - public async initTests() { - await this.kibanaServer.uiSettings.replace({ - defaultIndex: 'logstash-*', - }); - - this.log.debug('load kibana index'); - await this.esArchiver.load('test/functional/fixtures/es_archiver/timelion'); - - await this.common.navigateToApp('timelion'); - } - - public async setExpression(expression: string) { - const input = await this.testSubjects.find('timelionExpressionTextArea'); - await input.clearValue(); - await input.type(expression); - } - - public async updateExpression(updates: string) { - const input = await this.testSubjects.find('timelionExpressionTextArea'); - await input.type(updates); - await this.common.sleep(1000); - } - - public async getExpression() { - const input = await this.testSubjects.find('timelionExpressionTextArea'); - return input.getVisibleText(); - } public async getSuggestionItemsText() { - const elements = await this.testSubjects.findAll('timelionSuggestionListItem'); - return await Promise.all(elements.map(async (element) => await element.getVisibleText())); + const timelionCodeEditor = await this.testSubjects.find('timelionCodeEditor'); + const lists = await timelionCodeEditor.findAllByClassName('monaco-list-row'); + return await Promise.all(lists.map(async (element) => await element.getVisibleText())); } - public async clickSuggestion(suggestionIndex = 0, waitTime = 1000) { - const elements = await this.testSubjects.findAll('timelionSuggestionListItem'); - if (suggestionIndex > elements.length) { + public async clickSuggestion(suggestionIndex = 0) { + const timelionCodeEditor = await this.testSubjects.find('timelionCodeEditor'); + const lists = await timelionCodeEditor.findAllByCssSelector('.monaco-list-row'); + if (suggestionIndex > lists.length) { throw new Error( - `Unable to select suggestion ${suggestionIndex}, only ${elements.length} suggestions available.` + `Unable to select suggestion ${suggestionIndex}, only ${lists.length} suggestions available.` ); } - await elements[suggestionIndex].click(); - // Wait for timelion expression to be updated after clicking suggestions - await this.common.sleep(waitTime); - } - - public async saveTimelionSheet() { - await this.testSubjects.click('timelionSaveButton'); - await this.testSubjects.click('timelionSaveAsSheetButton'); - await this.testSubjects.click('timelionFinishSaveButton'); - await this.testSubjects.existOrFail('timelionSaveSuccessToast'); - await this.testSubjects.waitForDeleted('timelionSaveSuccessToast'); - } - - public async expectWriteControls() { - await this.testSubjects.existOrFail('timelionSaveButton'); - await this.testSubjects.existOrFail('timelionDeleteButton'); - } - - public async expectMissingWriteControls() { - await this.testSubjects.missingOrFail('timelionSaveButton'); - await this.testSubjects.missingOrFail('timelionDeleteButton'); + await lists[suggestionIndex].click(); } } diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index c96faab2dc321..81b2e2763eb1d 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -270,13 +270,14 @@ export class VisualBuilderPageObject extends FtrService { /** * change the data formatter for template in an `options` label tab * - * @param formatter - typeof formatter which you can use for presenting data. By default kibana show `Number` formatter + * @param formatter - typeof formatter which you can use for presenting data. By default kibana show `Default` formatter */ public async changeDataFormatter( - formatter: 'Bytes' | 'Number' | 'Percent' | 'Duration' | 'Custom' + formatter: 'default' | 'bytes' | 'number' | 'percent' | 'duration' | 'custom' ) { - const formatterEl = await this.testSubjects.find('tsvbDataFormatPicker'); - await this.comboBox.setElement(formatterEl, formatter, { clickWithMouse: true }); + await this.testSubjects.click('tsvbDataFormatPicker'); + await this.testSubjects.click(`tsvbDataFormatPicker-${formatter}`); + await this.visChart.waitForVisualizationRenderingStabilized(); } public async setDrilldownUrl(value: string) { @@ -304,16 +305,16 @@ export class VisualBuilderPageObject extends FtrService { }) { if (from) { await this.retry.try(async () => { - const fromCombobox = await this.find.byCssSelector('[id$="from-row"] .euiComboBox'); - await this.comboBox.setElement(fromCombobox, from, { clickWithMouse: true }); + await this.comboBox.set('dataFormatPickerDurationFrom', from); }); } if (to) { - const toCombobox = await this.find.byCssSelector('[id$="to-row"] .euiComboBox'); - await this.comboBox.setElement(toCombobox, to, { clickWithMouse: true }); + await this.retry.try(async () => { + await this.comboBox.set('dataFormatPickerDurationTo', to); + }); } if (decimalPlaces) { - const decimalPlacesInput = await this.find.byCssSelector('[id$="decimal"]'); + const decimalPlacesInput = await this.testSubjects.find('dataFormatPickerDurationDecimal'); await decimalPlacesInput.type(decimalPlaces); } } diff --git a/test/functional/services/monaco_editor.ts b/test/functional/services/monaco_editor.ts index 90674e101fc4e..63a5a7105ddb8 100644 --- a/test/functional/services/monaco_editor.ts +++ b/test/functional/services/monaco_editor.ts @@ -11,6 +11,7 @@ import { FtrService } from '../ftr_provider_context'; export class MonacoEditorService extends FtrService { private readonly retry = this.ctx.getService('retry'); private readonly browser = this.ctx.getService('browser'); + private readonly testSubjects = this.ctx.getService('testSubjects'); public async getCodeEditorValue(nthIndex: number = 0) { let values: string[] = []; @@ -27,6 +28,12 @@ export class MonacoEditorService extends FtrService { return values[nthIndex] as string; } + public async typeCodeEditorValue(value: string, testSubjId: string) { + const editor = await this.testSubjects.find(testSubjId); + const textarea = await editor.findByCssSelector('textarea'); + textarea.type(value); + } + public async setCodeEditorValue(value: string, nthIndex = 0) { await this.retry.try(async () => { await this.browser.execute( diff --git a/test/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json b/test/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json index 9f5edaad0fe76..f010fcea90b3f 100644 --- a/test/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json +++ b/test/new_visualize_flow/fixtures/es_archiver/kibana/mappings.json @@ -23,7 +23,6 @@ "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -366,47 +365,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/plugins/apm/dev_docs/testing.md b/x-pack/plugins/apm/dev_docs/testing.md index 93f32111048c1..4d0edc27fe644 100644 --- a/x-pack/plugins/apm/dev_docs/testing.md +++ b/x-pack/plugins/apm/dev_docs/testing.md @@ -42,7 +42,7 @@ The API tests are located in `x-pack/test/apm_api_integration/`. node scripts/test/e2e [--trial] [--help] ``` -The E2E tests are located [here](../../ftr_e2e) +The E2E tests are located [here](../ftr_e2e) --- diff --git a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx index 3103be6c03379..dd94cf4b175a6 100644 --- a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx @@ -8,14 +8,20 @@ import { i18n } from '@kbn/i18n'; import { defaults, omit } from 'lodash'; import React from 'react'; -import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { ForLastExpression } from '../../../../../triggers_actions_ui/public'; +import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { asInteger } from '../../../../common/utils/formatters'; import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher'; import { useFetcher } from '../../../hooks/use_fetcher'; import { ChartPreview } from '../chart_preview'; import { EnvironmentField, IsAboveField, ServiceField } from '../fields'; -import { AlertMetadata, getIntervalAndTimeRange, TimeUnit } from '../helper'; +import { + AlertMetadata, + getIntervalAndTimeRange, + isNewApmRuleFromStackManagement, + TimeUnit, +} from '../helper'; +import { NewAlertEmptyPrompt } from '../new_alert_empty_prompt'; import { ServiceAlertTrigger } from '../service_alert_trigger'; export interface AlertParams { @@ -81,6 +87,10 @@ export function ErrorCountAlertTrigger(props: Props) { ] ); + if (isNewApmRuleFromStackManagement(alertParams, metadata)) { + return ; + } + const fields = [ , ) => { + event.preventDefault(); + if (apmUrl && navigateToUrl) { + navigateToUrl(apmUrl); + } + }; + + return ( + + {i18n.translate('xpack.apm.NewAlertEmptyPrompt.goToApmLinkText', { + defaultMessage: 'Go to APM', + })} + , + ]} + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx index ba32561ac3378..dbbb7186de65c 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx @@ -9,10 +9,10 @@ import { EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { defaults, map, omit } from 'lodash'; import React from 'react'; -import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { CoreStart } from '../../../../../../../src/core/public'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { ForLastExpression } from '../../../../../triggers_actions_ui/public'; +import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { getDurationFormatter } from '../../../../common/utils/formatters'; import { useServiceTransactionTypesFetcher } from '../../../context/apm_service/use_service_transaction_types_fetcher'; import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher'; @@ -29,7 +29,13 @@ import { ServiceField, TransactionTypeField, } from '../fields'; -import { AlertMetadata, getIntervalAndTimeRange, TimeUnit } from '../helper'; +import { + AlertMetadata, + getIntervalAndTimeRange, + isNewApmRuleFromStackManagement, + TimeUnit, +} from '../helper'; +import { NewAlertEmptyPrompt } from '../new_alert_empty_prompt'; import { ServiceAlertTrigger } from '../service_alert_trigger'; import { PopoverExpression } from '../service_alert_trigger/popover_expression'; @@ -154,6 +160,10 @@ export function TransactionDurationAlertTrigger(props: Props) { /> ); + if (isNewApmRuleFromStackManagement(alertParams, metadata)) { + return ; + } + if (!params.serviceName) { return null; } diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx index f72126284395c..7b3c30da44c08 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_anomaly_alert_trigger/index.tsx @@ -17,7 +17,8 @@ import { ServiceField, TransactionTypeField, } from '../fields'; -import { AlertMetadata } from '../helper'; +import { AlertMetadata, isNewApmRuleFromStackManagement } from '../helper'; +import { NewAlertEmptyPrompt } from '../new_alert_empty_prompt'; import { ServiceAlertTrigger } from '../service_alert_trigger'; import { PopoverExpression } from '../service_alert_trigger/popover_expression'; import { @@ -73,6 +74,10 @@ export function TransactionDurationAnomalyAlertTrigger(props: Props) { end: metadata?.end, }); + if (isNewApmRuleFromStackManagement(alertParams, metadata)) { + return ; + } + const fields = [ , ; + } + const fields = [ , + + + diff --git a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx index fbbb2e1ffedf4..c69623f92987a 100644 --- a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx +++ b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx @@ -80,7 +80,7 @@ function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) { display="plain" textAlign="left" title={i18n.translate('xpack.apm.tutorial.apmServer.fleet.title', { - defaultMessage: 'Elastic APM (beta) now available in Fleet!', + defaultMessage: 'Elastic APM now available in Fleet!', })} description={i18n.translate( 'xpack.apm.tutorial.apmServer.fleet.message', diff --git a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts index 78fc82393994b..79bb75af677ae 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable_types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MAP_SAVED_OBJECT_TYPE } from '../../../../plugins/maps/common/constants'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../plugins/maps/common'; import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../../src/plugins/visualizations/common/constants'; import { LENS_EMBEDDABLE_TYPE } from '../../../../plugins/lens/common/constants'; import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../src/plugins/discover/common'; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/embedded_map.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/embedded_map.tsx index 29131b4a3372a..13aab06640bd5 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/embedded_map.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/embedded_map/embedded_map.tsx @@ -8,8 +8,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { htmlIdGenerator } from '@elastic/eui'; -import { LayerDescriptor } from '../../../../../../maps/common/descriptor_types'; -import { INITIAL_LOCATION } from '../../../../../../maps/common/constants'; +import { INITIAL_LOCATION, LayerDescriptor } from '../../../../../../maps/common'; import { MapEmbeddable, MapEmbeddableInput, diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content/format_utils.ts b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content/format_utils.ts index ddc9255b4834d..b314741e95284 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content/format_utils.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content/format_utils.ts @@ -8,7 +8,7 @@ import { Feature, Point } from 'geojson'; import { euiPaletteColorBlind } from '@elastic/eui'; import { DEFAULT_GEO_REGEX } from './geo_point_content'; -import { SOURCE_TYPES } from '../../../../../../../maps/common/constants'; +import { SOURCE_TYPES } from '../../../../../../../maps/common'; export const convertWKTGeoToLonLat = ( value: string | number diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content_with_map/geo_point_content_with_map.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content_with_map/geo_point_content_with_map.tsx index b4c8c3c22f5a9..42245742f8b7d 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content_with_map/geo_point_content_with_map.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/expanded_row/geo_point_content_with_map/geo_point_content_with_map.tsx @@ -13,10 +13,9 @@ import { ExpandedRowContent } from '../../stats_table/components/field_data_expa import { DocumentStatsTable } from '../../stats_table/components/field_data_expanded_row/document_stats'; import { ExamplesList } from '../../examples_list'; import { FieldVisConfig } from '../../stats_table/types'; -import { LayerDescriptor } from '../../../../../../../maps/common/descriptor_types'; import { useDataVisualizerKibana } from '../../../../kibana_context'; import { JOB_FIELD_TYPES } from '../../../../../../common'; -import { ES_GEO_FIELD_TYPE } from '../../../../../../../maps/common'; +import { ES_GEO_FIELD_TYPE, LayerDescriptor } from '../../../../../../../maps/common'; import { EmbeddedMapComponent } from '../../embedded_map'; export const GeoPointContentWithMap: FC<{ diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx index 6bd4de22beca4..c2e1e0b7dffa9 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/choropleth_map.tsx @@ -14,9 +14,9 @@ import { SOURCE_TYPES, STYLE_TYPE, COLOR_MAP_TYPE, -} from '../../../../../../../../maps/common/constants'; + VectorLayerDescriptor, +} from '../../../../../../../../maps/common'; import { EMSTermJoinConfig } from '../../../../../../../../maps/public'; -import { VectorLayerDescriptor } from '../../../../../../../../maps/common/descriptor_types'; import { EmbeddedMapComponent } from '../../../embedded_map'; import { FieldVisStats } from '../../../../../../../common/types'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx index 4a3dbbc31faf3..c303190651f57 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx @@ -105,7 +105,7 @@ export const staticSourceData = [ hasOauthRedirect: true, needsBaseUrl: true, documentationUrl: CONFLUENCE_DOCS_URL, - applicationPortalUrl: 'https://developer.atlassian.com/apps/', + applicationPortalUrl: 'https://developer.atlassian.com/console/myapps/', }, objTypes: [ SOURCE_OBJ_TYPES.PAGES, @@ -320,11 +320,11 @@ export const staticSourceData = [ addPath: ADD_JIRA_PATH, editPath: EDIT_JIRA_PATH, configuration: { - isPublicKey: true, + isPublicKey: false, hasOauthRedirect: true, - needsBaseUrl: false, + needsBaseUrl: true, documentationUrl: JIRA_DOCS_URL, - applicationPortalUrl: '', + applicationPortalUrl: 'https://developer.atlassian.com/console/myapps/', }, objTypes: [ SOURCE_OBJ_TYPES.EPICS, diff --git a/x-pack/plugins/features/kibana.json b/x-pack/plugins/features/kibana.json index 867f9ced7ed88..550b3899061d4 100644 --- a/x-pack/plugins/features/kibana.json +++ b/x-pack/plugins/features/kibana.json @@ -7,7 +7,6 @@ "version": "8.0.0", "kibanaVersion": "kibana", "requiredPlugins": ["licensing"], - "optionalPlugins": ["visTypeTimelion"], "configPath": ["xpack", "features"], "server": true, "ui": true, diff --git a/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap b/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap index 954701f0087f4..85eb1f5b71e71 100644 --- a/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap +++ b/x-pack/plugins/features/server/__snapshots__/oss_features.test.ts.snap @@ -551,7 +551,6 @@ Array [ "index-pattern", "search", "visualization", - "timelion-sheet", "canvas-workpad", "lens", "map", @@ -584,7 +583,6 @@ Array [ "index-pattern", "search", "visualization", - "timelion-sheet", "canvas-workpad", "lens", "map", @@ -857,54 +855,6 @@ Array [ ] `; -exports[`buildOSSFeatures with a basic license returns the timelion feature augmented with appropriate sub feature privileges 1`] = ` -Array [ - Object { - "privilege": Object { - "app": Array [ - "timelion", - "kibana", - ], - "catalogue": Array [ - "timelion", - ], - "savedObject": Object { - "all": Array [ - "timelion-sheet", - ], - "read": Array [ - "index-pattern", - ], - }, - "ui": Array [ - "save", - ], - }, - "privilegeId": "all", - }, - Object { - "privilege": Object { - "app": Array [ - "timelion", - "kibana", - ], - "catalogue": Array [ - "timelion", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "timelion-sheet", - ], - }, - "ui": Array [], - }, - "privilegeId": "read", - }, -] -`; - exports[`buildOSSFeatures with a basic license returns the visualize feature augmented with appropriate sub feature privileges 1`] = ` Array [ Object { @@ -1081,7 +1031,6 @@ Array [ "index-pattern", "search", "visualization", - "timelion-sheet", "canvas-workpad", "lens", "map", @@ -1114,7 +1063,6 @@ Array [ "index-pattern", "search", "visualization", - "timelion-sheet", "canvas-workpad", "lens", "map", @@ -1387,54 +1335,6 @@ Array [ ] `; -exports[`buildOSSFeatures with a enterprise license returns the timelion feature augmented with appropriate sub feature privileges 1`] = ` -Array [ - Object { - "privilege": Object { - "app": Array [ - "timelion", - "kibana", - ], - "catalogue": Array [ - "timelion", - ], - "savedObject": Object { - "all": Array [ - "timelion-sheet", - ], - "read": Array [ - "index-pattern", - ], - }, - "ui": Array [ - "save", - ], - }, - "privilegeId": "all", - }, - Object { - "privilege": Object { - "app": Array [ - "timelion", - "kibana", - ], - "catalogue": Array [ - "timelion", - ], - "savedObject": Object { - "all": Array [], - "read": Array [ - "index-pattern", - "timelion-sheet", - ], - }, - "ui": Array [], - }, - "privilegeId": "read", - }, -] -`; - exports[`buildOSSFeatures with a enterprise license returns the visualize feature augmented with appropriate sub feature privileges 1`] = ` Array [ Object { diff --git a/x-pack/plugins/features/server/oss_features.test.ts b/x-pack/plugins/features/server/oss_features.test.ts index 39bb12d90ea1c..7a1c5951192ec 100644 --- a/x-pack/plugins/features/server/oss_features.test.ts +++ b/x-pack/plugins/features/server/oss_features.test.ts @@ -11,52 +11,10 @@ import { KibanaFeature } from '.'; import { LicenseType, LICENSE_TYPE } from '../../licensing/server'; describe('buildOSSFeatures', () => { - it('returns features including timelion', () => { - expect( - buildOSSFeatures({ - savedObjectTypes: ['foo', 'bar'], - includeTimelion: true, - includeReporting: false, - }).map((f) => f.id) - ).toMatchInlineSnapshot(` -Array [ - "discover", - "visualize", - "dashboard", - "dev_tools", - "advancedSettings", - "indexPatterns", - "savedObjectsManagement", - "timelion", -] -`); - }); - - it('returns features excluding timelion', () => { - expect( - buildOSSFeatures({ - savedObjectTypes: ['foo', 'bar'], - includeTimelion: false, - includeReporting: false, - }).map((f) => f.id) - ).toMatchInlineSnapshot(` -Array [ - "discover", - "visualize", - "dashboard", - "dev_tools", - "advancedSettings", - "indexPatterns", - "savedObjectsManagement", -] -`); - }); - it('returns features including reporting subfeatures', () => { expect( buildOSSFeatures({ savedObjectTypes: ['foo', 'bar'], - includeTimelion: false, includeReporting: true, }).map(({ id, subFeatures }) => ({ id, subFeatures })) ).toMatchSnapshot(); @@ -66,7 +24,6 @@ Array [ expect( buildOSSFeatures({ savedObjectTypes: ['foo', 'bar'], - includeTimelion: false, includeReporting: false, }).map(({ id, subFeatures }) => ({ id, subFeatures })) ).toMatchSnapshot(); @@ -74,7 +31,6 @@ Array [ const features = buildOSSFeatures({ savedObjectTypes: ['foo', 'bar'], - includeTimelion: true, includeReporting: false, }); features.forEach((featureConfig) => { diff --git a/x-pack/plugins/features/server/oss_features.ts b/x-pack/plugins/features/server/oss_features.ts index 14265948c2dee..7c8432b8d9ec7 100644 --- a/x-pack/plugins/features/server/oss_features.ts +++ b/x-pack/plugins/features/server/oss_features.ts @@ -11,13 +11,11 @@ import type { KibanaFeatureConfig, SubFeatureConfig } from '../common'; export interface BuildOSSFeaturesParams { savedObjectTypes: string[]; - includeTimelion: boolean; includeReporting: boolean; } export const buildOSSFeatures = ({ savedObjectTypes, - includeTimelion, includeReporting, }: BuildOSSFeaturesParams): KibanaFeatureConfig[] => { return [ @@ -203,7 +201,6 @@ export const buildOSSFeatures = ({ 'index-pattern', 'search', 'visualization', - 'timelion-sheet', 'canvas-workpad', 'lens', 'map', @@ -221,7 +218,6 @@ export const buildOSSFeatures = ({ 'index-pattern', 'search', 'visualization', - 'timelion-sheet', 'canvas-workpad', 'lens', 'map', @@ -450,39 +446,9 @@ export const buildOSSFeatures = ({ }, }, }, - ...(includeTimelion ? [timelionFeature] : []), ] as KibanaFeatureConfig[]; }; -const timelionFeature: KibanaFeatureConfig = { - id: 'timelion', - name: 'Timelion', - order: 350, - category: DEFAULT_APP_CATEGORIES.kibana, - app: ['timelion', 'kibana'], - catalogue: ['timelion'], - privileges: { - all: { - app: ['timelion', 'kibana'], - catalogue: ['timelion'], - savedObject: { - all: ['timelion-sheet'], - read: ['index-pattern'], - }, - ui: ['save'], - }, - read: { - app: ['timelion', 'kibana'], - catalogue: ['timelion'], - savedObject: { - all: [], - read: ['index-pattern', 'timelion-sheet'], - }, - ui: [], - }, - }, -}; - const reportingPrivilegeGroupName = i18n.translate( 'xpack.features.ossFeatures.reporting.reportingTitle', { diff --git a/x-pack/plugins/features/server/plugin.test.ts b/x-pack/plugins/features/server/plugin.test.ts index ba809187a549e..e5940bc73ae97 100644 --- a/x-pack/plugins/features/server/plugin.test.ts +++ b/x-pack/plugins/features/server/plugin.test.ts @@ -46,36 +46,7 @@ describe('Features Plugin', () => { it('returns OSS + registered kibana features', async () => { const plugin = new FeaturesPlugin(initContext); - const { registerKibanaFeature } = await plugin.setup(coreSetup, {}); - registerKibanaFeature({ - id: 'baz', - name: 'baz', - app: [], - category: { id: 'foo', label: 'foo' }, - privileges: null, - }); - - const { getKibanaFeatures } = plugin.start(coreStart); - - expect(getKibanaFeatures().map((f) => f.id)).toMatchInlineSnapshot(` - Array [ - "baz", - "discover", - "visualize", - "dashboard", - "dev_tools", - "advancedSettings", - "indexPatterns", - "savedObjectsManagement", - ] - `); - }); - - it('returns OSS + registered kibana features with timelion when available', async () => { - const plugin = new FeaturesPlugin(initContext); - const { registerKibanaFeature: registerFeature } = await plugin.setup(coreSetup, { - visTypeTimelion: { uiEnabled: true }, - }); + const { registerKibanaFeature: registerFeature } = await plugin.setup(coreSetup); registerFeature({ id: 'baz', name: 'baz', @@ -96,7 +67,6 @@ describe('Features Plugin', () => { "advancedSettings", "indexPatterns", "savedObjectsManagement", - "timelion", ] `); }); @@ -105,7 +75,7 @@ describe('Features Plugin', () => { typeRegistry.isHidden.mockReturnValueOnce(true); typeRegistry.isHidden.mockReturnValueOnce(false); const plugin = new FeaturesPlugin(initContext); - await plugin.setup(coreSetup, {}); + await plugin.setup(coreSetup); const { getKibanaFeatures } = plugin.start(coreStart); const soTypes = @@ -120,7 +90,7 @@ describe('Features Plugin', () => { it('returns registered elasticsearch features', async () => { const plugin = new FeaturesPlugin(initContext); - const { registerElasticsearchFeature } = await plugin.setup(coreSetup, {}); + const { registerElasticsearchFeature } = await plugin.setup(coreSetup); registerElasticsearchFeature({ id: 'baz', privileges: [ @@ -142,7 +112,7 @@ describe('Features Plugin', () => { it('registers a capabilities provider', async () => { const plugin = new FeaturesPlugin(initContext); - await plugin.setup(coreSetup, {}); + await plugin.setup(coreSetup); expect(coreSetup.capabilities.registerProvider).toHaveBeenCalledTimes(1); expect(coreSetup.capabilities.registerProvider).toHaveBeenCalledWith(expect.any(Function)); diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index b1f540031f6dc..c1fbc4b8a5edb 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -81,10 +81,6 @@ export interface PluginStartContract { getKibanaFeatures(): KibanaFeature[]; } -interface TimelionSetupContract { - uiEnabled: boolean; -} - /** * Represents Features Plugin instance that will be managed by the Kibana plugin system. */ @@ -93,19 +89,13 @@ export class FeaturesPlugin Plugin, RecursiveReadonly> { private readonly logger: Logger; private readonly featureRegistry: FeatureRegistry = new FeatureRegistry(); - private isTimelionEnabled: boolean = false; private isReportingEnabled: boolean = false; constructor(private readonly initializerContext: PluginInitializerContext) { this.logger = this.initializerContext.logger.get(); } - public setup( - core: CoreSetup, - { visTypeTimelion }: { visTypeTimelion?: TimelionSetupContract } - ): RecursiveReadonly { - this.isTimelionEnabled = visTypeTimelion !== undefined && visTypeTimelion.uiEnabled; - + public setup(core: CoreSetup): RecursiveReadonly { defineRoutes({ router: core.http.createRouter(), featureRegistry: this.featureRegistry, @@ -160,14 +150,8 @@ export class FeaturesPlugin new Set([...savedObjectVisibleTypes, ...savedObjectImportableAndExportableHiddenTypes]) ); - this.logger.debug( - `Registering OSS features with SO types: ${savedObjectTypes.join(', ')}. "includeTimelion": ${ - this.isTimelionEnabled - }.` - ); const features = buildOSSFeatures({ savedObjectTypes, - includeTimelion: this.isTimelionEnabled, includeReporting: this.isReportingEnabled, }); diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json index cc732e67995ba..410a5e2c160d6 100644 --- a/x-pack/plugins/graph/kibana.json +++ b/x-pack/plugins/graph/kibana.json @@ -4,12 +4,29 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["licensing", "data", "navigation", "savedObjects", "kibanaLegacy"], - "optionalPlugins": ["home", "features"], - "configPath": ["xpack", "graph"], - "requiredBundles": ["kibanaUtils", "kibanaReact", "home"], + "requiredPlugins": [ + "licensing", + "data", + "navigation", + "savedObjects", + "kibanaLegacy" + ], + "optionalPlugins": [ + "home", + "features", + "spaces" + ], + "configPath": [ + "xpack", + "graph" + ], + "requiredBundles": [ + "kibanaUtils", + "kibanaReact", + "home" + ], "owner": { "name": "Data Discovery", "githubTeam": "kibana-data-discovery" } -} +} \ No newline at end of file diff --git a/x-pack/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.ts index 7461a7b5fc172..fc6c6170509d9 100644 --- a/x-pack/plugins/graph/public/application.ts +++ b/x-pack/plugins/graph/public/application.ts @@ -31,6 +31,7 @@ import './index.scss'; import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public'; import { GraphSavePolicy } from './types'; import { graphRouter } from './router'; +import { SpacesApi } from '../../spaces/public'; /** * These are dependencies of the Graph app besides the base dependencies @@ -63,6 +64,7 @@ export interface GraphDependencies { setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; uiSettings: IUiSettingsClient; history: ScopedHistory; + spaces?: SpacesApi; } export type GraphServices = Omit; diff --git a/x-pack/plugins/graph/public/apps/workspace_route.tsx b/x-pack/plugins/graph/public/apps/workspace_route.tsx index f4158238c33c6..55f481bf504f1 100644 --- a/x-pack/plugins/graph/public/apps/workspace_route.tsx +++ b/x-pack/plugins/graph/public/apps/workspace_route.tsx @@ -8,7 +8,7 @@ import React, { useMemo, useRef, useState } from 'react'; import { I18nProvider } from '@kbn/i18n/react'; import { Provider } from 'react-redux'; -import { useHistory, useLocation } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { showSaveModal } from '../../../../../src/plugins/saved_objects/public'; import { Workspace } from '../types'; @@ -40,6 +40,7 @@ export const WorkspaceRoute = ({ getBasePath, addBasePath, setHeaderActionMenu, + spaces, indexPatterns: getIndexPatternProvider, }, }: WorkspaceRouteProps) => { @@ -56,7 +57,6 @@ export const WorkspaceRoute = ({ */ const [renderCounter, setRenderCounter] = useState(0); const history = useHistory(); - const urlQuery = new URLSearchParams(useLocation().search).get('query'); const indexPatternProvider = useMemo( () => createCachedIndexPatternProvider(getIndexPatternProvider.get), @@ -114,22 +114,27 @@ export const WorkspaceRoute = ({ }) ); - const { savedWorkspace, indexPatterns } = useWorkspaceLoader({ + const loaded = useWorkspaceLoader({ workspaceRef, store, savedObjectsClient, - toastNotifications, + spaces, + coreStart, }); - if (!savedWorkspace || !indexPatterns) { + if (!loaded) { return null; } + const { savedWorkspace, indexPatterns, sharingSavedObjectProps } = loaded; + return ( diff --git a/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.test.tsx b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.test.tsx new file mode 100644 index 0000000000000..c535c9e32d335 --- /dev/null +++ b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.test.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { WorkspaceLayoutComponent } from '.'; +import { coreMock } from 'src/core/public/mocks'; +import { spacesPluginMock } from '../../../../spaces/public/mocks'; +import { NavigationPublicPluginStart as NavigationStart } from '../../../../../../src/plugins/navigation/public'; +import { GraphSavePolicy, GraphWorkspaceSavedObject, IndexPatternProvider } from '../../types'; +import { OverlayStart, Capabilities } from 'kibana/public'; +import { SharingSavedObjectProps } from '../../helpers/use_workspace_loader'; + +jest.mock('react-router-dom', () => { + const useLocation = () => ({ + search: '?query={}', + }); + return { + useLocation, + }; +}); + +describe('workspace_layout', () => { + const defaultProps = { + renderCounter: 1, + loading: false, + savedWorkspace: { id: 'test' } as GraphWorkspaceSavedObject, + hasFields: true, + overlays: {} as OverlayStart, + workspaceInitialized: true, + indexPatterns: [], + indexPatternProvider: {} as IndexPatternProvider, + capabilities: {} as Capabilities, + coreStart: coreMock.createStart(), + graphSavePolicy: 'configAndDataWithConsent' as GraphSavePolicy, + navigation: {} as NavigationStart, + canEditDrillDownUrls: true, + setHeaderActionMenu: jest.fn(), + sharingSavedObjectProps: { + outcome: 'exactMatch', + aliasTargetId: '', + } as SharingSavedObjectProps, + spaces: spacesPluginMock.createStartContract(), + }; + it('should display conflict notification if outcome is conflict', () => { + shallow( + + ); + expect(defaultProps.spaces.ui.components.getLegacyUrlConflict).toHaveBeenCalledWith({ + currentObjectId: 'test', + objectNoun: 'Graph', + otherObjectId: 'conflictId', + otherObjectPath: '#/workspace/conflictId?query={}', + }); + }); +}); diff --git a/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx index 70e5b82ec6526..5426ae9228518 100644 --- a/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx +++ b/x-pack/plugins/graph/public/components/workspace_layout/workspace_layout.tsx @@ -9,6 +9,7 @@ import React, { Fragment, memo, useCallback, useRef, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSpacer } from '@elastic/eui'; import { connect } from 'react-redux'; +import { useLocation } from 'react-router-dom'; import { SearchBar } from '../search_bar'; import { GraphState, @@ -33,6 +34,8 @@ import { GraphServices } from '../../application'; import { ControlPanel } from '../control_panel'; import { GraphVisualization } from '../graph_visualization'; import { colorChoices } from '../../helpers/style_choices'; +import { SharingSavedObjectProps } from '../../helpers/use_workspace_loader'; +import { getEditUrl } from '../../services/url'; /** * Each component, which depends on `worksapce` @@ -51,6 +54,7 @@ type WorkspaceLayoutProps = Pick< | 'coreStart' | 'canEditDrillDownUrls' | 'overlays' + | 'spaces' > & { renderCounter: number; workspace?: Workspace; @@ -58,7 +62,7 @@ type WorkspaceLayoutProps = Pick< indexPatterns: IndexPatternSavedObject[]; savedWorkspace: GraphWorkspaceSavedObject; indexPatternProvider: IndexPatternProvider; - urlQuery: string | null; + sharingSavedObjectProps?: SharingSavedObjectProps; }; interface WorkspaceLayoutStateProps { @@ -66,7 +70,7 @@ interface WorkspaceLayoutStateProps { hasFields: boolean; } -const WorkspaceLayoutComponent = ({ +export const WorkspaceLayoutComponent = ({ renderCounter, workspace, loading, @@ -81,8 +85,9 @@ const WorkspaceLayoutComponent = ({ graphSavePolicy, navigation, canEditDrillDownUrls, - urlQuery, setHeaderActionMenu, + sharingSavedObjectProps, + spaces, }: WorkspaceLayoutProps & WorkspaceLayoutStateProps) => { const [currentIndexPattern, setCurrentIndexPattern] = useState(); const [showInspect, setShowInspect] = useState(false); @@ -90,6 +95,10 @@ const WorkspaceLayoutComponent = ({ const [mergeCandidates, setMergeCandidates] = useState([]); const [control, setControl] = useState('none'); const selectedNode = useRef(undefined); + + const search = useLocation().search; + const urlQuery = new URLSearchParams(search).get('query'); + const isInitialized = Boolean(workspaceInitialized || savedWorkspace.id); const selectSelected = useCallback((node: WorkspaceNode) => { @@ -154,6 +163,27 @@ const WorkspaceLayoutComponent = ({ [] ); + const getLegacyUrlConflictCallout = useCallback(() => { + // This function returns a callout component *if* we have encountered a "legacy URL conflict" scenario + const currentObjectId = savedWorkspace.id; + if (spaces && sharingSavedObjectProps?.outcome === 'conflict' && currentObjectId) { + // We have resolved to one object, but another object has a legacy URL alias associated with this ID/page. We should display a + // callout with a warning for the user, and provide a way for them to navigate to the other object. + const otherObjectId = sharingSavedObjectProps?.aliasTargetId!; // This is always defined if outcome === 'conflict' + const otherObjectPath = + getEditUrl(coreStart.http.basePath.prepend, { id: otherObjectId }) + search; + return spaces.ui.components.getLegacyUrlConflict({ + objectNoun: i18n.translate('xpack.graph.legacyUrlConflict.objectNoun', { + defaultMessage: 'Graph', + }), + currentObjectId, + otherObjectId, + otherObjectPath, + }); + } + return null; + }, [savedWorkspace.id, sharingSavedObjectProps, spaces, coreStart.http, search]); + return ( - {isInitialized && }
+ {getLegacyUrlConflictCallout()} {!isInitialized && (
savedWorkspaceType, + ...defaultsProps, + } as GraphWorkspaceSavedObject, + }; +} + export async function getSavedWorkspace( savedObjectsClient: SavedObjectsClientContract, - id?: string + id: string ) { - const savedObject = { - id, - displayName: 'graph workspace', - getEsType: () => savedWorkspaceType, - } as { [key: string]: any }; - - if (!id) { - assign(savedObject, defaultsProps); - return Promise.resolve(savedObject); - } + const resolveResult = await savedObjectsClient.resolve>( + savedWorkspaceType, + id + ); - const resp = await savedObjectsClient.get>(savedWorkspaceType, id); - savedObject._source = cloneDeep(resp.attributes); + const resp = resolveResult.saved_object; if (!resp._version) { throw new SavedObjectNotFound(savedWorkspaceType, id || ''); } + const savedObject = { + id, + displayName: 'graph workspace', + getEsType: () => savedWorkspaceType, + _source: cloneDeep(resp.attributes), + } as GraphWorkspaceSavedObject; + // assign the defaults to the response defaults(savedObject._source, defaultsProps); @@ -120,7 +130,15 @@ export async function getSavedWorkspace( injectReferences(savedObject, resp.references); } - return savedObject as GraphWorkspaceSavedObject; + const sharingSavedObjectProps = { + outcome: resolveResult.outcome, + aliasTargetId: resolveResult.alias_target_id, + }; + + return { + savedObject, + sharingSavedObjectProps, + }; } export function deleteSavedWorkspace( diff --git a/x-pack/plugins/graph/public/helpers/use_workspace_loader.test.tsx b/x-pack/plugins/graph/public/helpers/use_workspace_loader.test.tsx new file mode 100644 index 0000000000000..db80289d0d32d --- /dev/null +++ b/x-pack/plugins/graph/public/helpers/use_workspace_loader.test.tsx @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { mount } from 'enzyme'; +import { useWorkspaceLoader, UseWorkspaceLoaderProps } from './use_workspace_loader'; +import { coreMock } from 'src/core/public/mocks'; +import { spacesPluginMock } from '../../../spaces/public/mocks'; +import { createMockGraphStore } from '../state_management/mocks'; +import { Workspace } from '../types'; +import { SavedObjectsClientCommon } from 'src/plugins/data/common'; +import { act } from 'react-dom/test-utils'; + +jest.mock('react-router-dom', () => { + const useLocation = () => ({ + search: '?query={}', + }); + + const replaceFn = jest.fn(); + + const useHistory = () => ({ + replace: replaceFn, + }); + return { + useHistory, + useLocation, + useParams: () => ({ + id: '1', + }), + }; +}); + +const mockSavedObjectsClient = ({ + resolve: jest.fn().mockResolvedValue({ + saved_object: { id: 10, _version: '7.15.0', attributes: { wsState: '{}' } }, + outcome: 'exactMatch', + }), + find: jest.fn().mockResolvedValue({ title: 'test' }), +} as unknown) as SavedObjectsClientCommon; + +async function setup(props: UseWorkspaceLoaderProps) { + const returnVal = {}; + function TestComponent() { + Object.assign(returnVal, useWorkspaceLoader(props)); + return null; + } + await act(async () => { + const promise = Promise.resolve(); + mount(); + await act(() => promise); + }); + return returnVal; +} + +describe('use_workspace_loader', () => { + const defaultProps = { + workspaceRef: { current: {} as Workspace }, + store: createMockGraphStore({}).store, + savedObjectsClient: mockSavedObjectsClient, + coreStart: coreMock.createStart(), + spaces: spacesPluginMock.createStartContract(), + }; + + it('should not redirect if outcome is exactMatch', async () => { + await act(async () => { + await setup((defaultProps as unknown) as UseWorkspaceLoaderProps); + }); + expect(defaultProps.spaces.ui.redirectLegacyUrl).not.toHaveBeenCalled(); + expect(defaultProps.store.dispatch).toHaveBeenCalled(); + }); + it('should redirect if outcome is aliasMatch', async () => { + const props = { + ...defaultProps, + spaces: spacesPluginMock.createStartContract(), + savedObjectsClient: { + ...mockSavedObjectsClient, + resolve: jest.fn().mockResolvedValue({ + saved_object: { id: 10, _version: '7.15.0', attributes: { wsState: '{}' } }, + outcome: 'aliasMatch', + alias_target_id: 'aliasTargetId', + }), + }, + }; + await act(async () => { + await setup((props as unknown) as UseWorkspaceLoaderProps); + }); + expect(props.spaces.ui.redirectLegacyUrl).toHaveBeenCalledWith( + '#/workspace/aliasTargetId?query={}', + 'Graph' + ); + }); +}); diff --git a/x-pack/plugins/graph/public/helpers/use_workspace_loader.ts b/x-pack/plugins/graph/public/helpers/use_workspace_loader.ts index 8b91546d52446..b9abf31e084fe 100644 --- a/x-pack/plugins/graph/public/helpers/use_workspace_loader.ts +++ b/x-pack/plugins/graph/public/helpers/use_workspace_loader.ts @@ -5,35 +5,48 @@ * 2.0. */ -import { SavedObjectsClientContract, ToastsStart } from 'kibana/public'; +import { SavedObjectsClientContract } from 'kibana/public'; import { useEffect, useState } from 'react'; import { useHistory, useLocation, useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; +import { CoreStart } from 'kibana/public'; import { GraphStore } from '../state_management'; import { GraphWorkspaceSavedObject, IndexPatternSavedObject, Workspace } from '../types'; -import { getSavedWorkspace } from './saved_workspace_utils'; - -interface UseWorkspaceLoaderProps { +import { getEmptyWorkspace, getSavedWorkspace } from './saved_workspace_utils'; +import { getEditUrl } from '../services/url'; +import { SpacesApi } from '../../../spaces/public'; +export interface UseWorkspaceLoaderProps { store: GraphStore; workspaceRef: React.MutableRefObject; savedObjectsClient: SavedObjectsClientContract; - toastNotifications: ToastsStart; + coreStart: CoreStart; + spaces?: SpacesApi; } interface WorkspaceUrlParams { id?: string; } +export interface SharingSavedObjectProps { + outcome?: 'aliasMatch' | 'exactMatch' | 'conflict'; + aliasTargetId?: string; +} + +interface WorkspaceLoadedState { + savedWorkspace: GraphWorkspaceSavedObject; + indexPatterns: IndexPatternSavedObject[]; + sharingSavedObjectProps?: SharingSavedObjectProps; +} export const useWorkspaceLoader = ({ + coreStart, + spaces, workspaceRef, store, savedObjectsClient, - toastNotifications, }: UseWorkspaceLoaderProps) => { - const [indexPatterns, setIndexPatterns] = useState(); - const [savedWorkspace, setSavedWorkspace] = useState(); - const history = useHistory(); - const location = useLocation(); + const [state, setState] = useState(); + const { replace: historyReplace } = useHistory(); + const { search } = useLocation(); const { id } = useParams(); /** @@ -41,7 +54,7 @@ export const useWorkspaceLoader = ({ * on changes in id parameter and URL query only. */ useEffect(() => { - const urlQuery = new URLSearchParams(location.search).get('query'); + const urlQuery = new URLSearchParams(search).get('query'); function loadWorkspace( fetchedSavedWorkspace: GraphWorkspaceSavedObject, @@ -71,24 +84,43 @@ export const useWorkspaceLoader = ({ .then((response) => response.savedObjects); } - async function fetchSavedWorkspace() { - return (id + async function fetchSavedWorkspace(): Promise<{ + savedObject: GraphWorkspaceSavedObject; + sharingSavedObjectProps?: SharingSavedObjectProps; + }> { + return id ? await getSavedWorkspace(savedObjectsClient, id).catch(function (e) { - toastNotifications.addError(e, { + coreStart.notifications.toasts.addError(e, { title: i18n.translate('xpack.graph.missingWorkspaceErrorMessage', { defaultMessage: "Couldn't load graph with ID", }), }); - history.replace('/home'); + historyReplace('/home'); // return promise that never returns to prevent the controller from loading return new Promise(() => {}); }) - : await getSavedWorkspace(savedObjectsClient)) as GraphWorkspaceSavedObject; + : getEmptyWorkspace(); } async function initializeWorkspace() { const fetchedIndexPatterns = await fetchIndexPatterns(); - const fetchedSavedWorkspace = await fetchSavedWorkspace(); + const { + savedObject: fetchedSavedWorkspace, + sharingSavedObjectProps: fetchedSharingSavedObjectProps, + } = await fetchSavedWorkspace(); + + if (spaces && fetchedSharingSavedObjectProps?.outcome === 'aliasMatch') { + // We found this object by a legacy URL alias from its old ID; redirect the user to the page with its new ID, preserving any URL hash + const newObjectId = fetchedSharingSavedObjectProps?.aliasTargetId!; // This is always defined if outcome === 'aliasMatch' + const newPath = getEditUrl(coreStart.http.basePath.prepend, { id: newObjectId }) + search; + spaces.ui.redirectLegacyUrl( + newPath, + i18n.translate('xpack.graph.legacyUrlConflict.objectNoun', { + defaultMessage: 'Graph', + }) + ); + return null; + } /** * Deal with situation of request to open saved workspace. Otherwise clean up store, @@ -99,22 +131,25 @@ export const useWorkspaceLoader = ({ } else if (workspaceRef.current) { clearStore(); } - - setIndexPatterns(fetchedIndexPatterns); - setSavedWorkspace(fetchedSavedWorkspace); + setState({ + savedWorkspace: fetchedSavedWorkspace, + indexPatterns: fetchedIndexPatterns, + sharingSavedObjectProps: fetchedSharingSavedObjectProps, + }); } initializeWorkspace(); }, [ id, - location, + search, store, - history, + historyReplace, savedObjectsClient, - setSavedWorkspace, - toastNotifications, + setState, + coreStart, workspaceRef, + spaces, ]); - return { savedWorkspace, indexPatterns }; + return state; }; diff --git a/x-pack/plugins/graph/public/plugin.ts b/x-pack/plugins/graph/public/plugin.ts index 1ff9afe505a3b..1c44714a2c498 100644 --- a/x-pack/plugins/graph/public/plugin.ts +++ b/x-pack/plugins/graph/public/plugin.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { BehaviorSubject } from 'rxjs'; +import { SpacesApi } from '../../spaces/public'; import { AppNavLinkStatus, AppUpdater, @@ -44,6 +45,7 @@ export interface GraphPluginStartDependencies { savedObjects: SavedObjectsStart; kibanaLegacy: KibanaLegacyStart; home?: HomePublicPluginStart; + spaces?: SpacesApi; } export class GraphPlugin @@ -110,6 +112,7 @@ export class GraphPlugin overlays: coreStart.overlays, savedObjects: pluginsStart.savedObjects, uiSettings: core.uiSettings, + spaces: pluginsStart.spaces, }); }, }); diff --git a/x-pack/plugins/graph/public/services/url.ts b/x-pack/plugins/graph/public/services/url.ts index e45d1f0d662be..b33fdc82d8642 100644 --- a/x-pack/plugins/graph/public/services/url.ts +++ b/x-pack/plugins/graph/public/services/url.ts @@ -18,13 +18,13 @@ export function getNewPath() { return '/workspace'; } -export function getEditPath({ id }: GraphWorkspaceSavedObject) { +export function getEditPath({ id }: Pick) { return `/workspace/${id}`; } export function getEditUrl( addBasePath: (url: string) => string, - workspace: GraphWorkspaceSavedObject + workspace: Pick ) { return addBasePath(`#${getEditPath(workspace)}`); } diff --git a/x-pack/plugins/graph/server/saved_objects/graph_workspace.ts b/x-pack/plugins/graph/server/saved_objects/graph_workspace.ts index b935302fd3a88..beb7dbf921e20 100644 --- a/x-pack/plugins/graph/server/saved_objects/graph_workspace.ts +++ b/x-pack/plugins/graph/server/saved_objects/graph_workspace.ts @@ -10,7 +10,8 @@ import { graphMigrations } from './migrations'; export const graphWorkspace: SavedObjectsType = { name: 'graph-workspace', - namespaceType: 'single', + namespaceType: 'multiple-isolated', + convertToMultiNamespaceTypeVersion: '8.0.0', hidden: false, management: { icon: 'graphApp', diff --git a/x-pack/plugins/graph/tsconfig.json b/x-pack/plugins/graph/tsconfig.json index d655f28c4e46e..6a5623b311d5e 100644 --- a/x-pack/plugins/graph/tsconfig.json +++ b/x-pack/plugins/graph/tsconfig.json @@ -24,6 +24,7 @@ { "path": "../../../src/plugins/kibana_legacy/tsconfig.json"}, { "path": "../../../src/plugins/home/tsconfig.json"}, { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, - { "path": "../../../src/plugins/kibana_react/tsconfig.json" } + { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, + { "path": "../spaces/tsconfig.json" } ] } diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx index fe0fbeecf8408..31bc09f9d4dd8 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiSpacer } from '@elastic/eui'; import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { useUiTracker } from '../../../../../../observability/public'; +import { useWaffleOptionsContext } from '../hooks/use_waffle_options'; import { InfraFormatter } from '../../../../lib/lib'; import { Timeline } from './timeline/timeline'; @@ -28,13 +29,20 @@ export const BottomDrawer: React.FC<{ formatter: InfraFormatter; width: number; }> = ({ measureRef, width, interval, formatter, children }) => { - const [isOpen, setIsOpen] = useState(false); + const { timelineOpen, changeTimelineOpen } = useWaffleOptionsContext(); + + const [isOpen, setIsOpen] = useState(Boolean(timelineOpen)); + + useEffect(() => { + if (isOpen !== timelineOpen) setIsOpen(Boolean(timelineOpen)); + }, [isOpen, timelineOpen]); const trackDrawerOpen = useUiTracker({ app: 'infra_metrics' }); const onClick = useCallback(() => { if (!isOpen) trackDrawerOpen({ metric: 'open_timeline_drawer__inventory' }); setIsOpen(!isOpen); - }, [isOpen, trackDrawerOpen]); + changeTimelineOpen(!isOpen); + }, [isOpen, trackDrawerOpen, changeTimelineOpen]); return ( diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx index ac4fac394dacc..d8b578769a1cb 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx @@ -130,6 +130,7 @@ const mockedUseWaffleOptionsContexReturnValue: ReturnType {}), changeLegend: jest.fn(() => {}), changeSort: jest.fn(() => {}), + changeTimelineOpen: jest.fn(() => {}), setWaffleOptionsState: jest.fn(() => {}), boundsOverride: { max: 1, min: 0 }, autoBounds: true, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts index 0ba8398fa4c42..8767be4f8a27e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts @@ -44,6 +44,7 @@ export const DEFAULT_WAFFLE_OPTIONS_STATE: WaffleOptionsState = { legend: DEFAULT_LEGEND, source: 'default', sort: { by: 'name', direction: 'desc' }, + timelineOpen: false, }; export const useWaffleOptions = () => { @@ -134,6 +135,11 @@ export const useWaffleOptions = () => { setCustomMetrics(state.customMetrics); }, [state, inventoryPrefill]); + const changeTimelineOpen = useCallback( + (timelineOpen: boolean) => setState((previous) => ({ ...previous, timelineOpen })), + [setState] + ); + return { ...DEFAULT_WAFFLE_OPTIONS_STATE, ...state, @@ -149,6 +155,7 @@ export const useWaffleOptions = () => { changeCustomMetrics, changeLegend, changeSort, + changeTimelineOpen, setWaffleOptionsState: setState, }; }; @@ -188,7 +195,7 @@ export const WaffleOptionsStateRT = rt.intersection([ customMetrics: rt.array(SnapshotCustomMetricInputRT), sort: WaffleSortOptionRT, }), - rt.partial({ source: rt.string, legend: WaffleLegendOptionsRT }), + rt.partial({ source: rt.string, legend: WaffleLegendOptionsRT, timelineOpen: rt.boolean }), ]); export type WaffleSortOption = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts index 91f1859899177..02a2144f1282e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts @@ -39,6 +39,7 @@ export const useWaffleViewState = () => { region, legend, sort, + timelineOpen, setWaffleOptionsState, } = useWaffleOptionsContext(); const { currentTime, isAutoReloading, setWaffleTimeState } = useWaffleTimeContext(); @@ -60,6 +61,7 @@ export const useWaffleViewState = () => { autoReload: isAutoReloading, filterQuery, legend, + timelineOpen, }; const onViewChange = useCallback( @@ -77,6 +79,7 @@ export const useWaffleViewState = () => { accountId: newState.accountId, region: newState.region, legend: newState.legend, + timelineOpen: newState.timelineOpen, }); if (newState.time) { diff --git a/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts b/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts index 5a42ea054b4d9..29fc88a81d182 100644 --- a/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts +++ b/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts @@ -5,16 +5,13 @@ * 2.0. */ -import { SavedObjectsClientContract, SavedObjectsBulkUpdateObject } from 'kibana/public'; +import { SavedObjectsClientContract } from 'kibana/public'; import { SavedObjectIndexStore } from './saved_object_store'; describe('LensStore', () => { function testStore(testId?: string) { const client = { create: jest.fn(() => Promise.resolve({ id: testId || 'testid' })), - bulkUpdate: jest.fn(([{ id }]: SavedObjectsBulkUpdateObject[]) => - Promise.resolve({ savedObjects: [{ id }, { id }] }) - ), resolve: jest.fn(), }; @@ -81,7 +78,7 @@ describe('LensStore', () => { }); test('updates and returns a visualization document', async () => { - const { client, store } = testStore(); + const { client, store } = testStore('Gandalf'); const doc = await store.save({ savedObjectId: 'Gandalf', title: 'Even the very wise cannot see all ends.', @@ -108,23 +105,11 @@ describe('LensStore', () => { }, }); - expect(client.bulkUpdate).toHaveBeenCalledTimes(1); - expect(client.bulkUpdate).toHaveBeenCalledWith([ - { - type: 'lens', - id: 'Gandalf', - references: [], - attributes: { - title: null, - visualizationType: null, - state: null, - }, - }, - { - type: 'lens', - id: 'Gandalf', - references: [], - attributes: { + expect(client.create).toHaveBeenCalledTimes(1); + expect(client.create.mock.calls).toEqual([ + [ + 'lens', + { title: 'Even the very wise cannot see all ends.', visualizationType: 'line', state: { @@ -134,7 +119,8 @@ describe('LensStore', () => { filters: [], }, }, - }, + { references: [], id: 'Gandalf', overwrite: true }, + ], ]); }); }); diff --git a/x-pack/plugins/lens/public/persistence/saved_object_store.ts b/x-pack/plugins/lens/public/persistence/saved_object_store.ts index 79d7b78f768ae..b8615b8852b1a 100644 --- a/x-pack/plugins/lens/public/persistence/saved_object_store.ts +++ b/x-pack/plugins/lens/public/persistence/saved_object_store.ts @@ -57,38 +57,23 @@ export class SavedObjectIndexStore implements SavedObjectStore { // remove this workaround when SavedObjectAttributes is updated. const attributes = (rest as unknown) as SavedObjectAttributes; - const result = await (savedObjectId - ? this.safeUpdate(savedObjectId, attributes, references) - : this.client.create(DOC_TYPE, attributes, { - references, - })); + const result = await this.client.create( + DOC_TYPE, + attributes, + savedObjectId + ? { + references, + overwrite: true, + id: savedObjectId, + } + : { + references, + } + ); return { ...vis, savedObjectId: result.id }; }; - // As Lens is using an object to store its attributes, using the update API - // will merge the new attribute object with the old one, not overwriting deleted - // keys. As Lens is using objects as maps in various places, this is a problem because - // deleted subtrees make it back into the object after a load. - // This function fixes this by doing two updates - one to empty out the document setting - // every key to null, and a second one to load the new content. - private async safeUpdate( - savedObjectId: string, - attributes: SavedObjectAttributes, - references: SavedObjectReference[] - ) { - const resetAttributes: SavedObjectAttributes = {}; - Object.keys(attributes).forEach((key) => { - resetAttributes[key] = null; - }); - return ( - await this.client.bulkUpdate([ - { type: DOC_TYPE, id: savedObjectId, attributes: resetAttributes, references }, - { type: DOC_TYPE, id: savedObjectId, attributes, references }, - ]) - ).savedObjects[1]; - } - async load(savedObjectId: string): Promise> { const resolveResult = await this.client.resolve( DOC_TYPE, diff --git a/x-pack/plugins/lens/server/saved_objects.ts b/x-pack/plugins/lens/server/saved_objects.ts index 0266378981fd6..4e376b23b3374 100644 --- a/x-pack/plugins/lens/server/saved_objects.ts +++ b/x-pack/plugins/lens/server/saved_objects.ts @@ -13,7 +13,8 @@ export function setupSavedObjects(core: CoreSetup) { core.savedObjects.registerType({ name: 'lens', hidden: false, - namespaceType: 'single', + namespaceType: 'multiple-isolated', + convertToMultiNamespaceTypeVersion: '8.0.0', management: { icon: 'lensApp', defaultSearchField: 'title', diff --git a/x-pack/plugins/maps/common/index.ts b/x-pack/plugins/maps/common/index.ts index 7c551b3ed9eb4..c1b5d26fca292 100644 --- a/x-pack/plugins/maps/common/index.ts +++ b/x-pack/plugins/maps/common/index.ts @@ -5,8 +5,24 @@ * 2.0. */ -// TODO: https://github.com/elastic/kibana/issues/109853 -/* eslint-disable @kbn/eslint/no_export_all */ +export { + AGG_TYPE, + COLOR_MAP_TYPE, + ES_GEO_FIELD_TYPE, + FIELD_ORIGIN, + INITIAL_LOCATION, + LABEL_BORDER_SIZES, + MAP_SAVED_OBJECT_TYPE, + SOURCE_TYPES, + STYLE_TYPE, + SYMBOLIZE_AS_TYPES, +} from './constants'; -export * from './constants'; -export * from './types'; +export { + EMSFileSourceDescriptor, + ESTermSourceDescriptor, + LayerDescriptor, + TooltipFeature, + VectorLayerDescriptor, + VectorStyleDescriptor, +} from './descriptor_types'; diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json index e2cc415820db5..bfd501dbcb295 100644 --- a/x-pack/plugins/maps/kibana.json +++ b/x-pack/plugins/maps/kibana.json @@ -34,7 +34,7 @@ "ui": true, "server": true, "extraPublicDirs": [ - "common/constants" + "common" ], "requiredBundles": [ "kibanaReact", diff --git a/x-pack/plugins/maps/public/actions/ui_actions.ts b/x-pack/plugins/maps/public/actions/ui_actions.ts index 27e11a938e22b..70e24283ef48f 100644 --- a/x-pack/plugins/maps/public/actions/ui_actions.ts +++ b/x-pack/plugins/maps/public/actions/ui_actions.ts @@ -12,7 +12,7 @@ import { getFlyoutDisplay } from '../selectors/ui_selectors'; import { FLYOUT_STATE } from '../reducers/ui'; import { setQuery, trackMapSettings } from './map_actions'; import { setSelectedLayer } from './layer_actions'; -import { DRAW_MODE } from '../../common'; +import { DRAW_MODE } from '../../common/constants'; import { UPDATE_EDIT_STATE } from './map_action_constants'; export const UPDATE_FLYOUT = 'UPDATE_FLYOUT'; diff --git a/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/config.tsx b/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/config.tsx index 5a82cf881e34d..0c3d1dc41d640 100644 --- a/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/config.tsx +++ b/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/config.tsx @@ -11,7 +11,7 @@ import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_re import { NewVectorLayerEditor } from './wizard'; import { DrawLayerIcon } from '../../layers/icons/draw_layer_icon'; import { getFileUpload } from '../../../kibana_services'; -import { LAYER_WIZARD_CATEGORY } from '../../../../common'; +import { LAYER_WIZARD_CATEGORY } from '../../../../common/constants'; const ADD_VECTOR_DRAWING_LAYER = 'ADD_VECTOR_DRAWING_LAYER'; diff --git a/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/create_new_index_pattern.ts b/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/create_new_index_pattern.ts index 2e57014824a3c..596d2ce86bbe2 100644 --- a/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/create_new_index_pattern.ts +++ b/x-pack/plugins/maps/public/classes/layers/new_vector_layer_wizard/create_new_index_pattern.ts @@ -6,11 +6,8 @@ */ import { getHttp } from '../../../kibana_services'; -import { - CreateDocSourceResp, - INDEX_SOURCE_API_PATH, - IndexSourceMappings, -} from '../../../../common'; +import { CreateDocSourceResp, IndexSourceMappings } from '../../../../common/types'; +import { INDEX_SOURCE_API_PATH } from '../../../../common/constants'; export const createNewIndexAndPattern = async ({ indexName, diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.test.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.test.ts index 939d174798ca1..8955342824a77 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { emsWorldLayerId } from '../../../../../common'; +import { emsWorldLayerId } from '../../../../../common/constants'; jest.mock('../../../../kibana_services', () => { return { diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/util/feature_edit.ts b/x-pack/plugins/maps/public/classes/sources/es_search_source/util/feature_edit.ts index 08ba33a72363f..af39019c2d14c 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/util/feature_edit.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/util/feature_edit.ts @@ -11,7 +11,7 @@ import { CHECK_IS_DRAWING_INDEX, GET_MATCHING_INDEXES_PATH, INDEX_FEATURE_PATH, -} from '../../../../../common'; +} from '../../../../../common/constants'; import { getHttp } from '../../../../kibana_services'; export const addFeatureToIndex = async ( diff --git a/x-pack/plugins/maps/public/connected_components/add_layer_panel/index.ts b/x-pack/plugins/maps/public/connected_components/add_layer_panel/index.ts index 67f0c3664acda..ed10b135899d5 100644 --- a/x-pack/plugins/maps/public/connected_components/add_layer_panel/index.ts +++ b/x-pack/plugins/maps/public/connected_components/add_layer_panel/index.ts @@ -22,7 +22,7 @@ import { import { MapStoreState } from '../../reducers/store'; import { LayerDescriptor } from '../../../common/descriptor_types'; import { hasPreviewLayers, isLoadingPreviewLayers } from '../../selectors/map_selectors'; -import { DRAW_MODE } from '../../../common'; +import { DRAW_MODE } from '../../../common/constants'; function mapStateToProps(state: MapStoreState) { return { diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx index 7b408df3a8134..0ebd145545a6c 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_feature_control/draw_feature_control.tsx @@ -16,7 +16,7 @@ import * as jsts from 'jsts'; import { MapMouseEvent } from '@kbn/mapbox-gl'; import { getToasts } from '../../../../kibana_services'; import { DrawControl } from '../'; -import { DRAW_MODE, DRAW_SHAPE } from '../../../../../common'; +import { DRAW_MODE, DRAW_SHAPE } from '../../../../../common/constants'; import { ILayer } from '../../../../classes/layers/layer'; import { EXCLUDE_CENTROID_FEATURES, diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_filter_control/index.ts b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_filter_control/index.ts index 795f6cf031f24..bf20314718bf7 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_filter_control/index.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/draw_filter_control/index.ts @@ -11,7 +11,7 @@ import { connect } from 'react-redux'; import { DrawFilterControl } from './draw_filter_control'; import { setDrawMode, updateDrawState } from '../../../../actions'; import { getDrawState, getGeoFieldNames } from '../../../../selectors/map_selectors'; -import { DRAW_MODE } from '../../../../../common'; +import { DRAW_MODE } from '../../../../../common/constants'; import { MapStoreState } from '../../../../reducers/store'; import { getDrawMode } from '../../../../selectors/ui_selectors'; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts index 847b0f2e429e2..b0f1941caec08 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/draw_control/index.ts @@ -11,7 +11,7 @@ import { connect } from 'react-redux'; import { updateEditShape } from '../../../actions'; import { MapStoreState } from '../../../reducers/store'; import { DrawControl } from './draw_control'; -import { DRAW_SHAPE } from '../../../../common'; +import { DRAW_SHAPE } from '../../../../common/constants'; function mapDispatchToProps(dispatch: ThunkDispatch) { return { diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/index.ts b/x-pack/plugins/maps/public/connected_components/mb_map/index.ts index 9936d412de9e6..d46d4f53de47f 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/index.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/index.ts @@ -32,7 +32,7 @@ import { import { getDrawMode, getIsFullScreen } from '../../selectors/ui_selectors'; import { getInspectorAdapters } from '../../reducers/non_serializable_instances'; import { MapStoreState } from '../../reducers/store'; -import { DRAW_MODE } from '../../../common'; +import { DRAW_MODE } from '../../../common/constants'; import { TileMetaFeature } from '../../../common/descriptor_types'; import type { MapExtentState } from '../../reducers/map/types'; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/index.ts b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/index.ts index 009a512023309..a9281898902e4 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/index.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/index.ts @@ -23,7 +23,7 @@ import { getGeoFieldNames, } from '../../../selectors/map_selectors'; import { getDrawMode } from '../../../selectors/ui_selectors'; -import { DRAW_MODE } from '../../../../common'; +import { DRAW_MODE } from '../../../../common/constants'; import { MapStoreState } from '../../../reducers/store'; function mapStateToProps(state: MapStoreState) { diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/index.ts b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/index.ts index 4b8005cc5054d..c525523047c40 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/index.ts +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/index.ts @@ -19,7 +19,7 @@ import { } from '../../../selectors/ui_selectors'; import { getLayerList } from '../../../selectors/map_selectors'; import { MapStoreState } from '../../../reducers/store'; -import { DRAW_MODE } from '../../../../common'; +import { DRAW_MODE } from '../../../../common/constants'; function mapStateToProps(state: MapStoreState) { return { diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/index.ts b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/index.ts index 7f879e8501629..87242aad41ced 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/index.ts +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/index.ts @@ -32,7 +32,7 @@ import { setDrawMode, updateDrawState, } from '../../../../../actions'; -import { DRAW_MODE } from '../../../../../../common'; +import { DRAW_MODE } from '../../../../../../common/constants'; function mapStateToProps(state: MapStoreState, ownProps: OwnProps): ReduxStateProps { const flyoutDisplay = getFlyoutDisplay(state); diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts index 400904b530963..407eb558c1038 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts @@ -21,7 +21,7 @@ import { import { getLayerListRaw } from '../../../../../../selectors/map_selectors'; import { getIsReadOnly } from '../../../../../../selectors/ui_selectors'; import { TOCEntryActionsPopover } from './toc_entry_actions_popover'; -import { DRAW_MODE } from '../../../../../../../common'; +import { DRAW_MODE } from '../../../../../../../common/constants'; function mapStateToProps(state: MapStoreState) { return { 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 322c0540528d7..33b95f4ca5816 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 @@ -19,7 +19,7 @@ import { } from '../action_labels'; import { ESSearchSource } from '../../../../../../classes/sources/es_search_source'; import { VectorLayer } from '../../../../../../classes/layers/vector_layer'; -import { SCALING_TYPES, VECTOR_SHAPE_TYPE } from '../../../../../../../common'; +import { SCALING_TYPES, VECTOR_SHAPE_TYPE } from '../../../../../../../common/constants'; import { ESSearchSourceSyncMeta } from '../../../../../../../common/descriptor_types'; export interface Props { diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/feature_draw_controls/feature_edit_tools/index.ts b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/feature_draw_controls/feature_edit_tools/index.ts index 727bbbb8c84a3..7e993407b821c 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/feature_draw_controls/feature_edit_tools/index.ts +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/feature_draw_controls/feature_edit_tools/index.ts @@ -16,7 +16,7 @@ import { } from './feature_edit_tools'; import { updateEditShape } from '../../../../actions'; import { MapStoreState } from '../../../../reducers/store'; -import { DRAW_SHAPE } from '../../../../../common'; +import { DRAW_SHAPE } from '../../../../../common/constants'; import { getEditState } from '../../../../selectors/map_selectors'; function mapStateToProps(state: MapStoreState): ReduxStateProps { diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.ts b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.ts index d98d24a313ec5..0dcb8f42f0f70 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.ts +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.ts @@ -10,7 +10,7 @@ import { ToolbarOverlay } from './toolbar_overlay'; import { MapStoreState } from '../../reducers/store'; import { getDrawMode } from '../../selectors/ui_selectors'; import { getGeoFieldNames } from '../../selectors/map_selectors'; -import { DRAW_MODE } from '../../../common'; +import { DRAW_MODE } from '../../../common/constants'; function mapStateToProps(state: MapStoreState) { return { diff --git a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/index.ts b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/index.ts index 49464e2614c36..9b330b22721db 100644 --- a/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/index.ts +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/index.ts @@ -12,7 +12,7 @@ import { ToolsControl } from './tools_control'; import { setDrawMode, updateDrawState } from '../../../actions'; import { MapStoreState } from '../../../reducers/store'; import { DrawState } from '../../../../common/descriptor_types'; -import { DRAW_MODE } from '../../../../common'; +import { DRAW_MODE } from '../../../../common/constants'; import { getDrawMode } from '../../../selectors/ui_selectors'; function mapStateToProps(state: MapStoreState) { diff --git a/x-pack/plugins/maps/public/reducers/ui.ts b/x-pack/plugins/maps/public/reducers/ui.ts index 8645fe2fed88d..f3f948bb96508 100644 --- a/x-pack/plugins/maps/public/reducers/ui.ts +++ b/x-pack/plugins/maps/public/reducers/ui.ts @@ -20,7 +20,7 @@ import { HIDE_TOC_DETAILS, SET_DRAW_MODE, } from '../actions'; -import { DRAW_MODE } from '../../common'; +import { DRAW_MODE } from '../../common/constants'; export enum FLYOUT_STATE { NONE = 'NONE', diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts index 9fc17b810964b..1b95db84e4916 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.test.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { LAYER_STYLE_TYPE, LAYER_TYPE, SOURCE_TYPES } from '../../common'; +import { LAYER_STYLE_TYPE, LAYER_TYPE, SOURCE_TYPES } from '../../common/constants'; jest.mock('../classes/layers/tiled_vector_layer/tiled_vector_layer', () => {}); jest.mock('../classes/layers/blended_vector_layer/blended_vector_layer', () => {}); diff --git a/x-pack/plugins/maps/public/selectors/ui_selectors.ts b/x-pack/plugins/maps/public/selectors/ui_selectors.ts index 24a2a098daf23..942a5190691a1 100644 --- a/x-pack/plugins/maps/public/selectors/ui_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/ui_selectors.ts @@ -8,7 +8,7 @@ import { MapStoreState } from '../reducers/store'; import { FLYOUT_STATE } from '../reducers/ui'; -import { DRAW_MODE } from '../../common'; +import { DRAW_MODE } from '../../common/constants'; export const getFlyoutDisplay = ({ ui }: MapStoreState): FLYOUT_STATE => ui.flyoutDisplay; export const getDrawMode = ({ ui }: MapStoreState): DRAW_MODE => ui.drawMode; diff --git a/x-pack/plugins/maps/server/data_indexing/create_doc_source.ts b/x-pack/plugins/maps/server/data_indexing/create_doc_source.ts index cb2e144dc0cf2..0e595848bd12d 100644 --- a/x-pack/plugins/maps/server/data_indexing/create_doc_source.ts +++ b/x-pack/plugins/maps/server/data_indexing/create_doc_source.ts @@ -6,12 +6,8 @@ */ import { ElasticsearchClient, IScopedClusterClient } from 'kibana/server'; -import { - MAPS_NEW_VECTOR_LAYER_META_CREATED_BY, - CreateDocSourceResp, - IndexSourceMappings, - BodySettings, -} from '../../common'; +import { CreateDocSourceResp, IndexSourceMappings, BodySettings } from '../../common/types'; +import { MAPS_NEW_VECTOR_LAYER_META_CREATED_BY } from '../../common/constants'; import { IndexPatternsCommonService } from '../../../../../src/plugins/data/server'; const DEFAULT_SETTINGS = { number_of_shards: 1 }; diff --git a/x-pack/plugins/maps/server/data_indexing/index_data.ts b/x-pack/plugins/maps/server/data_indexing/index_data.ts index 9a05b81d4269d..6b13e3f565196 100644 --- a/x-pack/plugins/maps/server/data_indexing/index_data.ts +++ b/x-pack/plugins/maps/server/data_indexing/index_data.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { ElasticsearchClient } from 'kibana/server'; -import { WriteSettings } from '../../common'; +import { WriteSettings } from '../../common/types'; export async function writeDataToIndex( index: string, diff --git a/x-pack/plugins/maps/server/maps_telemetry/util.ts b/x-pack/plugins/maps/server/maps_telemetry/util.ts index 844486db89abf..ff9339fca76cb 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/util.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/util.ts @@ -11,7 +11,13 @@ import { ESSearchSourceDescriptor, LayerDescriptor, } from '../../common/descriptor_types'; -import { GRID_RESOLUTION, LAYER_TYPE, RENDER_AS, SCALING_TYPES, SOURCE_TYPES } from '../../common'; +import { + GRID_RESOLUTION, + LAYER_TYPE, + RENDER_AS, + SCALING_TYPES, + SOURCE_TYPES, +} from '../../common/constants'; import { DEFAULT_EMS_DARKMAP_ID, DEFAULT_EMS_ROADMAP_DESATURATED_ID, diff --git a/x-pack/plugins/maps/server/routes.js b/x-pack/plugins/maps/server/routes.js index bff2f1ee24c9f..a25a28c4da21c 100644 --- a/x-pack/plugins/maps/server/routes.js +++ b/x-pack/plugins/maps/server/routes.js @@ -22,7 +22,7 @@ import { INDEX_SETTINGS_API_PATH, FONTS_API_PATH, API_ROOT_PATH, -} from '../common'; +} from '../common/constants'; import { EMSClient } from '@elastic/ems-client'; import fetch from 'node-fetch'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js b/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js index 6e68608c75cef..a0ac4db734bd7 100644 --- a/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js +++ b/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { emsWorldLayerId } from '../../common'; +import { emsWorldLayerId } from '../../common/constants'; const layerList = [ { diff --git a/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js b/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js index f3efe4c6e74dd..984b9de7ac2b6 100644 --- a/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js +++ b/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { emsWorldLayerId } from '../../common'; +import { emsWorldLayerId } from '../../common/constants'; const layerList = [ { diff --git a/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx b/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx index 394df03f46567..02c295e42f225 100644 --- a/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx +++ b/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx @@ -8,15 +8,17 @@ import React, { useEffect, useRef, useState } from 'react'; import { htmlIdGenerator } from '@elastic/eui'; -import { LayerDescriptor } from '../../../../../maps/common/descriptor_types'; -import { INITIAL_LOCATION } from '../../../../../maps/common/constants'; +import { + INITIAL_LOCATION, + LayerDescriptor, + MAP_SAVED_OBJECT_TYPE, +} from '../../../../../maps/common'; import type { MapEmbeddable, MapEmbeddableInput, MapEmbeddableOutput, RenderTooltipContentParams, } from '../../../../../maps/public'; -import { MAP_SAVED_OBJECT_TYPE } from '../../../../../maps/public'; import { EmbeddableFactory, diff --git a/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx index fe43bd659131f..d40f036a61089 100644 --- a/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx +++ b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx @@ -16,13 +16,13 @@ import { EuiTitle, htmlIdGenerator, } from '@elastic/eui'; -import { VectorLayerDescriptor } from '../../../../maps/common/descriptor_types'; import { FIELD_ORIGIN, SOURCE_TYPES, STYLE_TYPE, COLOR_MAP_TYPE, -} from '../../../../maps/common/constants'; + VectorLayerDescriptor, +} from '../../../../maps/common'; import { useMlKibana } from '../contexts/kibana'; import { isDefined } from '../../../common/types/guards'; import { MlEmbeddedMapComponent } from '../components/ml_embedded_map'; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_embedded_map.tsx b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_embedded_map.tsx index a3a5bd7ddecdf..e644a8b91e254 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_embedded_map.tsx +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_embedded_map.tsx @@ -7,7 +7,7 @@ import React, { useState, useEffect } from 'react'; import { Dictionary } from '../../../../common/types/common'; -import { LayerDescriptor } from '../../../../../maps/common/descriptor_types'; +import { LayerDescriptor } from '../../../../../maps/common'; import { getMLAnomaliesActualLayer, getMLAnomaliesTypicalLayer } from './map_config'; import { MlEmbeddedMapComponent } from '../../components/ml_embedded_map'; interface Props { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts b/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts index 49d20d3f59b61..d4bafe3898033 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FIELD_ORIGIN, STYLE_TYPE } from '../../../../../maps/common/constants'; +import { FIELD_ORIGIN, STYLE_TYPE } from '../../../../../maps/common'; import { ANOMALY_THRESHOLD, SEVERITY_COLORS } from '../../../../common'; import { AnomaliesTableData } from '../explorer_utils'; diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_breadcrumbs.ts b/x-pack/plugins/monitoring/public/application/hooks/use_breadcrumbs.ts new file mode 100644 index 0000000000000..5deac417ad3f5 --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/hooks/use_breadcrumbs.ts @@ -0,0 +1,289 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useCallback, useEffect, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import createContainer from 'constate'; +import { History } from 'history'; +import { Observable } from 'rxjs'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; + +interface Crumb { + url?: string | null; + label: string; + ignoreGlobalState: boolean; + testSubj?: string; +} +// Helper for making objects to use in a link element +const createCrumb = ( + url: string | null, + label: string, + testSubj?: string, + ignoreGlobalState = false +) => { + const crumb: Crumb = { url, label, ignoreGlobalState }; + if (testSubj) { + crumb.testSubj = testSubj; + } + return crumb; +}; + +// generate Elasticsearch breadcrumbs +function getElasticsearchBreadcrumbs(mainInstance: any) { + const breadcrumbs = []; + if (mainInstance.instance) { + breadcrumbs.push(createCrumb('#/elasticsearch', 'Elasticsearch')); + if (mainInstance.name === 'indices') { + breadcrumbs.push( + createCrumb( + '#/elasticsearch/indices', + i18n.translate('xpack.monitoring.breadcrumbs.es.indicesLabel', { + defaultMessage: 'Indices', + }), + 'breadcrumbEsIndices' + ) + ); + } else if (mainInstance.name === 'nodes') { + breadcrumbs.push( + createCrumb( + '#/elasticsearch/nodes', + i18n.translate('xpack.monitoring.breadcrumbs.es.nodesLabel', { defaultMessage: 'Nodes' }), + 'breadcrumbEsNodes' + ) + ); + } else if (mainInstance.name === 'ml') { + // ML Instance (for user later) + breadcrumbs.push( + createCrumb( + '#/elasticsearch/ml_jobs', + i18n.translate('xpack.monitoring.breadcrumbs.es.jobsLabel', { + defaultMessage: 'Machine learning jobs', + }) + ) + ); + } else if (mainInstance.name === 'ccr_shard') { + breadcrumbs.push( + createCrumb( + '#/elasticsearch/ccr', + i18n.translate('xpack.monitoring.breadcrumbs.es.ccrLabel', { defaultMessage: 'CCR' }) + ) + ); + } + breadcrumbs.push(createCrumb(null, mainInstance.instance)); + } else { + // don't link to Overview when we're possibly on Overview or its sibling tabs + breadcrumbs.push(createCrumb(null, 'Elasticsearch')); + } + return breadcrumbs; +} + +// generate Kibana breadcrumbs +function getKibanaBreadcrumbs(mainInstance: any) { + const breadcrumbs = []; + if (mainInstance.instance) { + breadcrumbs.push(createCrumb('#/kibana', 'Kibana')); + breadcrumbs.push( + createCrumb( + '#/kibana/instances', + i18n.translate('xpack.monitoring.breadcrumbs.kibana.instancesLabel', { + defaultMessage: 'Instances', + }) + ) + ); + breadcrumbs.push(createCrumb(null, mainInstance.instance)); + } else { + // don't link to Overview when we're possibly on Overview or its sibling tabs + breadcrumbs.push(createCrumb(null, 'Kibana')); + } + return breadcrumbs; +} + +// generate Logstash breadcrumbs +function getLogstashBreadcrumbs(mainInstance: any) { + const logstashLabel = i18n.translate('xpack.monitoring.breadcrumbs.logstashLabel', { + defaultMessage: 'Logstash', + }); + const breadcrumbs = []; + if (mainInstance.instance) { + breadcrumbs.push(createCrumb('#/logstash', logstashLabel)); + if (mainInstance.name === 'nodes') { + breadcrumbs.push( + createCrumb( + '#/logstash/nodes', + i18n.translate('xpack.monitoring.breadcrumbs.logstash.nodesLabel', { + defaultMessage: 'Nodes', + }) + ) + ); + } + breadcrumbs.push(createCrumb(null, mainInstance.instance)); + } else if (mainInstance.page === 'pipeline') { + breadcrumbs.push(createCrumb('#/logstash', logstashLabel)); + breadcrumbs.push( + createCrumb( + '#/logstash/pipelines', + i18n.translate('xpack.monitoring.breadcrumbs.logstash.pipelinesLabel', { + defaultMessage: 'Pipelines', + }) + ) + ); + } else { + // don't link to Overview when we're possibly on Overview or its sibling tabs + breadcrumbs.push(createCrumb(null, logstashLabel)); + } + + return breadcrumbs; +} + +// generate Beats breadcrumbs +function getBeatsBreadcrumbs(mainInstance: any) { + const beatsLabel = i18n.translate('xpack.monitoring.breadcrumbs.beatsLabel', { + defaultMessage: 'Beats', + }); + const breadcrumbs = []; + if (mainInstance.instance) { + breadcrumbs.push(createCrumb('#/beats', beatsLabel)); + breadcrumbs.push( + createCrumb( + '#/beats/beats', + i18n.translate('xpack.monitoring.breadcrumbs.beats.instancesLabel', { + defaultMessage: 'Instances', + }) + ) + ); + breadcrumbs.push(createCrumb(null, mainInstance.instance)); + } else { + breadcrumbs.push(createCrumb(null, beatsLabel)); + } + + return breadcrumbs; +} + +// generate Apm breadcrumbs +function getApmBreadcrumbs(mainInstance: any) { + const apmLabel = i18n.translate('xpack.monitoring.breadcrumbs.apmLabel', { + defaultMessage: 'APM server', + }); + const breadcrumbs = []; + if (mainInstance.instance) { + breadcrumbs.push(createCrumb('#/apm', apmLabel)); + breadcrumbs.push( + createCrumb( + '#/apm/instances', + i18n.translate('xpack.monitoring.breadcrumbs.apm.instancesLabel', { + defaultMessage: 'Instances', + }) + ) + ); + breadcrumbs.push(createCrumb(null, mainInstance.instance)); + } else { + // don't link to Overview when we're possibly on Overview or its sibling tabs + breadcrumbs.push(createCrumb(null, apmLabel)); + } + return breadcrumbs; +} + +function buildBreadcrumbs(clusterName: string, mainInstance?: any | null) { + const homeCrumb = i18n.translate('xpack.monitoring.breadcrumbs.clustersLabel', { + defaultMessage: 'Clusters', + }); + + let breadcrumbs = [createCrumb('#/home', homeCrumb, 'breadcrumbClusters', true)]; + + if (!mainInstance?.inOverview && clusterName) { + breadcrumbs.push(createCrumb('#/overview', clusterName)); + } + + if (mainInstance?.inElasticsearch) { + breadcrumbs = breadcrumbs.concat(getElasticsearchBreadcrumbs(mainInstance)); + } + if (mainInstance?.inKibana) { + breadcrumbs = breadcrumbs.concat(getKibanaBreadcrumbs(mainInstance)); + } + if (mainInstance?.inLogstash) { + breadcrumbs = breadcrumbs.concat(getLogstashBreadcrumbs(mainInstance)); + } + if (mainInstance?.inBeats) { + breadcrumbs = breadcrumbs.concat(getBeatsBreadcrumbs(mainInstance)); + } + if (mainInstance?.inApm) { + breadcrumbs = breadcrumbs.concat(getApmBreadcrumbs(mainInstance)); + } + + return breadcrumbs; +} +interface BreadcrumbItem { + ['data-test-subj']?: string; + href?: string; + text: string; + ignoreGlobalState?: boolean; +} + +export const useBreadcrumbs = ({ history }: { history: History }) => { + const chrome = useKibana().services.chrome; + const [breadcrumbs, setBreadcrumbs] = useState([]); + + const update = useCallback( + (bcrumbs?: BreadcrumbItem[]) => { + if (!chrome) return; + if (!bcrumbs) { + const currentBreadcrumbs: Observable & { + value?: BreadcrumbItem[]; + } = chrome.getBreadcrumbs$()?.source; + if (currentBreadcrumbs && currentBreadcrumbs.value) { + bcrumbs = currentBreadcrumbs.value; + } + } + const globalStateStr = location.hash.split('?')[1]; + if ( + !bcrumbs?.length || + globalStateStr?.indexOf('_g') !== 0 || + bcrumbs[0].href?.split('?')[1] === globalStateStr + ) { + return; + } + bcrumbs.forEach((breadcrumb: BreadcrumbItem) => { + const breadcrumbHref = breadcrumb.href?.split('?')[0]; + if (breadcrumbHref && !breadcrumb.ignoreGlobalState) { + breadcrumb.href = `${breadcrumbHref}?${globalStateStr}`; + } + delete breadcrumb.ignoreGlobalState; + }); + chrome.setBreadcrumbs(bcrumbs.slice(0)); + }, + [chrome] + ); + + const generate = useCallback( + (cluster: string, mainInstance?: any) => { + const crumbs = buildBreadcrumbs(cluster, mainInstance); + setBreadcrumbs(crumbs); + update( + crumbs.map((b) => ({ + text: b.label, + href: b.url ? b.url : undefined, + 'data-test-subj': b.testSubj, + ignoreGlobalState: b.ignoreGlobalState, + })) + ); + }, + [setBreadcrumbs, update] + ); + + useEffect(() => { + history.listen((location, action) => { + update(); + }); + }, [history, update]); + + return { + generate, + update, + breadcrumbs, + }; +}; + +export const BreadcrumbContainer = createContainer(useBreadcrumbs); diff --git a/x-pack/plugins/monitoring/public/application/pages/use_monitoring_time.tsx b/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts similarity index 100% rename from x-pack/plugins/monitoring/public/application/pages/use_monitoring_time.tsx rename to x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts diff --git a/x-pack/plugins/monitoring/public/application/index.tsx b/x-pack/plugins/monitoring/public/application/index.tsx index 19a367977ffc8..8d6c718d77ebb 100644 --- a/x-pack/plugins/monitoring/public/application/index.tsx +++ b/x-pack/plugins/monitoring/public/application/index.tsx @@ -18,7 +18,8 @@ import { GlobalStateProvider } from './global_state_context'; import { ExternalConfigContext, ExternalConfig } from './external_config_context'; import { createPreserveQueryHistory } from './preserve_query_history'; import { RouteInit } from './route_init'; -import { MonitoringTimeContainer } from './pages/use_monitoring_time'; +import { MonitoringTimeContainer } from './hooks/use_monitoring_time'; +import { BreadcrumbContainer } from './hooks/use_breadcrumbs'; export const renderApp = ( core: CoreStart, @@ -48,36 +49,38 @@ const MonitoringApp: React.FC<{ - - - - - - - - - - + + + + + + + + + + + + diff --git a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx index a7b498ddb88c1..e1b3624b1edda 100644 --- a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useContext, useState, useCallback } from 'react'; +import React, { useContext, useState, useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { CODE_PATH_ALL } from '../../../../common/constants'; import { PageTemplate } from '../page_template'; @@ -17,6 +17,7 @@ import { ExternalConfigContext } from '../../external_config_context'; import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants'; +import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; const CODE_PATHS = [CODE_PATH_ALL]; interface SetupModeProps { @@ -33,6 +34,7 @@ export const ClusterOverview: React.FC<{}> = () => { const ccs = state.ccs; const [clusters, setClusters] = useState([] as any); const [loaded, setLoaded] = useState(false); + const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); let tabs: TabMenuItem[] = []; @@ -85,6 +87,12 @@ export const ClusterOverview: React.FC<{}> = () => { } }, [ccs, clusterUuid, services.data?.query.timefilter.timefilter, services.http]); + useEffect(() => { + if (clusters && clusters.length) { + generateBreadcrumbs(clusters[0].cluster_name); + } + }, [clusters, generateBreadcrumbs]); + return ( { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-01', }, }, @@ -53,7 +52,6 @@ describe('Get Kibana Stats', () => { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, indices: 1, plugins: {}, }, @@ -77,7 +75,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-01', }, }, @@ -93,7 +90,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, indices: 1, plugins: {}, }, @@ -116,7 +112,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-01', }, }, @@ -132,7 +127,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, indices: 1, plugins: {}, }, @@ -155,7 +149,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-01', foo: { total: 5 }, xpack: { @@ -179,7 +172,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, indices: 1, plugins: { foo: { total: 5 }, fancy: { available: true, total: 15 } }, }, @@ -203,7 +195,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-01', }, }, @@ -220,7 +211,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-01', }, }, @@ -237,7 +227,6 @@ describe('Get Kibana Stats', () => { search: { total: 0 }, index_pattern: { total: 0 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-02', }, }, @@ -253,7 +242,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 2 }, - timelion_sheet: { total: 2 }, indices: 2, plugins: {}, }, @@ -276,7 +264,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-01', }, }, @@ -293,7 +280,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-01', }, }, @@ -310,7 +296,6 @@ describe('Get Kibana Stats', () => { search: { total: 3 }, index_pattern: { total: 3 }, graph_workspace: { total: 1 }, - timelion_sheet: { total: 1 }, index: '.kibana-test-02', }, }, @@ -326,7 +311,6 @@ describe('Get Kibana Stats', () => { search: { total: 4 }, index_pattern: { total: 4 }, graph_workspace: { total: 2 }, - timelion_sheet: { total: 2 }, indices: 2, plugins: {}, }, @@ -353,7 +337,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 3 }, - timelion_sheet: { total: 4 }, index: '.kibana-test-01', }, }, @@ -370,7 +353,6 @@ describe('Get Kibana Stats', () => { search: { total: 1 }, index_pattern: { total: 1 }, graph_workspace: { total: 3 }, - timelion_sheet: { total: 4 }, index: '.kibana-test-01', }, }, @@ -387,7 +369,6 @@ describe('Get Kibana Stats', () => { search: { total: 3 }, index_pattern: { total: 3 }, graph_workspace: { total: 3 }, - timelion_sheet: { total: 4 }, index: '.kibana-test-02', }, }, @@ -404,7 +385,6 @@ describe('Get Kibana Stats', () => { search: { total: 300 }, index_pattern: { total: 300 }, graph_workspace: { total: 3 }, - timelion_sheet: { total: 4 }, index: '.kibana-test-03', }, }, @@ -420,7 +400,6 @@ describe('Get Kibana Stats', () => { search: { total: 4 }, index_pattern: { total: 4 }, graph_workspace: { total: 6 }, - timelion_sheet: { total: 8 }, indices: 2, plugins: {}, }, @@ -430,7 +409,6 @@ describe('Get Kibana Stats', () => { search: { total: 300 }, index_pattern: { total: 300 }, graph_workspace: { total: 3 }, - timelion_sheet: { total: 4 }, indices: 1, plugins: {}, }, diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts b/x-pack/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts index 45ef1fbd00339..8313bcc9f5464 100644 --- a/x-pack/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts +++ b/x-pack/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts @@ -49,7 +49,6 @@ export interface ClusterUsageStats { search?: { total: number }; index_pattern?: { total: number }; graph_workspace?: { total: number }; - timelion_sheet?: { total: number }; indices: number; plugins?: { xpack?: unknown; @@ -95,7 +94,6 @@ export function getUsageStats(rawStats: estypes.SearchResponse search: rollUpTotals(rolledUpStats, currUsage, 'search'), index_pattern: rollUpTotals(rolledUpStats, currUsage, 'index_pattern'), graph_workspace: rollUpTotals(rolledUpStats, currUsage, 'graph_workspace'), - timelion_sheet: rollUpTotals(rolledUpStats, currUsage, 'timelion_sheet'), indices: rollUpIndices(rolledUpStats), }; @@ -108,7 +106,6 @@ export function getUsageStats(rawStats: estypes.SearchResponse /* eslint-disable @typescript-eslint/naming-convention */ index_pattern, graph_workspace, - timelion_sheet, /* eslint-enable @typescript-eslint/naming-convention */ xpack, ...pluginsTop diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/index.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/index.tsx index c4d455fb43b7f..fc246641101e3 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/index.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts_flyout/index.tsx @@ -127,9 +127,9 @@ export function AlertsFlyout({ ]; return ( - + - +

{alertData.fields[ALERT_RULE_NAME]}

@@ -141,13 +141,27 @@ export function AlertsFlyout({ compressed={true} type="responsiveColumn" listItems={overviewListItems} + titleProps={ + { + 'data-test-subj': 'alertsFlyoutDescriptionListTitle', + } as any // NOTE / TODO: This "any" is a temporary workaround: https://github.com/elastic/eui/issues/5148 + } + descriptionProps={ + { + 'data-test-subj': 'alertsFlyoutDescriptionListDescription', + } as any // NOTE / TODO: This "any" is a temporary workaround: https://github.com/elastic/eui/issues/5148 + } /> {alertData.link && !isInApp && ( - + View in app 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 2d325b6f3f7c4..bca8c8095511e 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 @@ -254,6 +254,7 @@ function ObservabilityActions({ iconType="expand" color="text" onClick={() => setFlyoutAlert(alert)} + data-test-subj="openFlyoutButton" /> diff --git a/x-pack/plugins/security/server/authentication/authenticator.test.ts b/x-pack/plugins/security/server/authentication/authenticator.test.ts index 27dfd89a31756..b2e33feb8cce2 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.test.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.test.ts @@ -1478,6 +1478,29 @@ describe('Authenticator', () => { ); expect(mockBasicAuthenticationProvider.authenticate).not.toHaveBeenCalled(); }); + + it('redirects to the Login Selector with auth provider hint when needed.', async () => { + const request = httpServerMock.createKibanaRequest({ + query: { [AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER]: 'custom1' }, + }); + + // Includes hint if there is no active session. + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.redirectTo( + '/mock-server-basepath/login?next=%2Fmock-server-basepath%2Fpath%3Fauth_provider_hint%3Dcustom1&auth_provider_hint=custom1' + ) + ); + + // Includes hint if session is unauthenticated. + mockOptions.session.get.mockResolvedValue({ ...mockSessVal, username: undefined }); + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.redirectTo( + '/mock-server-basepath/login?next=%2Fmock-server-basepath%2Fpath%3Fauth_provider_hint%3Dcustom1&auth_provider_hint=custom1' + ) + ); + + expect(mockBasicAuthenticationProvider.authenticate).not.toHaveBeenCalled(); + }); }); describe('with Access Agreement', () => { diff --git a/x-pack/plugins/security/server/authentication/authenticator.ts b/x-pack/plugins/security/server/authentication/authenticator.ts index 5252f5c618f97..f0ade974e4d56 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.ts @@ -349,27 +349,33 @@ export class Authenticator { assertRequest(request); const existingSessionValue = await this.getSessionValue(request); - const suggestedProviderName = - existingSessionValue?.provider.name ?? - request.url.searchParams.get(AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER); - if (this.shouldRedirectToLoginSelector(request, existingSessionValue)) { - this.logger.debug('Redirecting request to Login Selector.'); + const providerNameSuggestedByHint = request.url.searchParams.get( + AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER + ); + this.logger.debug( + `Redirecting request to Login Selector (provider hint: ${ + providerNameSuggestedByHint ?? 'n/a' + }).` + ); return AuthenticationResult.redirectTo( `${ this.options.basePath.serverBasePath }/login?${NEXT_URL_QUERY_STRING_PARAMETER}=${encodeURIComponent( `${this.options.basePath.get(request)}${request.url.pathname}${request.url.search}` )}${ - suggestedProviderName && !existingSessionValue + providerNameSuggestedByHint ? `&${AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER}=${encodeURIComponent( - suggestedProviderName + providerNameSuggestedByHint )}` : '' }` ); } + const suggestedProviderName = + existingSessionValue?.provider.name ?? + request.url.searchParams.get(AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER); for (const [providerName, provider] of this.providerIterator(suggestedProviderName)) { // Check if current session has been set by this provider. const ownsSession = diff --git a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts index 2c742ad3fd6af..942aed4166595 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts @@ -244,3 +244,4 @@ export const policyFactoryWithSupportedFeatures = ( * Reflects what string the Endpoint will use when message field is default/empty */ export const DefaultPolicyNotificationMessage = 'Elastic Security {action} {filename}'; +export const DefaultPolicyRuleNotificationMessage = 'Elastic Security {action} {rule}'; diff --git a/x-pack/plugins/security_solution/common/license/policy_config.test.ts b/x-pack/plugins/security_solution/common/license/policy_config.test.ts index 98e19b36dd910..725d6ba74afd0 100644 --- a/x-pack/plugins/security_solution/common/license/policy_config.test.ts +++ b/x-pack/plugins/security_solution/common/license/policy_config.test.ts @@ -11,6 +11,7 @@ import { } from './policy_config'; import { DefaultPolicyNotificationMessage, + DefaultPolicyRuleNotificationMessage, policyFactory, policyFactoryWithSupportedFeatures, policyFactoryWithoutPaidFeatures, @@ -367,7 +368,7 @@ describe('policy_config and licenses', () => { expect(retPolicy.windows.popup.memory_protection.message).not.toEqual(popupMessage); // need to invert the test, since it could be either value - expect(['', DefaultPolicyNotificationMessage]).toContain( + expect(['', DefaultPolicyRuleNotificationMessage]).toContain( retPolicy.windows.popup.memory_protection.message ); }); @@ -391,7 +392,7 @@ describe('policy_config and licenses', () => { expect(retPolicy.windows.popup.behavior_protection.message).not.toEqual(popupMessage); // need to invert the test, since it could be either value - expect(['', DefaultPolicyNotificationMessage]).toContain( + expect(['', DefaultPolicyRuleNotificationMessage]).toContain( retPolicy.windows.popup.behavior_protection.message ); @@ -402,7 +403,7 @@ describe('policy_config and licenses', () => { expect(retPolicy.mac.popup.behavior_protection.message).not.toEqual(popupMessage); // need to invert the test, since it could be either value - expect(['', DefaultPolicyNotificationMessage]).toContain( + expect(['', DefaultPolicyRuleNotificationMessage]).toContain( retPolicy.mac.popup.behavior_protection.message ); @@ -415,7 +416,7 @@ describe('policy_config and licenses', () => { expect(retPolicy.linux.popup.behavior_protection.message).not.toEqual(popupMessage); // need to invert the test, since it could be either value - expect(['', DefaultPolicyNotificationMessage]).toContain( + expect(['', DefaultPolicyRuleNotificationMessage]).toContain( retPolicy.linux.popup.behavior_protection.message ); }); diff --git a/x-pack/plugins/security_solution/common/license/policy_config.ts b/x-pack/plugins/security_solution/common/license/policy_config.ts index a684628a7d52c..a05478eef8eba 100644 --- a/x-pack/plugins/security_solution/common/license/policy_config.ts +++ b/x-pack/plugins/security_solution/common/license/policy_config.ts @@ -10,6 +10,7 @@ import { isAtLeast } from './license'; import { PolicyConfig } from '../endpoint/types'; import { DefaultPolicyNotificationMessage, + DefaultPolicyRuleNotificationMessage, policyFactoryWithoutPaidFeatures, policyFactoryWithSupportedFeatures, } from '../endpoint/models/policy_config'; @@ -113,7 +114,7 @@ function isEndpointMemoryPolicyValidForLicense(policy: PolicyConfig, license: IL if ( policy.windows.popup.memory_protection.message !== '' && - policy.windows.popup.memory_protection.message !== DefaultPolicyNotificationMessage + policy.windows.popup.memory_protection.message !== DefaultPolicyRuleNotificationMessage ) { return false; } @@ -164,11 +165,11 @@ function isEndpointBehaviorPolicyValidForLicense(policy: PolicyConfig, license: // Only Platinum or higher may change the behavior_protection message (which can be blank or what Endpoint defaults) if ( (policy.windows.popup.behavior_protection.message !== '' && - policy.windows.popup.behavior_protection.message !== DefaultPolicyNotificationMessage) || + policy.windows.popup.behavior_protection.message !== DefaultPolicyRuleNotificationMessage) || (policy.mac.popup.behavior_protection.message !== '' && - policy.mac.popup.behavior_protection.message !== DefaultPolicyNotificationMessage) || + policy.mac.popup.behavior_protection.message !== DefaultPolicyRuleNotificationMessage) || (policy.linux.popup.behavior_protection.message !== '' && - policy.linux.popup.behavior_protection.message !== DefaultPolicyNotificationMessage) + policy.linux.popup.behavior_protection.message !== DefaultPolicyRuleNotificationMessage) ) { return false; } diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts index 0d624980f57d7..4e99f330e0f28 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts @@ -34,6 +34,15 @@ export enum MatrixHistogramType { dns = 'dns', } +export const MatrixHistogramTypeToAggName = { + [MatrixHistogramType.alerts]: 'aggregations.alertsGroup.buckets', + [MatrixHistogramType.anomalies]: 'aggregations.anomalyActionGroup.buckets', + [MatrixHistogramType.authentications]: 'aggregations.eventActionGroup.buckets', + [MatrixHistogramType.authenticationsEntities]: 'aggregations.events.buckets', + [MatrixHistogramType.dns]: 'aggregations.dns_name_query_count.buckets', + [MatrixHistogramType.events]: 'aggregations.eventActionGroup.buckets', +}; + export interface MatrixHistogramRequestOptions extends RequestBasicOptions { timerange: TimerangeInput; histogramType: MatrixHistogramType; diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts index c65c1a8fdd841..e09dbe23d512a 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts @@ -14,6 +14,22 @@ import { TestProviders } from '../../mock/test_providers'; jest.mock('../../../common/lib/kibana'); +const basicResponse = { + isPartial: false, + isRunning: false, + total: 0, + loaded: 0, + rawResponse: { + took: 1, + timed_out: false, + hits: { + max_score: 0, + hits: [], + total: 0, + }, + }, +}; + describe('useMatrixHistogram', () => { const props = { endDate: new Date(Date.now()).toISOString(), @@ -57,6 +73,78 @@ describe('useMatrixHistogram', () => { expect(result1).toBe(result2); }); + + it("returns buckets for histogram Type 'events'", async () => { + const localProps = { ...props, histogramType: MatrixHistogramType.events }; + const mockEventsSearchStrategyResponse = { + ...basicResponse, + rawResponse: { + ...basicResponse.rawResponse, + aggregations: { + eventActionGroup: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'my dsn test buckets', + doc_count: 1, + }, + ], + }, + }, + }, + }; + + (useKibana().services.data.search.search as jest.Mock).mockReturnValueOnce({ + subscribe: ({ next }: { next: Function }) => next(mockEventsSearchStrategyResponse), + }); + + const { + result: { current }, + } = renderHook(() => useMatrixHistogram(localProps), { + wrapper: TestProviders, + }); + + expect(current[1].buckets).toBe( + mockEventsSearchStrategyResponse.rawResponse.aggregations?.eventActionGroup.buckets + ); + }); + + it("returns buckets for histogram Type 'dns'", async () => { + const mockDnsSearchStrategyResponse = { + ...basicResponse, + rawResponse: { + ...basicResponse.rawResponse, + aggregations: { + dns_name_query_count: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'my dsn test buckets', + doc_count: 1, + }, + ], + }, + }, + }, + }; + + const localProps = { ...props, histogramType: MatrixHistogramType.dns }; + (useKibana().services.data.search.search as jest.Mock).mockReturnValueOnce({ + subscribe: ({ next }: { next: Function }) => next(mockDnsSearchStrategyResponse), + }); + + const { + result: { current }, + } = renderHook(() => useMatrixHistogram(localProps), { + wrapper: TestProviders, + }); + + expect(current[1].buckets).toBe( + mockDnsSearchStrategyResponse.rawResponse.aggregations?.dns_name_query_count.buckets + ); + }); }); describe('useMatrixHistogramCombined', () => { diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts index 1be71423899e2..52d8fc150ac20 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts @@ -19,6 +19,7 @@ import { MatrixHistogramRequestOptions, MatrixHistogramStrategyResponse, MatrixHistogramData, + MatrixHistogramTypeToAggName, } from '../../../../common/search_strategy/security_solution'; import { isErrorResponse, isCompleteResponse } from '../../../../../../../src/plugins/data/common'; import { getInspectResponse } from '../../../helpers'; @@ -132,8 +133,8 @@ export const useMatrixHistogram = ({ if (isCompleteResponse(response)) { const histogramBuckets: Buckets = getOr( bucketEmpty, - 'rawResponse.aggregations.eventActionGroup.buckets', - response + MatrixHistogramTypeToAggName[histogramType], + response.rawResponse ); setLoading(false); setMatrixHistogramResponse((prevResponse) => ({ @@ -165,7 +166,7 @@ export const useMatrixHistogram = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, errorMessage, addError, addWarning] + [data.search, errorMessage, addError, addWarning, histogramType] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts index 6f8a41f4559de..250d9c82b6489 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts @@ -6,7 +6,10 @@ */ import { IHttpFetchError } from 'kibana/public'; -import { DefaultPolicyNotificationMessage } from '../../../../../../common/endpoint/models/policy_config'; +import { + DefaultPolicyNotificationMessage, + DefaultPolicyRuleNotificationMessage, +} from '../../../../../../common/endpoint/models/policy_config'; import { PolicyDetailsState, UpdatePolicyResponse } from '../../types'; import { policyIdFromParams, @@ -50,20 +53,20 @@ export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory { + if (protectionType === 'memory_protection') { + return i18n.translate( + 'xpack.securitySolution.endpoint.policyDetail.memoryProtectionTooltip', + { + defaultMessage: 'memory threat', + } + ); + } else if (protectionType === 'behavior_protection') { + return i18n.translate( + 'xpack.securitySolution.endpoint.policyDetail.behaviorProtectionTooltip', + { + defaultMessage: 'malicious behavior', + } + ); + } else { + return protectionType; + } + }; + + const tooltipBracketText = (protectionType: PolicyProtection) => { + if (protectionType === 'memory_protection' || protection === 'behavior_protection') { + return i18n.translate('xpack.securitySolution.endpoint.policyDetail.rule', { + defaultMessage: 'rule', + }); + } else { + return i18n.translate('xpack.securitySolution.endpoint.policyDetail.filename', { + defaultMessage: 'filename', + }); + } + }; + return ( <> @@ -139,14 +171,17 @@ export const UserNotification = React.memo( id="xpack.securitySolution.endpoint.policyDetailsConfig.notifyUserTooltip.a" defaultMessage="Selecting the user notification option will display a notification to the host user when { protectionName } is prevented or detected." values={{ - protectionName: protection, + protectionName: tooltipProtectionText(protection), }} /> } diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/map_config.ts b/x-pack/plugins/security_solution/public/network/components/embeddables/map_config.ts index ecbb80123e07e..042777491d9c3 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/map_config.ts +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/map_config.ts @@ -14,7 +14,7 @@ import { LayerMappingDetails, } from './types'; import * as i18n from './translations'; -import { SOURCE_TYPES } from '../../../../../maps/common/constants'; +import { SOURCE_TYPES } from '../../../../../maps/common'; const euiVisColorPalette = euiPaletteColorBlind(); // Update field mappings to modify what fields will be returned to map tooltip diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/map_tool_tip.test.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/map_tool_tip.test.tsx index bc869dead4556..7d5e34df8c8fc 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/map_tool_tip.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/map_tool_tip/map_tool_tip.test.tsx @@ -9,7 +9,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import '../../../../common/mock/match_media'; import { MapToolTipComponent } from './map_tool_tip'; -import { TooltipFeature } from '../../../../../../maps/common/descriptor_types'; +import { TooltipFeature } from '../../../../../../maps/common'; describe('MapToolTip', () => { test('placeholder component renders correctly against snapshot', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/__snapshots__/get_signals_template.test.ts.snap b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/__snapshots__/get_signals_template.test.ts.snap index 3c065ab0ac109..1d4e84ea5dccf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/__snapshots__/get_signals_template.test.ts.snap +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/__snapshots__/get_signals_template.test.ts.snap @@ -1,5 +1,609 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`get_signals_template backwards compatibility mappings for version 45 should match snapshot 1`] = ` +Object { + "_meta": Object { + "aliases_version": 1, + "version": 45, + }, + "properties": Object { + "kibana.alert.ancestors.depth": Object { + "path": "signal.ancestors.depth", + "type": "alias", + }, + "kibana.alert.ancestors.id": Object { + "path": "signal.ancestors.id", + "type": "alias", + }, + "kibana.alert.ancestors.index": Object { + "path": "signal.ancestors.index", + "type": "alias", + }, + "kibana.alert.ancestors.type": Object { + "path": "signal.ancestors.type", + "type": "alias", + }, + "kibana.alert.depth": Object { + "path": "signal.depth", + "type": "alias", + }, + "kibana.alert.original_event.action": Object { + "path": "signal.original_event.action", + "type": "alias", + }, + "kibana.alert.original_event.category": Object { + "path": "signal.original_event.category", + "type": "alias", + }, + "kibana.alert.original_event.code": Object { + "path": "signal.original_event.code", + "type": "alias", + }, + "kibana.alert.original_event.created": Object { + "path": "signal.original_event.created", + "type": "alias", + }, + "kibana.alert.original_event.dataset": Object { + "path": "signal.original_event.dataset", + "type": "alias", + }, + "kibana.alert.original_event.duration": Object { + "path": "signal.original_event.duration", + "type": "alias", + }, + "kibana.alert.original_event.end": Object { + "path": "signal.original_event.end", + "type": "alias", + }, + "kibana.alert.original_event.hash": Object { + "path": "signal.original_event.hash", + "type": "alias", + }, + "kibana.alert.original_event.id": Object { + "path": "signal.original_event.id", + "type": "alias", + }, + "kibana.alert.original_event.kind": Object { + "path": "signal.original_event.kind", + "type": "alias", + }, + "kibana.alert.original_event.module": Object { + "path": "signal.original_event.module", + "type": "alias", + }, + "kibana.alert.original_event.outcome": Object { + "path": "signal.original_event.outcome", + "type": "alias", + }, + "kibana.alert.original_event.provider": Object { + "path": "signal.original_event.provider", + "type": "alias", + }, + "kibana.alert.original_event.reason": Object { + "path": "signal.original_event.reason", + "type": "alias", + }, + "kibana.alert.original_event.risk_score": Object { + "path": "signal.original_event.risk_score", + "type": "alias", + }, + "kibana.alert.original_event.risk_score_norm": Object { + "path": "signal.original_event.risk_score_norm", + "type": "alias", + }, + "kibana.alert.original_event.sequence": Object { + "path": "signal.original_event.sequence", + "type": "alias", + }, + "kibana.alert.original_event.severity": Object { + "path": "signal.original_event.severity", + "type": "alias", + }, + "kibana.alert.original_event.start": Object { + "path": "signal.original_event.start", + "type": "alias", + }, + "kibana.alert.original_event.timezone": Object { + "path": "signal.original_event.timezone", + "type": "alias", + }, + "kibana.alert.original_event.type": Object { + "path": "signal.original_event.type", + "type": "alias", + }, + "kibana.alert.original_time": Object { + "path": "signal.original_time", + "type": "alias", + }, + "kibana.alert.reason": Object { + "path": "signal.reason", + "type": "alias", + }, + "kibana.alert.risk_score": Object { + "path": "signal.rule.risk_score", + "type": "alias", + }, + "kibana.alert.rule.author": Object { + "path": "signal.rule.author", + "type": "alias", + }, + "kibana.alert.rule.building_block_type": Object { + "path": "signal.rule.building_block_type", + "type": "alias", + }, + "kibana.alert.rule.created_at": Object { + "path": "signal.rule.created_at", + "type": "alias", + }, + "kibana.alert.rule.created_by": Object { + "path": "signal.rule.created_by", + "type": "alias", + }, + "kibana.alert.rule.description": Object { + "path": "signal.rule.description", + "type": "alias", + }, + "kibana.alert.rule.enabled": Object { + "path": "signal.rule.enabled", + "type": "alias", + }, + "kibana.alert.rule.false_positives": Object { + "path": "signal.rule.false_positives", + "type": "alias", + }, + "kibana.alert.rule.from": Object { + "path": "signal.rule.from", + "type": "alias", + }, + "kibana.alert.rule.immutable": Object { + "path": "signal.rule.immutable", + "type": "alias", + }, + "kibana.alert.rule.index": Object { + "path": "signal.rule.index", + "type": "alias", + }, + "kibana.alert.rule.interval": Object { + "path": "signal.rule.interval", + "type": "alias", + }, + "kibana.alert.rule.language": Object { + "path": "signal.rule.language", + "type": "alias", + }, + "kibana.alert.rule.license": Object { + "path": "signal.rule.license", + "type": "alias", + }, + "kibana.alert.rule.max_signals": Object { + "path": "signal.rule.max_signals", + "type": "alias", + }, + "kibana.alert.rule.name": Object { + "path": "signal.rule.name", + "type": "alias", + }, + "kibana.alert.rule.note": Object { + "path": "signal.rule.note", + "type": "alias", + }, + "kibana.alert.rule.query": Object { + "path": "signal.rule.query", + "type": "alias", + }, + "kibana.alert.rule.references": Object { + "path": "signal.rule.references", + "type": "alias", + }, + "kibana.alert.rule.risk_score_mapping.field": Object { + "path": "signal.rule.risk_score_mapping.field", + "type": "alias", + }, + "kibana.alert.rule.risk_score_mapping.operator": Object { + "path": "signal.rule.risk_score_mapping.operator", + "type": "alias", + }, + "kibana.alert.rule.risk_score_mapping.value": Object { + "path": "signal.rule.risk_score_mapping.value", + "type": "alias", + }, + "kibana.alert.rule.rule_id": Object { + "path": "signal.rule.rule_id", + "type": "alias", + }, + "kibana.alert.rule.rule_name_override": Object { + "path": "signal.rule.rule_name_override", + "type": "alias", + }, + "kibana.alert.rule.saved_id": Object { + "path": "signal.rule.saved_id", + "type": "alias", + }, + "kibana.alert.rule.severity_mapping.field": Object { + "path": "signal.rule.severity_mapping.field", + "type": "alias", + }, + "kibana.alert.rule.severity_mapping.operator": Object { + "path": "signal.rule.severity_mapping.operator", + "type": "alias", + }, + "kibana.alert.rule.severity_mapping.severity": Object { + "path": "signal.rule.severity_mapping.severity", + "type": "alias", + }, + "kibana.alert.rule.severity_mapping.value": Object { + "path": "signal.rule.severity_mapping.value", + "type": "alias", + }, + "kibana.alert.rule.tags": Object { + "path": "signal.rule.tags", + "type": "alias", + }, + "kibana.alert.rule.threat.framework": Object { + "path": "signal.rule.threat.framework", + "type": "alias", + }, + "kibana.alert.rule.threat.tactic.id": Object { + "path": "signal.rule.threat.tactic.id", + "type": "alias", + }, + "kibana.alert.rule.threat.tactic.name": Object { + "path": "signal.rule.threat.tactic.name", + "type": "alias", + }, + "kibana.alert.rule.threat.tactic.reference": Object { + "path": "signal.rule.threat.tactic.reference", + "type": "alias", + }, + "kibana.alert.rule.threat.technique.id": Object { + "path": "signal.rule.threat.technique.id", + "type": "alias", + }, + "kibana.alert.rule.threat.technique.name": Object { + "path": "signal.rule.threat.technique.name", + "type": "alias", + }, + "kibana.alert.rule.threat.technique.reference": Object { + "path": "signal.rule.threat.technique.reference", + "type": "alias", + }, + "kibana.alert.rule.threat.technique.subtechnique.id": Object { + "path": "signal.rule.threat.technique.subtechnique.id", + "type": "alias", + }, + "kibana.alert.rule.threat.technique.subtechnique.name": Object { + "path": "signal.rule.threat.technique.subtechnique.name", + "type": "alias", + }, + "kibana.alert.rule.threat.technique.subtechnique.reference": Object { + "path": "signal.rule.threat.technique.subtechnique.reference", + "type": "alias", + }, + "kibana.alert.rule.threat_index": Object { + "path": "signal.rule.threat_index", + "type": "alias", + }, + "kibana.alert.rule.threat_indicator_path": Object { + "path": "signal.rule.threat_indicator_path", + "type": "alias", + }, + "kibana.alert.rule.threat_language": Object { + "path": "signal.rule.threat_language", + "type": "alias", + }, + "kibana.alert.rule.threat_mapping.entries.field": Object { + "path": "signal.rule.threat_mapping.entries.field", + "type": "alias", + }, + "kibana.alert.rule.threat_mapping.entries.type": Object { + "path": "signal.rule.threat_mapping.entries.type", + "type": "alias", + }, + "kibana.alert.rule.threat_mapping.entries.value": Object { + "path": "signal.rule.threat_mapping.entries.value", + "type": "alias", + }, + "kibana.alert.rule.threat_query": Object { + "path": "signal.rule.threat_query", + "type": "alias", + }, + "kibana.alert.rule.threshold.field": Object { + "path": "signal.rule.threshold.field", + "type": "alias", + }, + "kibana.alert.rule.threshold.value": Object { + "path": "signal.rule.threshold.value", + "type": "alias", + }, + "kibana.alert.rule.timeline_id": Object { + "path": "signal.rule.timeline_id", + "type": "alias", + }, + "kibana.alert.rule.timeline_title": Object { + "path": "signal.rule.timeline_title", + "type": "alias", + }, + "kibana.alert.rule.to": Object { + "path": "signal.rule.to", + "type": "alias", + }, + "kibana.alert.rule.type": Object { + "path": "signal.rule.type", + "type": "alias", + }, + "kibana.alert.rule.updated_at": Object { + "path": "signal.rule.updated_at", + "type": "alias", + }, + "kibana.alert.rule.updated_by": Object { + "path": "signal.rule.updated_by", + "type": "alias", + }, + "kibana.alert.rule.uuid": Object { + "path": "signal.rule.id", + "type": "alias", + }, + "kibana.alert.rule.version": Object { + "path": "signal.rule.version", + "type": "alias", + }, + "kibana.alert.severity": Object { + "path": "signal.rule.severity", + "type": "alias", + }, + "kibana.alert.threshold_result.cardinality.field": Object { + "path": "signal.threshold_result.cardinality.field", + "type": "alias", + }, + "kibana.alert.threshold_result.cardinality.value": Object { + "path": "signal.threshold_result.cardinality.value", + "type": "alias", + }, + "kibana.alert.threshold_result.count": Object { + "path": "signal.threshold_result.count", + "type": "alias", + }, + "kibana.alert.threshold_result.from": Object { + "path": "signal.threshold_result.from", + "type": "alias", + }, + "kibana.alert.threshold_result.terms.field": Object { + "path": "signal.threshold_result.terms.field", + "type": "alias", + }, + "kibana.alert.threshold_result.terms.value": Object { + "path": "signal.threshold_result.terms.value", + "type": "alias", + }, + "kibana.alert.workflow_status": Object { + "path": "signal.status", + "type": "alias", + }, + "signal": Object { + "properties": Object { + "_meta": Object { + "properties": Object { + "version": Object { + "type": "long", + }, + }, + "type": "object", + }, + "ancestors": Object { + "properties": Object { + "depth": Object { + "type": "long", + }, + "id": Object { + "type": "keyword", + }, + "index": Object { + "type": "keyword", + }, + "rule": Object { + "type": "keyword", + }, + "type": Object { + "type": "keyword", + }, + }, + }, + "depth": Object { + "type": "integer", + }, + "group": Object { + "properties": Object { + "id": Object { + "type": "keyword", + }, + "index": Object { + "type": "integer", + }, + }, + "type": "object", + }, + "original_event": Object { + "properties": Object { + "reason": Object { + "type": "keyword", + }, + }, + "type": "object", + }, + "reason": Object { + "type": "keyword", + }, + "rule": Object { + "properties": Object { + "author": Object { + "type": "keyword", + }, + "building_block_type": Object { + "type": "keyword", + }, + "license": Object { + "type": "keyword", + }, + "note": Object { + "type": "text", + }, + "risk_score_mapping": Object { + "properties": Object { + "field": Object { + "type": "keyword", + }, + "operator": Object { + "type": "keyword", + }, + "value": Object { + "type": "keyword", + }, + }, + "type": "object", + }, + "rule_name_override": Object { + "type": "keyword", + }, + "severity_mapping": Object { + "properties": Object { + "field": Object { + "type": "keyword", + }, + "operator": Object { + "type": "keyword", + }, + "severity": Object { + "type": "keyword", + }, + "value": Object { + "type": "keyword", + }, + }, + "type": "object", + }, + "threat": Object { + "properties": Object { + "technique": Object { + "properties": Object { + "subtechnique": Object { + "properties": Object { + "id": Object { + "type": "keyword", + }, + "name": Object { + "type": "keyword", + }, + "reference": Object { + "type": "keyword", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + "threat_index": Object { + "type": "keyword", + }, + "threat_indicator_path": Object { + "type": "keyword", + }, + "threat_language": Object { + "type": "keyword", + }, + "threat_mapping": Object { + "properties": Object { + "entries": Object { + "properties": Object { + "field": Object { + "type": "keyword", + }, + "type": Object { + "type": "keyword", + }, + "value": Object { + "type": "keyword", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + "threat_query": Object { + "type": "keyword", + }, + "threshold": Object { + "properties": Object { + "field": Object { + "type": "keyword", + }, + "value": Object { + "type": "float", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + "threshold_result": Object { + "properties": Object { + "cardinality": Object { + "properties": Object { + "field": Object { + "type": "keyword", + }, + "value": Object { + "type": "long", + }, + }, + }, + "count": Object { + "type": "long", + }, + "from": Object { + "type": "date", + }, + "terms": Object { + "properties": Object { + "field": Object { + "type": "keyword", + }, + "value": Object { + "type": "keyword", + }, + }, + }, + }, + }, + }, + "type": "object", + }, + }, + "runtime": Object { + "host.os.name.caseless": Object { + "script": Object { + "source": "if(doc['host.os.name'].size()!=0) emit(doc['host.os.name'].value.toLowerCase());", + }, + "type": "keyword", + }, + }, +} +`; + +exports[`get_signals_template backwards compatibility mappings for version 57 should match snapshot 1`] = ` +Object { + "_meta": Object { + "aliases_version": 1, + "version": 57, + }, +} +`; + exports[`get_signals_template it should match snapshot 1`] = ` Object { "index_patterns": Array [ @@ -1495,6 +2099,11 @@ Object { }, "name": Object { "fields": Object { + "caseless": Object { + "ignore_above": 1024, + "normalizer": "lowercase", + "type": "keyword", + }, "text": Object { "norms": false, "type": "text", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts index d65a1ad87b41a..61635fdcef9f0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts @@ -23,11 +23,9 @@ import type { import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; import { buildSiemResponse } from '../utils'; import { - createSignalsFieldAliases, getSignalsTemplate, SIGNALS_TEMPLATE_VERSION, - SIGNALS_FIELD_ALIASES_VERSION, - ALIAS_VERSION_FIELD, + createBackwardsCompatibilityMapping, } from './get_signals_template'; import { ensureMigrationCleanupPolicy } from '../../migrations/migration_cleanup'; import signalsPolicy from './signals_policy.json'; @@ -35,7 +33,6 @@ import { templateNeedsUpdate } from './check_template_version'; import { getIndexVersion } from './get_index_version'; import { isOutdated } from '../../migrations/helpers'; import { RuleDataPluginService } from '../../../../../../rule_registry/server'; -import signalExtraFields from './signal_extra_fields.json'; import { ConfigType } from '../../../../config'; import { parseExperimentalConfigValue } from '../../../../../common/experimental_features'; @@ -126,7 +123,7 @@ export const createDetectionIndex = async ( } if (indexExists) { - await addFieldAliasesToIndices({ esClient, index, spaceId }); + await addFieldAliasesToIndices({ esClient, index }); // The internal user is used here because Elasticsearch requires the PUT alias requestor to have 'manage' permissions // for BOTH the index AND alias name. However, through 7.14 admins only needed permissions for .siem-signals (the index) // and not .alerts-security.alerts (the alias). From the security solution perspective, all .siem-signals--* @@ -148,33 +145,17 @@ export const createDetectionIndex = async ( const addFieldAliasesToIndices = async ({ esClient, index, - spaceId, }: { esClient: ElasticsearchClient; index: string; - spaceId: string; }) => { const { body: indexMappings } = await esClient.indices.get({ index }); - // Make sure that all signal fields we add aliases for are guaranteed to exist in the mapping for ALL historical - // signals indices (either by adding them to signalExtraFields or ensuring they exist in the original signals - // mapping) or else this call will fail and not update ANY signals indices - const fieldAliases = createSignalsFieldAliases(); for (const [indexName, mapping] of Object.entries(indexMappings)) { const currentVersion: number | undefined = get(mapping.mappings?._meta, 'version'); - const newMapping = { - properties: { - ...signalExtraFields, - ...fieldAliases, - // ...getRbacRequiredFields(spaceId), - }, - _meta: { - version: currentVersion, - [ALIAS_VERSION_FIELD]: SIGNALS_FIELD_ALIASES_VERSION, - }, - }; + const body = createBackwardsCompatibilityMapping(currentVersion ?? 0); await esClient.indices.putMapping({ index: indexName, - body: newMapping, + body, allow_no_indices: true, } as estypes.IndicesPutMappingRequest); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.test.ts index bb67dd1fca6df..70363cba34fce 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { getSignalsTemplate } from './get_signals_template'; +import { createBackwardsCompatibilityMapping, getSignalsTemplate } from './get_signals_template'; describe('get_signals_template', () => { test('it should set the lifecycle "name" and "rollover_alias" to be the name of the index passed in', () => { @@ -124,4 +124,14 @@ describe('get_signals_template', () => { ); expect(template).toMatchSnapshot(); }); + + test('backwards compatibility mappings for version 45 should match snapshot', () => { + const mapping = createBackwardsCompatibilityMapping(45); + expect(mapping).toMatchSnapshot(); + }); + + test('backwards compatibility mappings for version 57 should match snapshot', () => { + const mapping = createBackwardsCompatibilityMapping(57); + expect(mapping).toMatchSnapshot(); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts index 3470f955dbdba..b7a0521e5c3ce 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts @@ -11,10 +11,12 @@ import { ALERT_RULE_PRODUCER, ALERT_RULE_TYPE_ID, } from '@kbn/rule-data-utils'; +import { merge } from 'lodash'; import signalsMapping from './signals_mapping.json'; import ecsMapping from './ecs_mapping.json'; import otherMapping from './other_mappings.json'; import aadFieldConversion from './signal_aad_mapping.json'; +import signalExtraFields from './signal_extra_fields.json'; /** @constant @@ -22,7 +24,9 @@ import aadFieldConversion from './signal_aad_mapping.json'; @description This value represents the template version assumed by app code. If this number is greater than the user's signals index version, the detections UI will attempt to update the signals template and roll over to - a new signals index. + a new signals index. + + Since we create a new index for new versions, this version on an existing index should never change. If making mappings changes in a patch release, this number should be incremented by 1. If making mappings changes in a minor release, this number should be @@ -34,12 +38,24 @@ export const SIGNALS_TEMPLATE_VERSION = 57; @constant @type {number} @description This value represents the version of the field aliases that map the new field names - used for alerts-as-data to the old signal.* field names. If any .siem-signals- indices - have an aliases_version less than this value, the detections UI will call create_index_route and - and go through the index update process. Increment this number if making changes to the field - aliases we use to make signals forwards-compatible. + used for alerts-as-data to the old signal.* field names and any other runtime fields that are added + to .siem-signals indices for compatibility reasons (e.g. host.os.name.caseless). + + This version number can change over time on existing indices as we add backwards compatibility fields. + + If any .siem-signals- indices have an aliases_version less than this value, the detections + UI will call create_index_route and and go through the index update process. Increment this number if + making changes to the field aliases we use to make signals forwards-compatible. */ export const SIGNALS_FIELD_ALIASES_VERSION = 1; + +/** + @constant + @type {number} + @description This value represents the minimum required index version (SIGNALS_TEMPLATE_VERSION) for EQL + rules to write signals correctly. If the write index has a `version` less than this value, the EQL rule + will throw an error on execution. +*/ export const MIN_EQL_RULE_INDEX_VERSION = 2; export const ALIAS_VERSION_FIELD = 'aliases_version'; @@ -68,13 +84,12 @@ export const getSignalsTemplate = (index: string, spaceId: string, aadIndexAlias }, mappings: { dynamic: false, - properties: { - ...ecsMapping.mappings.properties, - ...otherMapping.mappings.properties, - ...fieldAliases, - // ...getRbacRequiredFields(spaceId), - signal: signalsMapping.mappings.properties.signal, - }, + properties: merge( + ecsMapping.mappings.properties, + otherMapping.mappings.properties, + fieldAliases, + signalsMapping.mappings.properties + ), _meta: { version: SIGNALS_TEMPLATE_VERSION, [ALIAS_VERSION_FIELD]: SIGNALS_FIELD_ALIASES_VERSION, @@ -97,6 +112,47 @@ export const createSignalsFieldAliases = () => { return fieldAliases; }; +export const backwardsCompatibilityMappings = [ + { + minVersion: 0, + // Version 45 shipped with 7.14 + maxVersion: 45, + mapping: { + runtime: { + 'host.os.name.caseless': { + type: 'keyword', + script: { + source: + "if(doc['host.os.name'].size()!=0) emit(doc['host.os.name'].value.toLowerCase());", + }, + }, + }, + properties: { + // signalExtraFields contains the field mappings that have been added to the signals indices over time. + // We need to include these here because we can't add an alias for a field that isn't in the mapping, + // and we want to apply the aliases to all old signals indices at the same time. + ...signalExtraFields, + ...createSignalsFieldAliases(), + }, + }, + }, +]; + +export const createBackwardsCompatibilityMapping = (version: number) => { + const mappings = backwardsCompatibilityMappings + .filter((mapping) => version <= mapping.maxVersion && version >= mapping.minVersion) + .map((mapping) => mapping.mapping); + + const meta = { + _meta: { + version, + [ALIAS_VERSION_FIELD]: SIGNALS_FIELD_ALIASES_VERSION, + }, + }; + + return merge({}, ...mappings, meta); +}; + export const getRbacRequiredFields = (spaceId: string) => { return { [SPACE_IDS]: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/other_mappings.json b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/other_mappings.json index b61ad2e43ac03..5ad8f5238a97d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/other_mappings.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/other_mappings.json @@ -98,6 +98,29 @@ } } }, + "host": { + "properties": { + "os": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + }, + "caseless": { + "ignore_above": 1024, + "normalizer": "lowercase", + "type": "keyword" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, "interface": { "properties": { "alias": { diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts index 938772ce72367..18c3baccf9aa0 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts @@ -111,6 +111,8 @@ export const allowlistEventFields: AllowlistFields = { events: allowlistBaseEventFields, // behavioral protection re-nests some field sets under Events.* (>=7.15) Events: allowlistBaseEventFields, + // behavioral protection response data under Response.* (>=7.15) + Responses: true, rule: { id: true, name: true, diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts index a4f8033f1160d..d04d0ab49afe9 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.test.ts @@ -80,6 +80,7 @@ describe('TelemetryEventsSender', () => { executable: null, // null fields are never allowlisted working_directory: '/some/usr/dir', }, + Responses: '{ "result": 0 }', // >= 7.15 Target: { process: { name: 'bar.exe', @@ -89,6 +90,9 @@ describe('TelemetryEventsSender', () => { }, }, }, + threat: { + ignored_object: true, // this field is not allowlisted + }, }, ]; @@ -136,6 +140,7 @@ describe('TelemetryEventsSender', () => { name: 'foo.exe', working_directory: '/some/usr/dir', }, + Responses: '{ "result": 0 }', Target: { process: { name: 'bar.exe', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts index 2fc2c42be617f..b97b8ed4f6549 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { MatrixHistogramTypeToAggName } from '../../../../../../common'; import { buildAlertsHistogramQuery } from './query.alerts_histogram.dsl'; export const alertsMatrixHistogramConfig = { buildDsl: buildAlertsHistogramQuery, - aggName: 'aggregations.alertsGroup.buckets', + aggName: MatrixHistogramTypeToAggName.alerts, parseKey: 'alerts.buckets', }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts index 396d1e8bd004b..ec307173ec20c 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { MatrixHistogramTypeToAggName } from '../../../../../../common'; import { buildAnomaliesHistogramQuery } from './query.anomalies_histogram.dsl'; export const anomaliesMatrixHistogramConfig = { buildDsl: buildAnomaliesHistogramQuery, - aggName: 'aggregations.anomalyActionGroup.buckets', + aggName: MatrixHistogramTypeToAggName.anomalies, parseKey: 'anomalies.buckets', }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts index c147b32be2c00..17f7d78167232 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts @@ -5,19 +5,20 @@ * 2.0. */ +import { MatrixHistogramTypeToAggName } from '../../../../../../common'; import { getEntitiesParser } from '../helpers'; import { buildAuthenticationsHistogramQuery } from './query.authentications_histogram.dsl'; import { buildAuthenticationsHistogramQueryEntities } from './query.authentications_histogram_entities.dsl'; export const authenticationsMatrixHistogramConfig = { buildDsl: buildAuthenticationsHistogramQuery, - aggName: 'aggregations.eventActionGroup.buckets', + aggName: MatrixHistogramTypeToAggName.authentications, parseKey: 'events.buckets', }; export const authenticationsMatrixHistogramEntitiesConfig = { buildDsl: buildAuthenticationsHistogramQueryEntities, - aggName: 'aggregations.events.buckets', + aggName: MatrixHistogramTypeToAggName.authenticationsEntities, parseKey: 'events.buckets', parser: getEntitiesParser, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts index 26f07d881618a..643b3f657ef0c 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts @@ -7,10 +7,11 @@ import { buildDnsHistogramQuery } from './query.dns_histogram.dsl'; import { getDnsParsedData } from './helpers'; +import { MatrixHistogramTypeToAggName } from '../../../../../../common'; export const dnsMatrixHistogramConfig = { buildDsl: buildDnsHistogramQuery, - aggName: 'aggregations.dns_name_query_count.buckets', + aggName: MatrixHistogramTypeToAggName.dns, parseKey: 'dns_question_name.buckets', parser: getDnsParsedData, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts index 0edfbe7df001b..a280950c37cd6 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { MatrixHistogramTypeToAggName } from '../../../../../../common'; import { buildEventsHistogramQuery } from './query.events_histogram.dsl'; export const eventsMatrixHistogramConfig = { buildDsl: buildEventsHistogramQuery, - aggName: 'aggregations.eventActionGroup.buckets', + aggName: MatrixHistogramTypeToAggName.events, parseKey: 'events.buckets', }; diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts index b5c0972031a8f..06be065c28a71 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts @@ -138,7 +138,6 @@ export interface UsageData extends UsageStats { graph?: number; uptime?: number; savedObjectsManagement?: number; - timelion?: number; dev_tools?: number; advancedSettings?: number; infrastructure?: number; @@ -269,12 +268,6 @@ export function getSpacesUsageCollector( description: 'The number of spaces which have this feature disabled.', }, }, - timelion: { - type: 'long', - _meta: { - description: 'The number of spaces which have this feature disabled.', - }, - }, dev_tools: { type: 'long', _meta: { 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 642189f716539..5cf39b89376d4 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -6655,12 +6655,6 @@ "description": "The number of spaces which have this feature disabled." } }, - "timelion": { - "type": "long", - "_meta": { - "description": "The number of spaces which have this feature disabled." - } - }, "dev_tools": { "type": "long", "_meta": { diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts b/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts index 47cd1ed92d661..5371d7004a864 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts +++ b/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts @@ -7,9 +7,6 @@ import { useState, useLayoutEffect } from 'react'; -// That could be different from security and observability. Get it as parameter? -const INITIAL_DATA_GRID_HEIGHT = 967; - // It will recalculate DataGrid height after this time interval. const TIME_INTERVAL = 50; @@ -18,8 +15,17 @@ const TIME_INTERVAL = 50; * 3 (three) is a number, numeral and digit. It is the natural number following 2 and preceding 4, and is the smallest * odd prime number and the only prime preceding a square number. It has religious or cultural significance in many societies. */ + const MAGIC_GAP = 3; +// Hard coded height for every page size +const DATA_GRID_HEIGHT_BY_PAGE_SIZE: { [key: number]: number } = { + 10: 457, + 25: 967, + 50: 1817, + 100: 3517, +}; + /** * HUGE HACK!!! * DataGrtid height isn't properly calculated when the grid has horizontal scroll. @@ -30,13 +36,15 @@ const MAGIC_GAP = 3; * Please delete me and allow DataGrid to calculate its height when the bug is fixed. */ export const useDataGridHeightHack = (pageSize: number, rowCount: number) => { - const [height, setHeight] = useState(INITIAL_DATA_GRID_HEIGHT); + const [height, setHeight] = useState(DATA_GRID_HEIGHT_BY_PAGE_SIZE[pageSize]); useLayoutEffect(() => { setTimeout(() => { const gridVirtualized = document.querySelector('#body-data-grid .euiDataGrid__virtualized'); - if ( + if (rowCount === pageSize) { + setHeight(DATA_GRID_HEIGHT_BY_PAGE_SIZE[pageSize]); + } else if ( gridVirtualized && gridVirtualized.children[0].clientHeight !== gridVirtualized.clientHeight // check if it has vertical scroll ) { diff --git a/x-pack/plugins/timelines/public/components/t_grid/shared/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/shared/index.tsx index 324a97ff2a39f..f4a9158a3e4e7 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/shared/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/shared/index.tsx @@ -51,7 +51,7 @@ export const TGridEmpty: React.FC<{ height?: keyof typeof heights }> = ({ height const { http } = useKibana().services; return ( - + diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c71980f14d428..d7c21fd6ee654 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2997,7 +2997,6 @@ "home.tutorial.card.sampleDataDescription": "これらの「ワンクリック」データセットで Kibana の探索を始めましょう。", "home.tutorial.card.sampleDataTitle": "サンプルデータ", "home.tutorial.elasticCloudButtonLabel": "Elastic Cloud", - "home.tutorial.instruction_variant.fleet": "FleetのElastic APM(ベータ版)", "home.tutorial.instruction.copyButtonLabel": "スニペットをコピー", "home.tutorial.instructionSet.checkStatusButtonLabel": "ステータスを確認", "home.tutorial.instructionSet.customizeLabel": "コードスニペットのカスタマイズ", @@ -4389,8 +4388,6 @@ "savedObjectsManagement.managementSectionLabel": "保存されたオブジェクト", "savedObjectsManagement.objects.savedObjectsDescription": "保存された検索、ビジュアライゼーション、ダッシュボードのインポート、エクスポート、管理を行います。", "savedObjectsManagement.objects.savedObjectsTitle": "保存されたオブジェクト", - "savedObjectsManagement.objectsTable.deleteConfirmModal.cannotDeleteCallout.content": "一部の選択したオブジェクトは削除できません。テーブル概要の一覧には表示されません", - "savedObjectsManagement.objectsTable.deleteConfirmModal.cannotDeleteCallout.title": "一部のオブジェクトを削除できません", "savedObjectsManagement.objectsTable.deleteConfirmModal.sharedObjectsCallout.content": "共有オブジェクトは属しているすべてのスペースから削除されます。", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.cancelButtonLabel": "キャンセル", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.idColumnName": "Id", @@ -4575,80 +4572,12 @@ "telemetry.welcomeBanner.enableButtonLabel": "有効にする", "telemetry.welcomeBanner.telemetryConfigDetailsDescription.telemetryPrivacyStatementLinkText": "プライバシーポリシー", "telemetry.welcomeBanner.title": "Elastic Stack の改善にご協力ください", - "timelion.badge.readOnly.text": "読み取り専用", - "timelion.badge.readOnly.tooltip": "Timelion シートを保存できません", - "timelion.breadcrumbs.create": "作成", - "timelion.breadcrumbs.root": "Timelion", - "timelion.cells.actions.fullscreenAriaLabel": "全画面チャート", - "timelion.cells.actions.fullscreenTooltip": "全画面", - "timelion.cells.actions.removeAriaLabel": "リモートスタート", - "timelion.cells.actions.removeTooltip": "削除", - "timelion.cells.actions.reorderAriaLabel": "ドラッグして並べ替え", - "timelion.cells.actions.reorderTooltip": "ドラッグして並べ替え", - "timelion.chart.seriesList.noSchemaWarning": "次のパネルタイプは存在しません:{renderType}", - "timelion.deprecation.here": "ダッシュボードに移行します。", - "timelion.deprecation.message": "Timelionアプリは7.0以降で非推奨となっています。7.16では削除される予定です。Timelionワークシートを引き続き使用するには、{timeLionDeprecationLink}。", "timelion.emptyExpressionErrorMessage": "Timelion エラー:式が入力されていません", - "timelion.expressionInputAriaLabel": "Timelion 式", - "timelion.expressionInputPlaceholder": "{esQuery} でのクエリを試してみてください。", - "timelion.expressionSuggestions.arg.infoTitle": "情報", - "timelion.expressionSuggestions.arg.listTitle": "引数:", - "timelion.expressionSuggestions.arg.nameTitle": "引数名", - "timelion.expressionSuggestions.arg.typesTitle": "対応タイプ", "timelion.expressionSuggestions.argument.description.acceptsText": "受け入れ", "timelion.expressionSuggestions.func.description.chainableHelpText": "連鎖可能", - "timelion.expressionSuggestions.func.description.chainableText": "{help}(連鎖可能)", "timelion.expressionSuggestions.func.description.dataSourceHelpText": "データソース", - "timelion.expressionSuggestions.func.description.dataSourceText": "{help}(データソース)", "timelion.fitFunctions.carry.downSampleErrorMessage": "ダウンサンプルには「carry」フィットメソドを使用せず、「scale」または「average」を使用してください", - "timelion.fullscreen.exitAriaLabel": "全画面を終了", - "timelion.fullscreen.exitTooltip": "全画面を終了", "timelion.function.help": "Timelion のビジュアライゼーションです。", - "timelion.help.configuration.firstTimeConfigurationLinkText": "初回構成", - "timelion.help.configuration.notValid.advancedSettingsPathText": "管理 / Kibana / 高度な設定", - "timelion.help.configuration.notValid.notValidSettingsErrorMessage": "Elasticsearch の設定を確認できませんでした:{reason}。高度な設定を確認して再試行してください。({count})", - "timelion.help.configuration.notValid.paragraph1": "Logstash を使用している場合、Timelion でのログデータの探索開始に何も構成する必要はありません。他のインデックスを検索するには、{advancedSettingsPath} に移動し、{esDefaultIndex} および {esTimefield} 設定を構成し、インデックスと一致させます。", - "timelion.help.configuration.notValid.paragraph2": "他の Timelion 設定もあります。今のところ他の設定は気にしなくて大丈夫です。後程、必要に応じていつでも設定できることがわかります。", - "timelion.help.configuration.notValid.validateButtonLabel": "構成を検証", - "timelion.help.configuration.notValidTitle": "初回構成", - "timelion.help.configuration.valid.advancedSettingsPathText": "管理/Kibana/高度な設定", - "timelion.help.configuration.valid.intervalIsAutoText": "準備完了です!", - "timelion.help.configuration.valid.intervals.content.intervalIsNotAutoText": "Timelion が適切な間隔を選択できるよう、{auto} に設定します。", - "timelion.help.configuration.valid.intervalsTextPart1": "インプットバーの右にある間隔設定は、サンプリングの頻度をコントロールできます。現在 {interval} に設定されています。", - "timelion.help.configuration.valid.intervalsTextPart2": "Timelion が選択された時間範囲と間隔の組み合わせによりデータポイントが多くなりすぎると判断した場合、エラーが発生します。制限を調整するには、{advancedSettingsPath} で {maxBuckets} を構成します。", - "timelion.help.configuration.valid.intervalsTitle": "間隔", - "timelion.help.configuration.valid.paragraph1Part1": "デフォルトのインデックスと時間フィールドを検証し、すべて問題なさそうです。{statsMin} から {statsMax} へのデータが見つかりました。おそらく準備完了です。何か問題がある場合は、", - "timelion.help.configuration.valid.paragraph1Part2": "で Elasticsearch データソースの構成に関する詳細をご覧ください。", - "timelion.help.configuration.valid.paragraph2": "すでにチャートが 1 つ表示されていますが、興味深いデータを得るにはいくつか調整が必要な可能性があります。", - "timelion.help.configuration.valid.paragraph3": "これで、一定期間のデータポイントの数を示す折れ線グラフが表示されるはずです。", - "timelion.help.configuration.valid.timeRangeText": "時間フィルターを使用して、可視化するデータを含む期間を選択します。上記のすべてまたは一部の時間範囲を含む時間範囲を選択するようにしてください。", - "timelion.help.configuration.valid.timeRangeTitle": "時間範囲", - "timelion.help.configuration.validTitle": "良いお知らせです。Elasticsearch が正しく構成されました!", - "timelion.help.dataTransforming.functionReferenceLinkText": "機能リファレンス", - "timelion.help.dataTransforming.paragraph1": "基本を覚えたところで、Timelion の実力を発揮させましょう。データのサブセットが、一定期間における全体の何パーセントを表しているか見てみましょう。たとえば、Web トラフィックの何パーセントが米国からのものでしょう?", - "timelion.help.dataTransforming.paragraph2": "まず初めに、米国を含むすべてのイベントを見つけます:{esUsQuery}。", - "timelion.help.dataTransforming.paragraph3": "次に、全体に対する米国のイベントの比率を割り出します。{us} をすべてで割るために、{divide} 関数 {divideDataQuery} を使用できます。", - "timelion.help.dataTransforming.paragraph4": "まぁまぁですが、これでは 0 から 1 までの値になってしまいます。パーセンテージに変換するには、100 を掛けます:{multiplyDataQuery}。", - "timelion.help.dataTransforming.paragraph5": "これでトラフィックの何パーセントが米国からのものなのか分かり、一定期間内にどのように変化したのか見ることができます!Timelion には、{sum}、{subtract}、{multiply}、{divide} などのいくつもの演算機能が搭載されています。これらの多くが数列や数字を扱えます。また、{movingaverage}、{abs}、{derivative} といった他の便利な変換機能もあります。", - "timelion.help.dataTransforming.paragraph6Part1": "構文を学んだところで、", - "timelion.help.dataTransforming.paragraph6Part2": "Timelion で利用できるすべての機能の使い方をご覧ください。ツールバーの\\{Docs\\}をクリックしていつでもリファレンスを表示できます。このチュートリアルに戻るには、リファレンスの上にある \\{Tutorial\\} リンクをクリックします。", - "timelion.help.dataTransformingTitle": "データの変換:お楽しみの始まりです!", - "timelion.help.dontShowHelpButtonLabel": "今後表示しない", - "timelion.help.expressions.examples.customStylingDescription": "{descriptionTitle}初めの数列を赤くし、2 つ目の数列に 1 ピクセル幅のバーを使用します。", - "timelion.help.expressions.examples.customStylingDescriptionTitle": "カスタムスタイリング。", - "timelion.help.expressions.examples.groupedExpressionsDescription": "{descriptionTitle} 式のグループを関数に連結させることもできます。ここでは両方の数列が線ではなく点で表示されています。", - "timelion.help.expressions.examples.groupedExpressionsDescriptionTitle": "式のグループ化。", - "timelion.help.expressions.examples.namedArgumentsDescription": "{descriptionTitle}引数の指定順序を覚える必要はありません。名前付き引数を使えば、式の読み書きが楽になります。", - "timelion.help.expressions.examples.namedArgumentsDescriptionTitle": "名前付き引数。", - "timelion.help.expressions.examples.twoExpressionsDescription": "{descriptionTitle}同じチャートに 2 つの式が使えます。", - "timelion.help.expressions.examples.twoExpressionsDescriptionTitle": "2 倍の楽しみ。", - "timelion.help.expressions.functionReferenceLinkText": "機能リファレンス", - "timelion.help.expressions.paragraph1": "それぞれの式はデータソース関数で始まります。ここから、新しい関数をデータソースに追加して変換や強化ができます。", - "timelion.help.expressions.paragraph2": "ところで、ここから先はデータの持ち主が一番よくご存知なのではないでしょうか。サンプルクエリをより有意義なものと自由に置き換えてみてください。", - "timelion.help.expressions.paragraph3": "実験をします。ツールバーの{strongAdd}をクリックして、他のチャートをいくつか追加してみましょう。そして、チャートを選択して次の式の内の 1 つをコピーし、インプットバーに貼り付けて、Enter を押します。リセットして繰り返し、他の式を試してみましょう。", - "timelion.help.expressions.paragraph4": "Timelion は、チャートの見た目をカスタマイズするための他のビュー変換機能も搭載しています。完全なリストは次のリソースをご覧ください", - "timelion.help.expressions.strongAddText": "追加", - "timelion.help.expressionsTitle": "式を使って式を定義", "timelion.help.functions.absHelpText": "数列リストの各値の絶対値を返します", "timelion.help.functions.aggregate.args.functionHelpText": "{functions} の 1 つ", "timelion.help.functions.aggregateHelpText": "数列のすべての点の処理結果に基づく線を作成します。利用可能な関数:{functions}", @@ -4779,64 +4708,9 @@ "timelion.help.functions.yaxis.args.unitsHelpText": "Y 軸のラベルのフォーマットに使用する機能です。次のいずれかです。{formatters}", "timelion.help.functions.yaxis.args.yaxisHelpText": "この数列をプロットする数字の Y 軸です。例:2 本目の Y 軸は .yaxis(2)になります。", "timelion.help.functions.yaxisHelpText": "さまざまな Y 軸のオプションを構成します。おそらく最も重要なのは、N 本目(例:2 本目)の Y 軸を追加する機能です。", - "timelion.help.mainPage.functionReference.detailsTable.acceptedTypesColumnLabel": "対応タイプ", - "timelion.help.mainPage.functionReference.detailsTable.argumentNameColumnLabel": "引数名", - "timelion.help.mainPage.functionReference.detailsTable.informationColumnLabel": "情報", - "timelion.help.mainPage.functionReference.gettingStartedText": "関数をクリックすると詳細が表示されます。初心者の方ですか?", - "timelion.help.mainPage.functionReference.noArgumentsFunctionErrorMessage": "この関数には引数を使用できません。簡単でしょう?", - "timelion.help.mainPage.functionReference.welcomePageLinkText": "チュートリアルをご覧ください", - "timelion.help.mainPage.functionReferenceTitle": "機能リファレンス", - "timelion.help.mainPage.keyboardTips.autoComplete.downArrowDescription": "自動入力メニューに焦点を切り替えます。矢印を使用してさらに用語を選択します", - "timelion.help.mainPage.keyboardTips.autoComplete.downArrowLabel": "下矢印", - "timelion.help.mainPage.keyboardTips.autoComplete.enterTabDescription": "現在の選択項目または自動入力メニューで最も使用されている用語を選択します", - "timelion.help.mainPage.keyboardTips.autoComplete.escDescription": "自動入力メニューを閉じます", - "timelion.help.mainPage.keyboardTips.autoCompleteTitle": "自動入力が有効な場合", - "timelion.help.mainPage.keyboardTips.generalEditing.submitRequestText": "リクエストを送信します", - "timelion.help.mainPage.keyboardTips.generalEditingTitle": "一般編集", - "timelion.help.mainPage.keyboardTipsTitle": "キーボードのヒント", - "timelion.help.mainPageTitle": "ヘルプ", - "timelion.help.nextPageButtonLabel": "次へ", - "timelion.help.previousPageButtonLabel": "前へ", - "timelion.help.querying.countMetricAggregationLinkText": "Elasticsearch メトリック集約", - "timelion.help.querying.countTextPart1": "イベントをカウントするのも良いですが、Elasticsearch のデータソースは単独の値を返す", - "timelion.help.querying.countTextPart2": "もサポートしています。最も便利なものには、{min}、{max}、{avg}、{sum}、{cardinality} があります。{srcIp} フィールドのユニークカウントを求めたいとしましょう。{cardinality} メトリック {esCardinalityQuery} を使用します。{bytes} フィールドの平均を取得するには、{avg} metric:メトリック {esAvgQuery} を使用できます。", - "timelion.help.querying.countTitle": "カウントを超えて", - "timelion.help.querying.esAsteriskQueryDescriptionText": "Elasticsearch、デフォルトインデックスのすべてを計算", - "timelion.help.querying.esIndexQueryDescriptionText": "* を logstash-* インデックスの q(クエリ)として使用します", - "timelion.help.querying.luceneQueryLinkText": "Lucene クエリ文字列", - "timelion.help.querying.paragraph1": "Elasticsearch データソースが利用可能であることを確認済みなので、クエリの送信ができます。手始めに、インプットバーに {esPattern} と入力し Enter を押してみましょう。", - "timelion.help.querying.paragraph2Part1": "これは{esAsteriskQueryDescription}となります。サブセットを把握したい場合は、{htmlQuery} で {html} に一致するイベントをカウントしたり、{bobQuery} で {user} フィールドに {bob} を含み、{bytes} フィールドが 100 より大きな値のイベントを検索したりできます。このクエリはシングルクォートで囲まれています。スペースを含むためです。いずれかの", - "timelion.help.querying.paragraph2Part2": "を {esQuery} 関数の初めの引数として入力することができます。", - "timelion.help.querying.passingArgumentsText": "Timelion には一般的な操作を簡単に行えるよう、いくつものショートカットがあります。スペースや特殊文字を含まないシンプルな引数用のものがその一つで、クォートを使う必要はありません。また、多くの関数にデフォルトがあります。たとえば、{esEmptyQuery} と {esStarQuery} で実行される処理は同じです。引数には名前も付いているため、特定の順序で指定する必要はありません。たとえば、{esLogstashQuery} を入力して、Elasticsearch データソース {esIndexQueryDescription} に命令することができます。", - "timelion.help.querying.passingArgumentsTitle": "引数の受け渡し", - "timelion.help.queryingTitle": "Elasticsearch データソースにクエリを実行中", - "timelion.help.unknownErrorMessage": "不明なエラー", - "timelion.help.welcome.content.emphasizedEverythingText": "すべて", - "timelion.help.welcome.content.functionReferenceLinkText": "関数リファレンスに移動", - "timelion.help.welcome.content.paragraph1": "Timelion は時系列に関する {emphasizedEverything} を司る、全知全能のツールです。データストアから提供された時系列データは、Timelion にお任せください。Timelion は複数データソースのデータセットを、覚えやすい式構文で比較、結合、整理できます。このチュートリアルは Elasticsearch が中心となりますが、ここで学んだことは Timelion がサポートするすべてのデータソースに適用できます。", - "timelion.help.welcome.content.paragraph2": "はじめてみる{strongNext} をクリックします。チュートリアルをスキップしてドキュメントを表示しますか?", - "timelion.help.welcome.content.strongNextText": "次へ", - "timelion.help.welcomeTitle": "{strongTimelionLabel} へようこそ!", - "timelion.intervals.customIntervalAriaLabel": "カスタム間隔", - "timelion.intervals.selectIntervalAriaLabel": "間隔を選択", "timelion.noFunctionErrorMessage": "そのような関数はありません:{name}", - "timelion.panels.noRenderFunctionErrorMessage": "パネルにはレンダリング関数が必要です", "timelion.panels.timechart.unknownIntervalErrorMessage": "不明な間隔", "timelion.requestHandlerErrorTitle": "Timelion リクエストエラー", - "timelion.savedObjectFinder.addNewItemButtonLabel": "新規{item}を追加", - "timelion.savedObjectFinder.manageItemsButtonLabel": "{items}の管理", - "timelion.savedObjectFinder.noMatchesFoundDescription": "一致する{items}が見つかりません。", - "timelion.savedObjectFinder.pageItemsFromHitCountDescription": "{pageFirstItem}-{pageLastItem} of {hitCount}", - "timelion.savedObjectFinder.sortByButtonLabeAscendingScreenReaderOnly": "昇順", - "timelion.savedObjectFinder.sortByButtonLabeDescendingScreenReaderOnly": "降順", - "timelion.savedObjectFinder.sortByButtonLabel": "名前", - "timelion.savedObjectFinder.sortByButtonLabelScreenReaderOnly": "並べ替え基準", - "timelion.savedObjects.howToSaveAsNewDescription": "以前のバージョンの Kibana では、{savedObjectName} の名前を変更すると新しい名前でコピーが作成されました。現在のバージョンで同じように保存するには、[新規 {savedObjectName} として保存]チェックボックスを使用します。", - "timelion.savedObjects.saveAsNewLabel": "新規 {savedObjectName} として保存", - "timelion.saveExpression.successNotificationText": "保存された式「{title}」", - "timelion.saveSheet.successNotificationText": "保存されたシート「{title}」", - "timelion.search.submitAriaLabel": "検索", - "timelion.searchErrorTitle": "Timelion リクエストエラー", "timelion.serverSideErrors.argumentsOverflowErrorMessage": "{functionName} に引き渡された引数が多すぎます", "timelion.serverSideErrors.bucketsOverflowErrorMessage": "最大バケットを超えました:{bucketCount}/{maxBuckets} が許可されています。より広い間隔または短い期間を選択してください", "timelion.serverSideErrors.colorFunction.colorNotProvidedErrorMessage": "色が指定されていません", @@ -4857,47 +4731,8 @@ "timelion.serverSideErrors.yaxisFunction.notSupportedUnitTypeErrorMessage": "{units} はサポートされているユニットタイプではありません。", "timelion.serverSideErrors.yaxisFunction.notValidCurrencyFormatErrorMessage": "通貨は 3 文字のコードでなければなりません", "timelion.timelionDescription": "グラフに時系列データを表示します。", - "timelion.topNavMenu.addChartButtonAriaLabel": "チャートを追加", - "timelion.topNavMenu.addChartButtonLabel": "追加", - "timelion.topNavMenu.delete.modal.confirmButtonLabel": "削除", - "timelion.topNavMenu.delete.modal.successNotificationText": "「{title}」が削除されました", - "timelion.topNavMenu.delete.modal.warningText": "削除されたシートは復元できません。", - "timelion.topNavMenu.delete.modalTitle": "Timelion シート「{title}」を削除しますか?", - "timelion.topNavMenu.deleteSheetButtonAriaLabel": "現在のシートを削除", - "timelion.topNavMenu.deleteSheetButtonLabel": "削除", - "timelion.topNavMenu.helpButtonAriaLabel": "ヘルプ", - "timelion.topNavMenu.helpButtonLabel": "ヘルプ", - "timelion.topNavMenu.newSheetButtonAriaLabel": "新規シート", - "timelion.topNavMenu.newSheetButtonLabel": "新規", - "timelion.topNavMenu.openSheetButtonAriaLabel": "シートを開く", - "timelion.topNavMenu.openSheetButtonLabel": "開く", - "timelion.topNavMenu.openSheetTitle": "シートを開く", - "timelion.topNavMenu.options.columnsCountLabel": "列(列カウントは 12 できっかりと割れる必要があります)", - "timelion.topNavMenu.options.rowsCountLabel": "行(これは現在のウィンドウの縦の長さに基づく目標値です)。", - "timelion.topNavMenu.optionsButtonAriaLabel": "オプション", - "timelion.topNavMenu.optionsButtonLabel": "オプション", - "timelion.topNavMenu.save.saveAsDashboardPanel.inputPlaceholder": "このパネルに名前を付ける", - "timelion.topNavMenu.save.saveAsDashboardPanel.selectedExpressionLabel": "現在選択されている式", - "timelion.topNavMenu.save.saveAsDashboardPanel.submitButtonLabel": "保存", - "timelion.topNavMenu.save.saveAsDashboardPanelDescription": "Kibana ダッシュボードにチャートの追加が必要ですか?できます!このオプションは、現在選択されている式を、他のオブジェクトの追加と同じように Kibana ダッシュボードに追加可能なパネルとして保存します。他のパネルへのリファレンスが使用されている場合、リファレンスの表現を直接保存する表現にコピーして、リファレンスを削除する必要があります。他の表現式を保存するよう選択するには、チャートをクリックします。", - "timelion.topNavMenu.save.saveAsDashboardPanelLabel": "式に名前を付けて保存", - "timelion.topNavMenu.save.saveAsDashboardPanelTitle": "現在の式を Kibana ダッシュボードのパネルとして保存", - "timelion.topNavMenu.save.saveEntireSheet.inputAriaLabel": "名前", - "timelion.topNavMenu.save.saveEntireSheet.inputPlaceholder": "このシートに名前を付ける...", - "timelion.topNavMenu.save.saveEntireSheet.submitButtonLabel": "保存", - "timelion.topNavMenu.save.saveEntireSheetDescription": "Timelion 式を主に Timelion アプリで使用し、Kibana のダッシュボードに Timelion のチャートを追加する必要がない場合は、このオプションを使用します。他のパネルへのリファレンスを使用する場合もこのオプションを使用します。", - "timelion.topNavMenu.save.saveEntireSheetLabel": "シートに名前を付けて保存", - "timelion.topNavMenu.save.saveEntireSheetTitle": "Timelion シート全体の保存", - "timelion.topNavMenu.saveSheetButtonAriaLabel": "シートを保存", - "timelion.topNavMenu.saveSheetButtonLabel": "保存", - "timelion.topNavMenu.sheetOptionsTitle": "シートオプション", - "timelion.topNavMenu.statsDescription": "クエリ時間 {queryTime}ms / 処理時間 {processingTime}ms", - "timelion.uiSettings.defaultColumnsDescription": "デフォルトの Timelion シートの列数です", - "timelion.uiSettings.defaultColumnsLabel": "デフォルトの列", "timelion.uiSettings.defaultIndexDescription": "{esParam} で検索するデフォルトの Elasticsearch インデックスです", "timelion.uiSettings.defaultIndexLabel": "デフォルトのインデックス", - "timelion.uiSettings.defaultRowsDescription": "デフォルトの Timelion シートの行数です", - "timelion.uiSettings.defaultRowsLabel": "デフォルトの行", "timelion.uiSettings.experimentalLabel": "実験的", "timelion.uiSettings.graphiteURLDescription": "{experimentalLabel} Graphite ホストの URL", "timelion.uiSettings.graphiteURLLabel": "Graphite URL", @@ -4910,8 +4745,6 @@ "timelion.uiSettings.minimumIntervalLabel": "最低間隔", "timelion.uiSettings.quandlKeyDescription": "{experimentalLabel} www.quandl.com からの API キーです", "timelion.uiSettings.quandlKeyLabel": "Quandl キー", - "timelion.uiSettings.showTutorialDescription": "Timelion アプリの起動時にデフォルトでチュートリアルを表示しますか?", - "timelion.uiSettings.showTutorialLabel": "チュートリアルを表示", "timelion.uiSettings.targetBucketsDescription": "自動間隔の使用時に目標となるバケット数です。", "timelion.uiSettings.targetBucketsLabel": "目標バケット数", "timelion.uiSettings.timeFieldDescription": "{esParam} の使用時にタイムスタンプを含むデフォルトのフィールドです", @@ -5317,8 +5150,8 @@ "visTypeTimeseries.dataFormatPicker.customLabel": "カスタム", "visTypeTimeseries.dataFormatPicker.decimalPlacesLabel": "小数部分の桁数", "visTypeTimeseries.dataFormatPicker.durationLabel": "期間", - "visTypeTimeseries.dataFormatPicker.formatStringHelpText": "{numeralJsLink}を参照", - "visTypeTimeseries.dataFormatPicker.formatStringLabel": "フォーマット文字列", + "visTypeTimeseries.dataFormatPicker.formatPatternHelpText": "ドキュメント", + "visTypeTimeseries.dataFormatPicker.formatPatternLabel": "Numeral.js のフォーマットパターン (デフォルト: {defaultPattern})", "visTypeTimeseries.dataFormatPicker.fromLabel": "開始:", "visTypeTimeseries.dataFormatPicker.numberLabel": "数字", "visTypeTimeseries.dataFormatPicker.percentLabel": "パーセント", @@ -7150,7 +6983,6 @@ "xpack.apm.tutorial.apmServer.fleet.apmIntegration.button": "APM統合", "xpack.apm.tutorial.apmServer.fleet.manageApmIntegration.button": "FleetでAPM統合を管理", "xpack.apm.tutorial.apmServer.fleet.message": "APMA統合は、APMデータ用にElasticsearchテンプレートとIngest Nodeパイプラインをインストールします。", - "xpack.apm.tutorial.apmServer.fleet.title": "Elastic APM(ベータ版)がFleetで提供されました。", "xpack.apm.tutorial.apmServer.statusCheck.btnLabel": "APM Server ステータスを確認", "xpack.apm.tutorial.apmServer.statusCheck.errorMessage": "APM Server が検出されました。7.0 以上に更新され、動作中であることを確認してください。", "xpack.apm.tutorial.apmServer.statusCheck.successMessage": "APM Server が正しくセットアップされました", @@ -22423,7 +22255,6 @@ "xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.network": "ネットワーク", "xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.process": "プロセス", "xpack.securitySolution.endpoint.policyDetailsConfig.notifyUserTooltip.a": "ユーザー通知オプションを選択すると、{ protectionName }が防御または検出されたときに、ホストユーザーに通知を表示します。", - "xpack.securitySolution.endpoint.policyDetailsConfig.notifyUserTooltip.b": " ユーザー通知は、以下のテキストボックスでカスタマイズできます。括弧内のタグを使用すると、該当するアクション(防御または検出など)とファイル名を動的に入力できます。", "xpack.securitySolution.endpoint.policyDetailsConfig.protectionLevel": "保護レベル", "xpack.securitySolution.endpoint.policyDetailsConfig.userNotification": "ユーザー通知", "xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dllDriverLoad": "DLL とドライバーの読み込み", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ae2793d56ca9b..ca0f2359da575 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3026,7 +3026,6 @@ "home.tutorial.card.sampleDataDescription": "开始使用这些“一键式”数据集浏览 Kibana。", "home.tutorial.card.sampleDataTitle": "样例数据", "home.tutorial.elasticCloudButtonLabel": "Elastic Cloud", - "home.tutorial.instruction_variant.fleet": "Fleet 中的 Elastic APM(公测版)", "home.tutorial.instruction.copyButtonLabel": "复制代码片段", "home.tutorial.instructionSet.checkStatusButtonLabel": "检查状态", "home.tutorial.instructionSet.customizeLabel": "定制您的代码片段", @@ -4430,8 +4429,6 @@ "savedObjectsManagement.managementSectionLabel": "已保存对象", "savedObjectsManagement.objects.savedObjectsDescription": "导入、导出和管理您的已保存搜索、可视化和仪表板。", "savedObjectsManagement.objects.savedObjectsTitle": "已保存对象", - "savedObjectsManagement.objectsTable.deleteConfirmModal.cannotDeleteCallout.content": "部分选定的对象无法删除,且不会列在表摘要中", - "savedObjectsManagement.objectsTable.deleteConfirmModal.cannotDeleteCallout.title": "一些对象无法删除", "savedObjectsManagement.objectsTable.deleteConfirmModal.sharedObjectsCallout.content": "共享对象已从其所在的各个工作区中移除。", "savedObjectsManagement.objectsTable.deleteConfirmModal.sharedObjectsCallout.title": "{sharedObjectsCount, plural, one {# 个已保存对象已共享} other {您的已保存对象有 # 个已共享}}", "savedObjectsManagement.objectsTable.deleteSavedObjectsConfirmModal.cancelButtonLabel": "取消", @@ -4620,80 +4617,12 @@ "telemetry.welcomeBanner.enableButtonLabel": "启用", "telemetry.welcomeBanner.telemetryConfigDetailsDescription.telemetryPrivacyStatementLinkText": "隐私声明", "telemetry.welcomeBanner.title": "帮助我们改进 Elastic Stack", - "timelion.badge.readOnly.text": "只读", - "timelion.badge.readOnly.tooltip": "无法保存 Timelion 工作表", - "timelion.breadcrumbs.create": "创建", - "timelion.breadcrumbs.root": "Timelion", - "timelion.cells.actions.fullscreenAriaLabel": "全屏图表", - "timelion.cells.actions.fullscreenTooltip": "全屏", - "timelion.cells.actions.removeAriaLabel": "删除图表", - "timelion.cells.actions.removeTooltip": "移除", - "timelion.cells.actions.reorderAriaLabel": "拖动以重新排序", - "timelion.cells.actions.reorderTooltip": "拖动以重新排序", - "timelion.chart.seriesList.noSchemaWarning": "没有此类面板类型:{renderType}", - "timelion.deprecation.here": "请将其迁移至仪表板。", - "timelion.deprecation.message": "Timelion 应用自 7.0 版起已过时,将在 7.16 中移除。要继续使用 Timelion 工作表,{timeLionDeprecationLink}。", "timelion.emptyExpressionErrorMessage": "Timelion 错误:未提供表达式", - "timelion.expressionInputAriaLabel": "Timelion 表达式", - "timelion.expressionInputPlaceholder": "请尝试使用 {esQuery} 查询", - "timelion.expressionSuggestions.arg.infoTitle": "信息", - "timelion.expressionSuggestions.arg.listTitle": "参数:", - "timelion.expressionSuggestions.arg.nameTitle": "参数类型", - "timelion.expressionSuggestions.arg.typesTitle": "接受的类型", "timelion.expressionSuggestions.argument.description.acceptsText": "接受", "timelion.expressionSuggestions.func.description.chainableHelpText": "可串接", - "timelion.expressionSuggestions.func.description.chainableText": "{help}(可链接)", "timelion.expressionSuggestions.func.description.dataSourceHelpText": "数据源", - "timelion.expressionSuggestions.func.description.dataSourceText": "{help}(数据源)", "timelion.fitFunctions.carry.downSampleErrorMessage": "请勿使用“carry”拟合方法降低采样率,而是使用“scale”或“average”", - "timelion.fullscreen.exitAriaLabel": "退出全屏", - "timelion.fullscreen.exitTooltip": "退出全屏", "timelion.function.help": "Timelion 可视化", - "timelion.help.configuration.firstTimeConfigurationLinkText": "首次配置", - "timelion.help.configuration.notValid.advancedSettingsPathText": "管理/Kibana/高级设置", - "timelion.help.configuration.notValid.notValidSettingsErrorMessage": "无法验证 Elasticsearch 设置:{reason}。请检查您的高级设置,然后重试。({count})", - "timelion.help.configuration.notValid.paragraph1": "如果正在使用 Logstash,则无需进行任何配置,即可开始使用 Timelion 浏览日志数据。要搜索其他索引,请前往 {advancedSettingsPath} 并配置 {esDefaultIndex} 和 {esTimefield} 设置,以匹配您的索引。", - "timelion.help.configuration.notValid.paragraph2": "您还会看到一些其他 Timelion 设置。您现在无需担心这些设置。稍后就会发现,您可以根据需要即时设置其中大部分的设置。", - "timelion.help.configuration.notValid.validateButtonLabel": "验证配置", - "timelion.help.configuration.notValidTitle": "首次配置", - "timelion.help.configuration.valid.advancedSettingsPathText": "管理/Kibana/高级设置", - "timelion.help.configuration.valid.intervalIsAutoText": "全部准备就绪!", - "timelion.help.configuration.valid.intervals.content.intervalIsNotAutoText": "将其设为 {auto} 以允许 Timelion 选择适当的间隔。", - "timelion.help.configuration.valid.intervalsTextPart1": "输入栏右侧的时间间隔选择器可让您控制采样频率。当前设为 {interval}。", - "timelion.help.configuration.valid.intervalsTextPart2": "如果 Timelion 认为您的时间范围和时间间隔组合生成的数据点过多,将引发错误。可以通过在 {advancedSettingsPath} 中配置 {maxBuckets},来调整该限制。", - "timelion.help.configuration.valid.intervalsTitle": "时间间隔", - "timelion.help.configuration.valid.paragraph1Part1": "我们已验证您的默认索引和时间字段,所有设置都看起来没有什么问题。我们找到 {statsMin} 到 {statsMax} 的数据。您大概已准备就绪。如果有问题,请参阅", - "timelion.help.configuration.valid.paragraph1Part2": ",了解有关配置 Elasticsearch 数据源的信息。", - "timelion.help.configuration.valid.paragraph2": "您应该能看到一个图表,但还需要做一些调整才能看到所有感兴趣的数据:", - "timelion.help.configuration.valid.paragraph3": "现在,您应看到一个数据点计数随时间变化的折线图。", - "timelion.help.configuration.valid.timeRangeText": "使用时间筛选,选择包含要可视化的数据的时间段。请确保选择包含上述所示全部或部分时间范围的时间段。", - "timelion.help.configuration.valid.timeRangeTitle": "时间范围", - "timelion.help.configuration.validTitle": "真棒,Elasticsearch 配置正确!", - "timelion.help.dataTransforming.functionReferenceLinkText": "函数引用", - "timelion.help.dataTransforming.paragraph1": "至此,您已经掌握了基础知识,接下来开始体验 Timelion 的强大功能。让我们来计算随着时间的推移数据的某个子集占整体数据的百分比。例如,Web 流量中来自美国的占多少百分比?", - "timelion.help.dataTransforming.paragraph2": "首先,我们需要查找包含 US 的所有事件:{esUsQuery}。", - "timelion.help.dataTransforming.paragraph3": "接下来,要计算美国事件与全部事件的比率。要将 {us} 除以全部,我们可以使用 {divide} 函数:{divideDataQuery}。", - "timelion.help.dataTransforming.paragraph4": "还不错,但这是一个介于 0 到 1 之间的数字。要将其转换为百分比,只需乘以 100:{multiplyDataQuery}。", - "timelion.help.dataTransforming.paragraph5": "现在,我们知道了来自美国的流量所占的百分比,并且可以看到随时间的推移它的变化情况!Timelion 具有很多内置算术函数,例如 {sum}、{subtract}、{multiply} 和 {divide}。其中很多函数可以取序列或数字。此外,还有其他有用的数据转换函数,例如 {movingaverage}、{abs} 和 {derivative}。", - "timelion.help.dataTransforming.paragraph6Part1": "现在您已熟悉了语法,然后,请参阅", - "timelion.help.dataTransforming.paragraph6Part2": ",了解如何使用所有可用的 Timelion 函数。您可以通过单击工具栏上的\\{Docs\\},随时查看参考资料。要返回此教程,请单击参考资料顶部的\\{Tutorial\\}链接。", - "timelion.help.dataTransformingTitle": "转换您的数据:真正的乐趣所在!", - "timelion.help.dontShowHelpButtonLabel": "不再显示此内容", - "timelion.help.expressions.examples.customStylingDescription": "{descriptionTitle}将第一个序列着色为红色,并为第二个序列使用 1 像素宽的条。", - "timelion.help.expressions.examples.customStylingDescriptionTitle": "定制样式。", - "timelion.help.expressions.examples.groupedExpressionsDescription": "{descriptionTitle}您还可以将表达式组链锁到函数。这里两个序列都显示为点,而不是线。", - "timelion.help.expressions.examples.groupedExpressionsDescriptionTitle": "将表达式分组。", - "timelion.help.expressions.examples.namedArgumentsDescription": "{descriptionTitle}请不要尝试记住指定参数所需的顺序,使用命名参数可使表达式更易于读写。", - "timelion.help.expressions.examples.namedArgumentsDescriptionTitle": "命名参数。", - "timelion.help.expressions.examples.twoExpressionsDescription": "{descriptionTitle}同一个图表上的两个表达式。", - "timelion.help.expressions.examples.twoExpressionsDescriptionTitle": "乐趣倍增。", - "timelion.help.expressions.functionReferenceLinkText": "函数引用", - "timelion.help.expressions.paragraph1": "每个表达式都以数据源函数开头。接着,您可以将新函数新函数到数据源后面,以转换和增加数据源。", - "timelion.help.expressions.paragraph2": "顺便提一下,从现在开始,您可能比我们更了解自己的数据。可以随意将示例查询替换为更有意义的查询!", - "timelion.help.expressions.paragraph3": "我们将实验一下,单击工具栏中的”{strongAdd}” 以再添加一个图表或添加三个图表。然后,选择一个图表,复制下列其中一个表达式并粘贴到输入栏,然后按 Enter 键。清除,再重复上述操作来尝试其他表达式。", - "timelion.help.expressions.paragraph4": "Timelion 还提供了其他视图转换函数,可用于定制图表的外观。有关完整列表,请参阅", - "timelion.help.expressions.strongAddText": "添加", - "timelion.help.expressionsTitle": "使用表达式表达您自己", "timelion.help.functions.absHelpText": "返回序列列表中每个值的绝对值", "timelion.help.functions.aggregate.args.functionHelpText": "以下选项之一:{functions}", "timelion.help.functions.aggregateHelpText": "基于对序列中所有点的处理结果创建静态线。可用函数:{functions}", @@ -4824,64 +4753,9 @@ "timelion.help.functions.yaxis.args.unitsHelpText": "用于设置 Y 轴标签格式的函数。以下之一:{formatters}", "timelion.help.functions.yaxis.args.yaxisHelpText": "用于绘制此序列的带编号的 Y 轴,例如,.yaxis(2) 对应于第 2 个 Y 轴。", "timelion.help.functions.yaxisHelpText": "配置各种 Y 轴选项,其中最重要的选项可能是添加第 N 个(例如第 2 个)Y 轴的功能", - "timelion.help.mainPage.functionReference.detailsTable.acceptedTypesColumnLabel": "接受的类型", - "timelion.help.mainPage.functionReference.detailsTable.argumentNameColumnLabel": "参数类型", - "timelion.help.mainPage.functionReference.detailsTable.informationColumnLabel": "信息", - "timelion.help.mainPage.functionReference.gettingStartedText": "请单击任何函数以了解更多信息。马上开始使用?", - "timelion.help.mainPage.functionReference.noArgumentsFunctionErrorMessage": "此函数不接受任何参数。这很简单,不是吗?", - "timelion.help.mainPage.functionReference.welcomePageLinkText": "查看教程", - "timelion.help.mainPage.functionReferenceTitle": "函数引用", - "timelion.help.mainPage.keyboardTips.autoComplete.downArrowDescription": "将焦点切换到自动完成菜单。使用箭头进一步选择词", - "timelion.help.mainPage.keyboardTips.autoComplete.downArrowLabel": "向下箭头", - "timelion.help.mainPage.keyboardTips.autoComplete.enterTabDescription": "选择自动完成菜单中当前选定的词或最顶部的词", - "timelion.help.mainPage.keyboardTips.autoComplete.escDescription": "关闭自动完成菜单", - "timelion.help.mainPage.keyboardTips.autoCompleteTitle": "当自动完成可见时", - "timelion.help.mainPage.keyboardTips.generalEditing.submitRequestText": "提交请求", - "timelion.help.mainPage.keyboardTips.generalEditingTitle": "常规编辑", - "timelion.help.mainPage.keyboardTipsTitle": "键盘提示", - "timelion.help.mainPageTitle": "帮助", - "timelion.help.nextPageButtonLabel": "下一页", - "timelion.help.previousPageButtonLabel": "上一页", - "timelion.help.querying.countMetricAggregationLinkText": "Elasticsearch 指标聚合", - "timelion.help.querying.countTextPart1": "除了具有卓越的事件计数功能,Elasticsearch 数据源还支持任何", - "timelion.help.querying.countTextPart2": "返回单个值的查询。最有用的包括 {min}、{max}、{avg}、{sum} 和 {cardinality}。假设您需要 {srcIp} 字段的唯一计数。只需使用 {cardinality} 指标:{esCardinalityQuery}。要获取 {bytes} 字段的平均值,可以使用 {avg} 指标:{esAvgQuery}。", - "timelion.help.querying.countTitle": "不只是计数", - "timelion.help.querying.esAsteriskQueryDescriptionText": "嗨 Elasticsearch,请在我的默认索引中查找所有内容", - "timelion.help.querying.esIndexQueryDescriptionText": "使用 * 作为 logstash-* 索引的 q (query)", - "timelion.help.querying.luceneQueryLinkText": "Lucene 查询字符串", - "timelion.help.querying.paragraph1": "至此,我们已验证您的 Elasticsearch 数据源工作正常,因此您可以开始提交查询。如果是初学者,请在输入栏中输入 {esPattern},然后按 Enter 键。", - "timelion.help.querying.paragraph2Part1": "即 {esAsteriskQueryDescription}。如果要查找子集,可以输入 {htmlQuery} 之类的内容以算出与 {html} 匹配的事件个数,或输入 {bobQuery} 来查找在 {user} 字段中包含 {bob} 且 {bytes} 字段大于 100 的事件。注意,需要用单引号引起此查询,因为其包含空格。您可以输入任何内容", - "timelion.help.querying.paragraph2Part2": "作为 {esQuery} 函数的第一个参数。", - "timelion.help.querying.passingArgumentsText": "Timelion 具有很多快捷方式,让您可以轻松完成各种常见操作。对于不包含空格或特殊字符的简单参数,无需使用引号。而且很多函数都具有默认值。例如 {esEmptyQuery} 和 {esStarQuery} 有相同的作用。参数也有名称,您无需以特定顺序指定它们。例如,可以输入 {esLogstashQuery},让 Elasticsearch 数据源知道 {esIndexQueryDescription}。", - "timelion.help.querying.passingArgumentsTitle": "传递参数", - "timelion.help.queryingTitle": "查询 Elasticsearch 数据源", - "timelion.help.unknownErrorMessage": "未知错误", - "timelion.help.welcome.content.emphasizedEverythingText": "所有内容", - "timelion.help.welcome.content.functionReferenceLinkText": "跳到函数引用", - "timelion.help.welcome.content.paragraph1": "Timelion 是抓取精准、适用于{emphasizedEverything}的可插拔时间序列接口。如果您的数据存储可以生成时间序列,就能够随意使用 Timelion 的所有强大功能。借助 Timelion,可使用易于掌握的表达式语法,对跨多个数据源的数据集进行比较、合并和整理。尽管本教程主要介绍 Elasticsearch,但您很快会发现,本文所介绍的内容适用于 Timelion 支持的任何数据源。", - "timelion.help.welcome.content.paragraph2": "准备开始了吗?单击“{strongNext}”。想要跳过教程并查看文档?", - "timelion.help.welcome.content.strongNextText": "下一步", - "timelion.help.welcomeTitle": "欢迎来到 {strongTimelionLabel}!", - "timelion.intervals.customIntervalAriaLabel": "定制时间间隔", - "timelion.intervals.selectIntervalAriaLabel": "选择时间间隔", "timelion.noFunctionErrorMessage": "没有此类函数:{name}", - "timelion.panels.noRenderFunctionErrorMessage": "面板必须具有渲染函数", "timelion.panels.timechart.unknownIntervalErrorMessage": "时间间隔未知", "timelion.requestHandlerErrorTitle": "Timelion 请求错误", - "timelion.savedObjectFinder.addNewItemButtonLabel": "添加新的 {item}", - "timelion.savedObjectFinder.manageItemsButtonLabel": "管理 {items}", - "timelion.savedObjectFinder.noMatchesFoundDescription": "未找到任何匹配的 {items}。", - "timelion.savedObjectFinder.pageItemsFromHitCountDescription": "{pageFirstItem}-{pageLastItem}/{hitCount}", - "timelion.savedObjectFinder.sortByButtonLabeAscendingScreenReaderOnly": "升序", - "timelion.savedObjectFinder.sortByButtonLabeDescendingScreenReaderOnly": "降序", - "timelion.savedObjectFinder.sortByButtonLabel": "名称", - "timelion.savedObjectFinder.sortByButtonLabelScreenReaderOnly": "排序依据", - "timelion.savedObjects.howToSaveAsNewDescription": "在 Kibana 的以前版本中,更改 {savedObjectName} 的名称将创建具有新名称的副本。使用“另存为新 {savedObjectName}”复选框来立即执行此操作。", - "timelion.savedObjects.saveAsNewLabel": "另存为新的 {savedObjectName}", - "timelion.saveExpression.successNotificationText": "已保存表达式“{title}”", - "timelion.saveSheet.successNotificationText": "已保存工作表“{title}”", - "timelion.search.submitAriaLabel": "搜索", - "timelion.searchErrorTitle": "Timelion 请求错误", "timelion.serverSideErrors.argumentsOverflowErrorMessage": "太多参数传递到:{functionName}", "timelion.serverSideErrors.bucketsOverflowErrorMessage": "最大桶数已超过:允许 {bucketCount}/{maxBuckets}选择较大的时间间隔或较短的时间范围", "timelion.serverSideErrors.colorFunction.colorNotProvidedErrorMessage": "未提供颜色", @@ -4902,47 +4776,8 @@ "timelion.serverSideErrors.yaxisFunction.notSupportedUnitTypeErrorMessage": "{units} 为不受支持的单元类型。", "timelion.serverSideErrors.yaxisFunction.notValidCurrencyFormatErrorMessage": "货币必须使用三个字母的代码", "timelion.timelionDescription": "在图表上显示时间序列数据。", - "timelion.topNavMenu.addChartButtonAriaLabel": "添加图表", - "timelion.topNavMenu.addChartButtonLabel": "添加", - "timelion.topNavMenu.delete.modal.confirmButtonLabel": "删除", - "timelion.topNavMenu.delete.modal.successNotificationText": "已删除“{title}”", - "timelion.topNavMenu.delete.modal.warningText": "您无法恢复已删除的工作表。", - "timelion.topNavMenu.delete.modalTitle": "是否删除 Timelion 工作表 “{title}”?", - "timelion.topNavMenu.deleteSheetButtonAriaLabel": "删除当前工作表", - "timelion.topNavMenu.deleteSheetButtonLabel": "删除", - "timelion.topNavMenu.helpButtonAriaLabel": "帮助", - "timelion.topNavMenu.helpButtonLabel": "帮助", - "timelion.topNavMenu.newSheetButtonAriaLabel": "新建工作表", - "timelion.topNavMenu.newSheetButtonLabel": "新建", - "timelion.topNavMenu.openSheetButtonAriaLabel": "打开工作表", - "timelion.topNavMenu.openSheetButtonLabel": "打开", - "timelion.topNavMenu.openSheetTitle": "打开工作表", - "timelion.topNavMenu.options.columnsCountLabel": "列(列数必须均分为 12)", - "timelion.topNavMenu.options.rowsCountLabel": "行(这是基于当前窗口高度的目标)", - "timelion.topNavMenu.optionsButtonAriaLabel": "选项", - "timelion.topNavMenu.optionsButtonLabel": "选项", - "timelion.topNavMenu.save.saveAsDashboardPanel.inputPlaceholder": "命名此面板", - "timelion.topNavMenu.save.saveAsDashboardPanel.selectedExpressionLabel": "当前选择的表达式", - "timelion.topNavMenu.save.saveAsDashboardPanel.submitButtonLabel": "保存", - "timelion.topNavMenu.save.saveAsDashboardPanelDescription": "是否需要将图表添加到 Kibana 仪表板?我们可以做到这一点!此选项会将当前选择的表达式另存为面板,该面板可像任何其他内容一样添加到 Kibana 仪表板。注意,如果使用对其他面板的引用,则需要删除该引用,方法是:将引用的表达式直接复制到要保存的表达式中。单击图表可选择要保存的其他表达式。", - "timelion.topNavMenu.save.saveAsDashboardPanelLabel": "将表达式另存为", - "timelion.topNavMenu.save.saveAsDashboardPanelTitle": "将当前表达式另存为 Kibana 仪表板面板", - "timelion.topNavMenu.save.saveEntireSheet.inputAriaLabel": "名称", - "timelion.topNavMenu.save.saveEntireSheet.inputPlaceholder": "命名此工作表......", - "timelion.topNavMenu.save.saveEntireSheet.submitButtonLabel": "保存", - "timelion.topNavMenu.save.saveEntireSheetDescription": "如果您主要在 Timelion 应用中使用 Timelion 表达式,且不需要将 Timelion 图表添加到 Kibana 仪表板,则需要使用此选项。如果您使用对其他面板的引用,可能也需要此选项。", - "timelion.topNavMenu.save.saveEntireSheetLabel": "将工作表另存为", - "timelion.topNavMenu.save.saveEntireSheetTitle": "保存整个 Timelion 工作表", - "timelion.topNavMenu.saveSheetButtonAriaLabel": "保存工作表", - "timelion.topNavMenu.saveSheetButtonLabel": "保存", - "timelion.topNavMenu.sheetOptionsTitle": "工作表选项", - "timelion.topNavMenu.statsDescription": "查询时间 {queryTime}ms /处理时间 {processingTime}ms", - "timelion.uiSettings.defaultColumnsDescription": "默认情况下 Timelion 工作表上的列数目", - "timelion.uiSettings.defaultColumnsLabel": "默认列", "timelion.uiSettings.defaultIndexDescription": "要使用 {esParam} 搜索的默认 Elasticsearch 索引", "timelion.uiSettings.defaultIndexLabel": "默认索引", - "timelion.uiSettings.defaultRowsDescription": "默认情况下 Timelion 工作表上的行数目", - "timelion.uiSettings.defaultRowsLabel": "默认行数", "timelion.uiSettings.experimentalLabel": "实验性", "timelion.uiSettings.graphiteURLDescription": "{experimentalLabel} Graphite 主机的 URL", "timelion.uiSettings.graphiteURLLabel": "Graphite URL", @@ -4955,8 +4790,6 @@ "timelion.uiSettings.minimumIntervalLabel": "最小时间间隔", "timelion.uiSettings.quandlKeyDescription": "{experimentalLabel} 来自 www.quandl.com 的 API 密钥", "timelion.uiSettings.quandlKeyLabel": "Quandl 密钥", - "timelion.uiSettings.showTutorialDescription": "进入 Timelion 应用时我是否应该默认显示教程?", - "timelion.uiSettings.showTutorialLabel": "显示教程", "timelion.uiSettings.targetBucketsDescription": "使用自动时间间隔时想要的存储桶数目", "timelion.uiSettings.targetBucketsLabel": "目标存储桶", "timelion.uiSettings.timeFieldDescription": "使用 {esParam} 时包含时间戳的默认字段", @@ -5362,8 +5195,8 @@ "visTypeTimeseries.dataFormatPicker.customLabel": "定制", "visTypeTimeseries.dataFormatPicker.decimalPlacesLabel": "小数位数", "visTypeTimeseries.dataFormatPicker.durationLabel": "持续时间", - "visTypeTimeseries.dataFormatPicker.formatStringHelpText": "请参阅 {numeralJsLink}", - "visTypeTimeseries.dataFormatPicker.formatStringLabel": "格式字符串", + "visTypeTimeseries.dataFormatPicker.formatPatternHelpText": "文档", + "visTypeTimeseries.dataFormatPicker.formatPatternLabel": "Numeral.js 格式模式(默认值:{defaultPattern})", "visTypeTimeseries.dataFormatPicker.fromLabel": "自", "visTypeTimeseries.dataFormatPicker.numberLabel": "数字", "visTypeTimeseries.dataFormatPicker.percentLabel": "百分比", @@ -7208,7 +7041,6 @@ "xpack.apm.tutorial.apmServer.fleet.apmIntegration.button": "APM 集成", "xpack.apm.tutorial.apmServer.fleet.manageApmIntegration.button": "在 Fleet 中管理 APM 集成", "xpack.apm.tutorial.apmServer.fleet.message": "APM 集成安装用于 APM 数据的 Elasticsearch 模板和采集节点管道。", - "xpack.apm.tutorial.apmServer.fleet.title": "现在 Fleet 中包含 Elastic APM(公测版)!", "xpack.apm.tutorial.apmServer.statusCheck.btnLabel": "检查 APM Server 状态", "xpack.apm.tutorial.apmServer.statusCheck.errorMessage": "未检测到任何 APM Server。请确保其正在运行并且您已升级到 7.0 或更高版本。", "xpack.apm.tutorial.apmServer.statusCheck.successMessage": "您已正确设置 APM Server", @@ -22772,7 +22604,6 @@ "xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.network": "网络", "xpack.securitySolution.endpoint.policyDetailsConfig.mac.events.process": "进程", "xpack.securitySolution.endpoint.policyDetailsConfig.notifyUserTooltip.a": "选择用户通知选项后,在阻止或检测到{ protectionName }时将向主机用户显示通知。", - "xpack.securitySolution.endpoint.policyDetailsConfig.notifyUserTooltip.b": " 可在下方文本框中定制用户通知。括号中的标签可用于动态填充适用操作(如已阻止或已检测)和文件名。", "xpack.securitySolution.endpoint.policyDetailsConfig.protectionLevel": "防护级别", "xpack.securitySolution.endpoint.policyDetailsConfig.userNotification": "用户通知", "xpack.securitySolution.endpoint.policyDetailsConfig.windows.events.dllDriverLoad": "DLL 和驱动程序加载", diff --git a/x-pack/test/api_integration/apis/features/features/features.ts b/x-pack/test/api_integration/apis/features/features/features.ts index 6a6a0e13a1e1e..c157c8ae354ac 100644 --- a/x-pack/test/api_integration/apis/features/features/features.ts +++ b/x-pack/test/api_integration/apis/features/features/features.ts @@ -102,7 +102,6 @@ export default function ({ getService }: FtrProviderContext) { 'enterpriseSearch', 'advancedSettings', 'indexPatterns', - 'timelion', 'graph', 'monitoring', 'savedObjectsManagement', diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts index d39d9e599bcc8..42666e10341f1 100644 --- a/x-pack/test/api_integration/apis/security/privileges.ts +++ b/x-pack/test/api_integration/apis/security/privileges.ts @@ -59,7 +59,6 @@ export default function ({ getService }: FtrProviderContext) { advancedSettings: ['all', 'read'], indexPatterns: ['all', 'read'], savedObjectsManagement: ['all', 'read'], - timelion: ['all', 'read'], osquery: [ 'all', 'read', diff --git a/x-pack/test/api_integration/apis/security/privileges_basic.ts b/x-pack/test/api_integration/apis/security/privileges_basic.ts index dc00be028412b..368d5361b7e1c 100644 --- a/x-pack/test/api_integration/apis/security/privileges_basic.ts +++ b/x-pack/test/api_integration/apis/security/privileges_basic.ts @@ -28,7 +28,6 @@ export default function ({ getService }: FtrProviderContext) { indexPatterns: ['all', 'read'], savedObjectsManagement: ['all', 'read'], savedObjectsTagging: ['all', 'read'], - timelion: ['all', 'read'], graph: ['all', 'read'], maps: ['all', 'read'], canvas: ['all', 'read'], diff --git a/x-pack/test/api_integration/apis/spaces/get_active_space.ts b/x-pack/test/api_integration/apis/spaces/get_active_space.ts index e1edfdf5a7092..627b0847aee35 100644 --- a/x-pack/test/api_integration/apis/spaces/get_active_space.ts +++ b/x-pack/test/api_integration/apis/spaces/get_active_space.ts @@ -17,7 +17,7 @@ export default function ({ getService }: FtrProviderContext) { await spacesService.create({ id: 'foo-space', name: 'Foo Space', - disabledFeatures: ['timelion'], + disabledFeatures: [], color: '#AABBCC', }); }); @@ -63,7 +63,7 @@ export default function ({ getService }: FtrProviderContext) { .expect(200, { id: 'foo-space', name: 'Foo Space', - disabledFeatures: ['timelion'], + disabledFeatures: [], color: '#AABBCC', }); }); diff --git a/x-pack/test/api_integration/apis/telemetry/fixtures/basiccluster.json b/x-pack/test/api_integration/apis/telemetry/fixtures/basiccluster.json index 74d91a6215c79..1da39cf296537 100644 --- a/x-pack/test/api_integration/apis/telemetry/fixtures/basiccluster.json +++ b/x-pack/test/api_integration/apis/telemetry/fixtures/basiccluster.json @@ -195,9 +195,6 @@ "search": { "total": 0 }, - "timelion_sheet": { - "total": 0 - }, "versions": [ { "count": 1, diff --git a/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json b/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json index f1cc32f33dd2c..ad1ff9e82bb45 100644 --- a/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json +++ b/x-pack/test/api_integration/apis/telemetry/fixtures/multicluster.json @@ -972,9 +972,6 @@ "graph_workspace": { "total": 0 }, - "timelion_sheet": { - "total": 0 - }, "indices": 1, "plugins": {} }, diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts b/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts index 2412b91e6ee68..508a6584e9246 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts @@ -77,7 +77,6 @@ export default function ({ getService }: FtrProviderContext) { expect(stats.stack_stats.kibana.graph_workspace.total).to.be.a('number'); expect(stats.stack_stats.kibana.index_pattern.total).to.be.a('number'); expect(stats.stack_stats.kibana.search.total).to.be.a('number'); - expect(stats.stack_stats.kibana.timelion_sheet.total).to.be.a('number'); expect(stats.stack_stats.kibana.visualization.total).to.be.a('number'); expect(stats.stack_stats.kibana.plugins.apm.services_per_agent).to.be.an('object'); @@ -157,7 +156,6 @@ export default function ({ getService }: FtrProviderContext) { 'stack_stats.kibana.os', 'stack_stats.kibana.plugins', 'stack_stats.kibana.search', - 'stack_stats.kibana.timelion_sheet', 'stack_stats.kibana.versions', 'stack_stats.kibana.visualization', 'stack_stats.xpack.ccr', diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts b/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts index 969315cb3f98d..53225e4ea2ce0 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/query_signals.ts @@ -17,6 +17,7 @@ import { getSignalStatus, createSignalsIndex, deleteSignalsIndex } from '../../u // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); describe('query_signals_route and find_alerts_route', () => { describe('validation checks', () => { @@ -61,6 +62,49 @@ export default ({ getService }: FtrProviderContext) => { }); }); + describe('backwards compatibility', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/endpoint/resolver/signals'); + await createSignalsIndex(supertest); + }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/endpoint/resolver/signals'); + await deleteSignalsIndex(supertest); + }); + + it('should be able to filter old signals on host.os.name.caseless using runtime field', async () => { + const query = { + query: { + bool: { + should: [{ match_phrase: { 'host.os.name.caseless': 'windows' } }], + }, + }, + }; + const { body } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(query) + .expect(200); + expect(body.hits.total.value).to.eql(3); + }); + + it('should be able to filter old signals using field aliases', async () => { + const query = { + query: { + bool: { + should: [{ match_phrase: { 'kibana.alert.workflow_status': 'open' } }], + }, + }, + }; + const { body } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(query) + .expect(200); + expect(body.hits.total.value).to.eql(3); + }); + }); + describe('find_alerts_route', () => { describe('validation checks', () => { it('should not give errors when querying and the signals index does not exist yet', async () => { diff --git a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects/mappings.json b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects/mappings.json index 422e2fa74af00..a083c8f7e3bcf 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects/mappings.json +++ b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects/mappings.json @@ -44,7 +44,6 @@ "search": "181661168bbadd1eff5902361e2a0d5c", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "todo": "082a2cc96a590268344d5cd74c159ac4", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", @@ -2163,47 +2162,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "todo": { "properties": { "icon": { diff --git a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_different_key/mappings.json b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_different_key/mappings.json index 6de44ddece61d..22b79eb9bbd8e 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_different_key/mappings.json +++ b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/encrypted_saved_objects_different_key/mappings.json @@ -44,7 +44,6 @@ "search": "181661168bbadd1eff5902361e2a0d5c", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "todo": "082a2cc96a590268344d5cd74c159ac4", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", @@ -2190,47 +2189,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "todo": { "properties": { "icon": { diff --git a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json index 26364c89dba3d..74083cbfa949a 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json +++ b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json @@ -81,7 +81,6 @@ "maps-telemetry": "5ef305b18111b77789afefbd36b66171", "namespace": "2f4316de49999235636386fe51dc06c1", "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "config": "c63748b75f39d0c54de12d12c1ccbc20", "ingest-agent-policies": "8b0733cce189659593659dad8db426f0", @@ -2170,47 +2169,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/apps/maps/mapbox_styles.js b/x-pack/test/functional/apps/maps/mapbox_styles.js index a0a858c303722..58c69950590cf 100644 --- a/x-pack/test/functional/apps/maps/mapbox_styles.js +++ b/x-pack/test/functional/apps/maps/mapbox_styles.js @@ -6,7 +6,10 @@ */ import expect from '@kbn/expect'; -import { KBN_IS_TILE_COMPLETE, KBN_METADATA_FEATURE } from '../../../../plugins/maps/common'; +import { + KBN_IS_TILE_COMPLETE, + KBN_METADATA_FEATURE, +} from '../../../../plugins/maps/common/constants'; export default function ({ getPageObjects, getService }) { const PageObjects = getPageObjects(['maps']); @@ -219,6 +222,7 @@ export default function ({ getPageObjects, getService }) { const layer = mapboxStyle.layers.find((mbLayer) => { return mbLayer.id === 'n1t6f_toomanyfeatures'; }); + expect(layer).to.eql({ id: 'n1t6f_toomanyfeatures', type: 'fill', diff --git a/x-pack/test/functional/apps/saved_objects_management/exports/_7.13_import_saved_objects.ndjson b/x-pack/test/functional/apps/saved_objects_management/exports/_7.13_import_saved_objects.ndjson index 5c80b7f0624fe..21be31349af0f 100644 --- a/x-pack/test/functional/apps/saved_objects_management/exports/_7.13_import_saved_objects.ndjson +++ b/x-pack/test/functional/apps/saved_objects_management/exports/_7.13_import_saved_objects.ndjson @@ -70,7 +70,6 @@ {"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"}]","timeRestore":false,"title":"shakespeare_dashboard","version":1},"coreMigrationVersion":"7.13.2","id":"73398a90-619e-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"185283c0-619e-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"33736660-619e-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"622ac7f0-619e-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"712ebbe0-619d-11eb-aebf-c306684b328d","name":"4:panel_4","type":"search"},{"id":"ddacc820-619d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"search"},{"id":"f852d570-619d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"search"}],"sort":[1623693556928,649],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDEsNF0="} {"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[{\"meta\":{\"negate\":false,\"disabled\":false,\"alias\":null,\"type\":\"phrase\",\"key\":\"geo.srcdest\",\"value\":\"IN:US\",\"params\":{\"query\":\"IN:US\",\"type\":\"phrase\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match\":{\"geo.srcdest\":{\"query\":\"IN:US\",\"type\":\"phrase\"}}},\"$state\":{\"store\":\"appState\"}}]}"},"optionsJSON":"{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"1\",\"w\":24,\"x\":0,\"y\":0},\"panelIndex\":\"1\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"2\",\"w\":24,\"x\":24,\"y\":0},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"3\",\"w\":24,\"x\":0,\"y\":15},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"4\",\"w\":24,\"x\":24,\"y\":15},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"5\",\"w\":24,\"x\":0,\"y\":30},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"6\",\"w\":24,\"x\":24,\"y\":30},\"panelIndex\":\"6\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_6\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"7\",\"w\":24,\"x\":0,\"y\":45},\"panelIndex\":\"7\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_7\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"8\",\"w\":24,\"x\":24,\"y\":45},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"9\",\"w\":24,\"x\":0,\"y\":60},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"10\",\"w\":24,\"x\":24,\"y\":60},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_10\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"11\",\"w\":24,\"x\":0,\"y\":75},\"panelIndex\":\"11\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_11\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"12\",\"w\":24,\"x\":24,\"y\":75},\"panelIndex\":\"12\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_12\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"13\",\"w\":24,\"x\":0,\"y\":90},\"panelIndex\":\"13\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_13\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"14\",\"w\":24,\"x\":24,\"y\":90},\"panelIndex\":\"14\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_14\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"15\",\"w\":24,\"x\":0,\"y\":105},\"panelIndex\":\"15\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_15\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"16\",\"w\":24,\"x\":24,\"y\":105},\"panelIndex\":\"16\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_16\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"17\",\"w\":24,\"x\":0,\"y\":120},\"panelIndex\":\"17\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_17\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"18\",\"w\":24,\"x\":24,\"y\":120},\"panelIndex\":\"18\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_18\"},{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"h\":15,\"i\":\"19\",\"w\":24,\"x\":0,\"y\":135},\"panelIndex\":\"19\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_19\"},{\"version\":\"7.3.0\",\"type\":\"search\",\"gridData\":{\"h\":15,\"i\":\"20\",\"w\":24,\"x\":24,\"y\":135},\"panelIndex\":\"20\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_20\"}]","timeRestore":false,"title":"logstash_dashboardwithfilters","version":1},"coreMigrationVersion":"7.13.2","id":"79794f20-6249-11eb-aebf-c306684b328d","migrationVersion":{"dashboard":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"36b91810-6239-11eb-aebf-c306684b328d","name":"1:panel_1","type":"visualization"},{"id":"0a274320-61cc-11eb-aebf-c306684b328d","name":"2:panel_2","type":"visualization"},{"id":"e4aef350-623d-11eb-aebf-c306684b328d","name":"3:panel_3","type":"visualization"},{"id":"f92e5630-623e-11eb-aebf-c306684b328d","name":"4:panel_4","type":"visualization"},{"id":"9853d4d0-623d-11eb-aebf-c306684b328d","name":"5:panel_5","type":"visualization"},{"id":"6ecb33b0-623d-11eb-aebf-c306684b328d","name":"6:panel_6","type":"visualization"},{"id":"b8e35c80-623c-11eb-aebf-c306684b328d","name":"7:panel_7","type":"visualization"},{"id":"f1bc75d0-6239-11eb-aebf-c306684b328d","name":"8:panel_8","type":"visualization"},{"id":"0d8a8860-623a-11eb-aebf-c306684b328d","name":"9:panel_9","type":"visualization"},{"id":"d79fe3d0-6239-11eb-aebf-c306684b328d","name":"10:panel_10","type":"visualization"},{"id":"318375a0-6240-11eb-aebf-c306684b328d","name":"11:panel_11","type":"visualization"},{"id":"e461eb20-6245-11eb-aebf-c306684b328d","name":"12:panel_12","type":"visualization"},{"id":"25bdc750-6242-11eb-aebf-c306684b328d","name":"13:panel_13","type":"visualization"},{"id":"71dd7bc0-6248-11eb-aebf-c306684b328d","name":"14:panel_14","type":"visualization"},{"id":"6aea48a0-6240-11eb-aebf-c306684b328d","name":"15:panel_15","type":"visualization"},{"id":"32b681f0-6241-11eb-aebf-c306684b328d","name":"16:panel_16","type":"visualization"},{"id":"ccca99e0-6244-11eb-aebf-c306684b328d","name":"17:panel_17","type":"visualization"},{"id":"a4d7be80-6245-11eb-aebf-c306684b328d","name":"18:panel_18","type":"visualization"},{"id":"c94d8440-6248-11eb-aebf-c306684b328d","name":"19:panel_19","type":"visualization"},{"id":"db6226f0-61c0-11eb-aebf-c306684b328d","name":"20:panel_20","type":"search"}],"sort":[1623693556928,671],"type":"dashboard","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDIsNF0="} {"attributes":{"description":"","state":{"datasourceStates":{"indexpattern":{"layers":{"037b7937-790b-4d2d-94a5-7f5837a6ef05":{"columnOrder":["b3d46616-75e0-419e-97ea-91148961ef94","025a0fb3-dc44-4f5c-b517-2d71d3f26f14","c476db14-0cc1-40ec-863e-d2779256a407"],"columns":{"025a0fb3-dc44-4f5c-b517-2d71d3f26f14":{"dataType":"date","isBucketed":true,"label":"@timestamp","operationType":"date_histogram","params":{"interval":"auto"},"scale":"interval","sourceField":"@timestamp"},"b3d46616-75e0-419e-97ea-91148961ef94":{"dataType":"string","isBucketed":true,"label":"Top values of geo.srcdest","operationType":"terms","params":{"missingBucket":false,"orderBy":{"columnId":"c476db14-0cc1-40ec-863e-d2779256a407","type":"column"},"orderDirection":"desc","otherBucket":true,"size":3},"scale":"ordinal","sourceField":"geo.srcdest"},"c476db14-0cc1-40ec-863e-d2779256a407":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"Records"}},"incompleteColumns":{}}}}},"filters":[],"query":{"language":"lucene","query":""},"visualization":{"axisTitlesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"fittingFunction":"None","gridlinesVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"layers":[{"accessors":["c476db14-0cc1-40ec-863e-d2779256a407"],"layerId":"037b7937-790b-4d2d-94a5-7f5837a6ef05","position":"top","seriesType":"bar_stacked","showGridlines":false,"splitAccessor":"b3d46616-75e0-419e-97ea-91148961ef94","xAccessor":"025a0fb3-dc44-4f5c-b517-2d71d3f26f14"}],"legend":{"isVisible":true,"position":"right"},"preferredSeriesType":"bar_stacked","tickLabelsVisibilitySettings":{"x":true,"yLeft":true,"yRight":true},"valueLabels":"hide"}},"title":"lens_verticalstacked","visualizationType":"lnsXY"},"coreMigrationVersion":"7.13.2","id":"8dc19b50-be32-11eb-9520-1b4c3ca6a781","migrationVersion":{"lens":"7.13.1"},"references":[{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"56b34100-619d-11eb-aebf-c306684b328d","name":"indexpattern-datasource-layer-037b7937-790b-4d2d-94a5-7f5837a6ef05","type":"index-pattern"},{"id":"e6994960-bd9e-11eb-9520-1b4c3ca6a781","name":"tag-ref-e6994960-bd9e-11eb-9520-1b4c3ca6a781","type":"tag"}],"sort":[1623693556928,675],"type":"lens","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDMsNF0="} -{"attributes":{"description":"","hits":0,"timelion_chart_height":275,"timelion_columns":2,"timelion_interval":"auto","timelion_rows":2,"timelion_sheet":[".es(index=logstash-*, \"sum:bytes\")"],"title":"logstash_timelionsheet","version":1},"coreMigrationVersion":"7.13.2","id":"a8961990-be5c-11eb-9520-1b4c3ca6a781","references":[],"sort":[1623693556928,676],"type":"timelion-sheet","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDQsNF0="} {"attributes":{"columns":[],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"geo.dest\",\"params\":{\"query\":\"US\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"geo.dest\":\"US\"}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"drilldown_logstash","version":1},"coreMigrationVersion":"7.13.2","id":"b3288100-ca2c-11eb-bf5e-3de94e83d4f0","migrationVersion":{"search":"7.9.3"},"references":[{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"43fcac20-ca27-11eb-bf5e-3de94e83d4f0","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"}],"sort":[1623415891791,216],"type":"search","updated_at":"2021-06-11T12:51:31.791Z","version":"WzE0NjMsNF0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{}"},"title":"logstash_timelion_panel","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"logstash_timelion_panel\",\"type\":\"timelion\",\"aggs\":[],\"params\":{\"expression\":\".es(index=logstash-*, \\\"sum:bytes\\\")\",\"interval\":\"auto\"}}"},"coreMigrationVersion":"7.13.2","id":"b3a44cd0-be5c-11eb-9520-1b4c3ca6a781","migrationVersion":{"visualization":"7.13.1"},"references":[],"sort":[1623693556928,677],"type":"visualization","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDUsNF0="} {"attributes":{"color":"#9170B8","description":"","name":"alltogether"},"coreMigrationVersion":"7.13.2","id":"be808cb0-be32-11eb-9520-1b4c3ca6a781","references":[],"sort":[1623693556928,678],"type":"tag","updated_at":"2021-06-14T17:59:16.928Z","version":"WzE3MDYsNF0="} diff --git a/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts b/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts index 427e42b7b7a65..790909164b33d 100644 --- a/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts +++ b/x-pack/test/functional/apps/saved_objects_management/import_saved_objects_between_versions.ts @@ -48,14 +48,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.savedObjects.clickImportDone(); const importedSavedObjects = await testSubjects.getVisibleText('exportAllObjects'); // verifying the count of saved objects after importing .ndjson - await expect(importedSavedObjects).to.be('Export 88 objects'); + await expect(importedSavedObjects).to.be('Export 87 objects'); }); it('should be able to import alerts and actions saved objects from 7.14 into 8.0.0', async function () { await retry.tryForTime(10000, async () => { const existingSavedObjects = await testSubjects.getVisibleText('exportAllObjects'); // Kibana always has 1 advanced setting as a saved object - await expect(existingSavedObjects).to.be('Export 88 objects'); + await expect(existingSavedObjects).to.be('Export 87 objects'); }); await PageObjects.savedObjects.importFile( path.join(__dirname, 'exports', '_7.14_import_alerts_actions.ndjson') @@ -64,7 +64,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.savedObjects.clickImportDone(); const importedSavedObjects = await testSubjects.getVisibleText('exportAllObjects'); // verifying the count of saved objects after importing .ndjson - await expect(importedSavedObjects).to.be('Export 111 objects'); + await expect(importedSavedObjects).to.be('Export 110 objects'); }); }); } diff --git a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts b/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts deleted file mode 100644 index 8a1021abb4330..0000000000000 --- a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts +++ /dev/null @@ -1,196 +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'; -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ getPageObjects, getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const security = getService('security'); - const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects([ - 'common', - 'error', - 'header', - 'security', - 'spaceSelector', - 'timelion', - ]); - const appsMenu = getService('appsMenu'); - const globalNav = getService('globalNav'); - - describe('feature controls security', () => { - before(async () => { - await kibanaServer.importExport.load( - 'x-pack/test/functional/fixtures/kbn_archiver/timelion/feature_controls.json' - ); - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - }); - - after(async () => { - await kibanaServer.importExport.unload( - 'x-pack/test/functional/fixtures/kbn_archiver/timelion/feature_controls.json' - ); - await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional'); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/110396 - describe.skip('global timelion all privileges', () => { - before(async () => { - await security.role.create('global_timelion_all_role', { - elasticsearch: { - indices: [{ names: ['logstash-*'], privileges: ['read', 'view_index_metadata'] }], - }, - kibana: [ - { - feature: { - timelion: ['all'], - }, - spaces: ['*'], - }, - ], - }); - - await security.user.create('global_timelion_all_user', { - password: 'global_timelion_all_user-password', - roles: ['global_timelion_all_role'], - full_name: 'test user', - }); - - await PageObjects.security.forceLogout(); - - await PageObjects.security.login( - 'global_timelion_all_user', - 'global_timelion_all_user-password', - { - expectSpaceSelector: false, - } - ); - }); - - after(async () => { - await PageObjects.security.forceLogout(); - await security.role.delete('global_timelion_all_role'); - await security.user.delete('global_timelion_all_user'); - }); - - it('shows timelion navlink', async () => { - const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Overview', 'Timelion']); - }); - - it(`allows a timelion sheet to be created`, async () => { - await PageObjects.common.navigateToApp('timelion'); - await PageObjects.timelion.saveTimelionSheet(); - }); - - it(`doesn't show read-only badge`, async () => { - await globalNav.badgeMissingOrFail(); - }); - }); - - describe('global timelion read-only privileges', () => { - before(async () => { - await security.role.create('global_timelion_read_role', { - elasticsearch: { - indices: [{ names: ['logstash-*'], privileges: ['read', 'view_index_metadata'] }], - }, - kibana: [ - { - feature: { - timelion: ['read'], - }, - spaces: ['*'], - }, - ], - }); - - await security.user.create('global_timelion_read_user', { - password: 'global_timelion_read_user-password', - roles: ['global_timelion_read_role'], - full_name: 'test user', - }); - - await PageObjects.security.login( - 'global_timelion_read_user', - 'global_timelion_read_user-password', - { - expectSpaceSelector: false, - } - ); - }); - - after(async () => { - await PageObjects.security.forceLogout(); - await security.role.delete('global_timelion_read_role'); - await security.user.delete('global_timelion_read_user'); - }); - - it('shows timelion navlink', async () => { - const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Overview', 'Timelion']); - }); - - it(`does not allow a timelion sheet to be created`, async () => { - await PageObjects.common.navigateToApp('timelion'); - await PageObjects.timelion.expectMissingWriteControls(); - }); - - it(`shows read-only badge`, async () => { - await globalNav.badgeExistsOrFail('Read only'); - }); - }); - - describe('no timelion privileges', () => { - before(async () => { - await security.role.create('no_timelion_privileges_role', { - elasticsearch: { - indices: [{ names: ['logstash-*'], privileges: ['read', 'view_index_metadata'] }], - }, - kibana: [ - { - feature: { - discover: ['all'], - }, - spaces: ['*'], - }, - ], - }); - - await security.user.create('no_timelion_privileges_user', { - password: 'no_timelion_privileges_user-password', - roles: ['no_timelion_privileges_role'], - full_name: 'test user', - }); - - await PageObjects.security.forceLogout(); - - await PageObjects.security.login( - 'no_timelion_privileges_user', - 'no_timelion_privileges_user-password', - { - expectSpaceSelector: false, - } - ); - }); - - after(async () => { - await PageObjects.security.forceLogout(); - await security.role.delete('no_timelion_privileges_role'); - await security.user.delete('no_timelion_privileges_user'); - }); - - it(`returns a 403`, async () => { - await PageObjects.common.navigateToActualUrl('timelion', '', { - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - }); - PageObjects.error.expectForbidden(); - }); - }); - }); -} diff --git a/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts b/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts deleted file mode 100644 index a1dea695fce86..0000000000000 --- a/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts +++ /dev/null @@ -1,158 +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'; -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ getPageObjects, getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const spacesService = getService('spaces'); - const PageObjects = getPageObjects(['common', 'timelion', 'security', 'spaceSelector']); - const appsMenu = getService('appsMenu'); - const kibanaServer = getService('kibanaServer'); - - describe('timelion', () => { - before(async () => { - await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - }); - - describe('space with no features disabled', () => { - before(async () => { - // we need to load the following in every situation as deleting - // a space deletes all of the associated saved objects - // await esArchiver.load('x-pack/test/functional/es_archives/timelion/feature_controls'); - await kibanaServer.importExport.load( - 'x-pack/test/functional/fixtures/kbn_archiver/timelion/feature_controls.json' - ); - - await spacesService.create({ - id: 'custom_space', - name: 'custom_space', - disabledFeatures: [], - }); - - await kibanaServer.importExport.load( - 'x-pack/test/functional/fixtures/kbn_archiver/timelion/timelion_custom_space.json', - { space: 'custom_space' } - ); - }); - - after(async () => { - await spacesService.delete('custom_space'); - await kibanaServer.importExport.unload( - 'x-pack/test/functional/fixtures/kbn_archiver/timelion/feature_controls.json' - ); - }); - - it('shows timelion navlink', async () => { - await PageObjects.common.navigateToApp('home', { - basePath: '/s/custom_space', - }); - - const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.contain('Timelion'); - }); - - it(`allows a timelion sheet to be created`, async () => { - await PageObjects.common.navigateToApp('timelion', { - basePath: '/s/custom_space', - }); - - await PageObjects.timelion.saveTimelionSheet(); - }); - }); - - describe('space with Timelion disabled', () => { - before(async () => { - // we need to load the following in every situation as deleting - // a space deletes all of the associated saved objects - // await esArchiver.load('x-pack/test/functional/es_archives/timelion/feature_controls'); - - await kibanaServer.importExport.load( - 'x-pack/test/functional/fixtures/kbn_archiver/timelion/feature_controls.json' - ); - - await spacesService.create({ - id: 'custom_space', - name: 'custom_space', - disabledFeatures: ['timelion'], - }); - - await kibanaServer.importExport.load( - 'x-pack/test/functional/fixtures/kbn_archiver/timelion/timelion_custom_space.json', - { space: 'custom_space' } - ); - }); - - after(async () => { - await spacesService.delete('custom_space'); - await kibanaServer.importExport.unload( - 'x-pack/test/functional/fixtures/kbn_archiver/timelion/feature_controls.json' - ); - }); - - it(`doesn't show timelion navlink`, async () => { - await PageObjects.common.navigateToApp('home', { - basePath: '/s/custom_space', - }); - const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).not.to.contain('Timelion'); - }); - - it(`create new timelion returns a 404`, async () => { - await PageObjects.common.navigateToActualUrl('timelion', 'i-exist', { - basePath: '/s/custom_space', - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - }); - - const messageText = await PageObjects.common.getJsonBodyText(); - expect(messageText).to.eql( - JSON.stringify({ - statusCode: 404, - error: 'Not Found', - message: 'Not Found', - }) - ); - }); - - it(`edit timelion sheet which doesn't exist returns a 404`, async () => { - await PageObjects.common.navigateToActualUrl('timelion', 'i-dont-exist', { - basePath: '/s/custom_space', - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - }); - - const messageText = await PageObjects.common.getJsonBodyText(); - expect(messageText).to.eql( - JSON.stringify({ - statusCode: 404, - error: 'Not Found', - message: 'Not Found', - }) - ); - }); - - it(`edit timelion sheet which exists returns a 404`, async () => { - await PageObjects.common.navigateToActualUrl('timelion', 'i-exist', { - basePath: '/s/custom_space', - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - }); - - const messageText = await PageObjects.common.getJsonBodyText(); - expect(messageText).to.eql( - JSON.stringify({ - statusCode: 404, - error: 'Not Found', - message: 'Not Found', - }) - ); - }); - }); - }); -} diff --git a/x-pack/test/functional/apps/timelion/index.ts b/x-pack/test/functional/apps/timelion/index.ts deleted file mode 100644 index 009f080adddec..0000000000000 --- a/x-pack/test/functional/apps/timelion/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function timelion({ loadTestFile }: FtrProviderContext) { - describe('Timelion', function visualizeTestSuite() { - this.tags(['ciGroup4', 'skipFirefox']); - - loadTestFile(require.resolve('./feature_controls/timelion_security')); - loadTestFile(require.resolve('./feature_controls/timelion_spaces')); - }); -} diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 87a21f815bfd4..f8e8ce76bace1 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -40,7 +40,6 @@ export default async function ({ readConfigFile }) { resolve(__dirname, './apps/rollup_job'), resolve(__dirname, './apps/maps'), resolve(__dirname, './apps/status_page'), - resolve(__dirname, './apps/timelion'), resolve(__dirname, './apps/upgrade_assistant'), resolve(__dirname, './apps/visualize'), resolve(__dirname, './apps/uptime'), @@ -87,7 +86,6 @@ export default async function ({ readConfigFile }) { '--xpack.security.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', // server restarts should not invalidate active sessions '--xpack.encryptedSavedObjects.encryptionKey="DkdXazszSCYexXqz4YktBGHCRkV6hyNK"', '--xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled=true', - '--timelion.ui.enabled=true', '--savedObjects.maxImportPayloadBytes=10485760', // for OSS test management/_import_objects '--xpack.observability.unsafe.cases.enabled=true', '--xpack.observability.unsafe.alertingExperience.enabled=true', // NOTE: Can be removed once enabled by default diff --git a/x-pack/test/functional/es_archives/action_task_params/mappings.json b/x-pack/test/functional/es_archives/action_task_params/mappings.json index d0eb35fa3b157..2bb6be179d890 100644 --- a/x-pack/test/functional/es_archives/action_task_params/mappings.json +++ b/x-pack/test/functional/es_archives/action_task_params/mappings.json @@ -67,7 +67,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2312,47 +2311,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/actions/mappings.json b/x-pack/test/functional/es_archives/actions/mappings.json index 8289174ffd57d..eeb9d09d3b0a2 100644 --- a/x-pack/test/functional/es_archives/actions/mappings.json +++ b/x-pack/test/functional/es_archives/actions/mappings.json @@ -67,7 +67,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2311,47 +2310,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/alerts/mappings.json b/x-pack/test/functional/es_archives/alerts/mappings.json index 287d9a79a68cf..ecaf138a0cdc4 100644 --- a/x-pack/test/functional/es_archives/alerts/mappings.json +++ b/x-pack/test/functional/es_archives/alerts/mappings.json @@ -45,7 +45,6 @@ "search": "181661168bbadd1eff5902361e2a0d5c", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "todo": "082a2cc96a590268344d5cd74c159ac4", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", diff --git a/x-pack/test/functional/es_archives/alerts_legacy/mappings.json b/x-pack/test/functional/es_archives/alerts_legacy/mappings.json index 8c33155636a8b..69edf30c1ffd0 100644 --- a/x-pack/test/functional/es_archives/alerts_legacy/mappings.json +++ b/x-pack/test/functional/es_archives/alerts_legacy/mappings.json @@ -66,7 +66,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2312,47 +2311,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/banners/multispace/mappings.json b/x-pack/test/functional/es_archives/banners/multispace/mappings.json index 9f3201d73abc1..f813fca64c328 100644 --- a/x-pack/test/functional/es_archives/banners/multispace/mappings.json +++ b/x-pack/test/functional/es_archives/banners/multispace/mappings.json @@ -180,48 +180,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/canvas/default/mappings.json b/x-pack/test/functional/es_archives/canvas/default/mappings.json index 3bde3969e5ded..61481a0f949fa 100644 --- a/x-pack/test/functional/es_archives/canvas/default/mappings.json +++ b/x-pack/test/functional/es_archives/canvas/default/mappings.json @@ -245,47 +245,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/canvas/reports/mappings.json b/x-pack/test/functional/es_archives/canvas/reports/mappings.json index 66f40d746b794..047a52aaa1f98 100644 --- a/x-pack/test/functional/es_archives/canvas/reports/mappings.json +++ b/x-pack/test/functional/es_archives/canvas/reports/mappings.json @@ -70,7 +70,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2175,47 +2174,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.10.0/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/7.10.0/mappings.json index 7101af08400a2..c7e1a180e6c54 100644 --- a/x-pack/test/functional/es_archives/cases/migrations/7.10.0/mappings.json +++ b/x-pack/test/functional/es_archives/cases/migrations/7.10.0/mappings.json @@ -67,7 +67,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2305,47 +2304,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.11.1/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/7.11.1/mappings.json index 5140a81b3ed46..bc719ab40aebd 100644 --- a/x-pack/test/functional/es_archives/cases/migrations/7.11.1/mappings.json +++ b/x-pack/test/functional/es_archives/cases/migrations/7.11.1/mappings.json @@ -78,7 +78,6 @@ "spaces-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", "tag": "83d55da58f6530f7055415717ec06474", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2502,47 +2501,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/cases/migrations/7.13.2/mappings.json b/x-pack/test/functional/es_archives/cases/migrations/7.13.2/mappings.json index e79ebf2b8fc10..88f3e5d78cb16 100644 --- a/x-pack/test/functional/es_archives/cases/migrations/7.13.2/mappings.json +++ b/x-pack/test/functional/es_archives/cases/migrations/7.13.2/mappings.json @@ -84,7 +84,6 @@ "spaces-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", "tag": "83d55da58f6530f7055415717ec06474", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2643,47 +2642,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json b/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json index 5001bb053e01b..1766723cc436f 100644 --- a/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json +++ b/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json @@ -137,48 +137,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/dashboard/drilldowns/mappings.json b/x-pack/test/functional/es_archives/dashboard/drilldowns/mappings.json index a842c20b6965e..8382a490ac230 100644 --- a/x-pack/test/functional/es_archives/dashboard/drilldowns/mappings.json +++ b/x-pack/test/functional/es_archives/dashboard/drilldowns/mappings.json @@ -137,48 +137,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json index 29c56a751c038..a4392cd88b356 100644 --- a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json +++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json @@ -386,47 +386,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json index 0cd1a29f92241..51dfbb6d066fc 100644 --- a/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json +++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json @@ -358,47 +358,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/dashboard/session_in_space/mappings.json b/x-pack/test/functional/es_archives/dashboard/session_in_space/mappings.json index a842c20b6965e..8382a490ac230 100644 --- a/x-pack/test/functional/es_archives/dashboard/session_in_space/mappings.json +++ b/x-pack/test/functional/es_archives/dashboard/session_in_space/mappings.json @@ -137,48 +137,6 @@ } } }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/data/search_sessions/mappings.json b/x-pack/test/functional/es_archives/data/search_sessions/mappings.json index 9c27bc1458c7f..a3eab7787c3eb 100644 --- a/x-pack/test/functional/es_archives/data/search_sessions/mappings.json +++ b/x-pack/test/functional/es_archives/data/search_sessions/mappings.json @@ -2356,47 +2356,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/empty_kibana/mappings.json b/x-pack/test/functional/es_archives/empty_kibana/mappings.json index 499c994780285..07dc66dd8ce94 100644 --- a/x-pack/test/functional/es_archives/empty_kibana/mappings.json +++ b/x-pack/test/functional/es_archives/empty_kibana/mappings.json @@ -177,48 +177,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/agent_only/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/agent_only/mappings.json index 9d7da7b5bbd87..4dcb8f905b197 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/agent_only/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/agent_only/mappings.json @@ -66,7 +66,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2336,47 +2335,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_different_states/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_different_states/mappings.json index 7aadc43a3ad94..d6d91281151aa 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_different_states/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_different_states/mappings.json @@ -67,7 +67,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2341,47 +2340,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_installed/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_installed/mappings.json index 7aadc43a3ad94..d6d91281151aa 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_installed/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_installed/mappings.json @@ -67,7 +67,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2341,47 +2340,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_uninstalled/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_uninstalled/mappings.json index 7aadc43a3ad94..d6d91281151aa 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_uninstalled/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/cloned_endpoint_uninstalled/mappings.json @@ -67,7 +67,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2341,47 +2340,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_disabled/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_disabled/mappings.json index 9d7da7b5bbd87..4dcb8f905b197 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_disabled/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_disabled/mappings.json @@ -66,7 +66,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2336,47 +2335,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_enabled/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_enabled/mappings.json index 9d7da7b5bbd87..4dcb8f905b197 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_enabled/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_malware_enabled/mappings.json @@ -66,7 +66,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2336,47 +2335,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_uninstalled/mappings.json b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_uninstalled/mappings.json index 9d7da7b5bbd87..4dcb8f905b197 100644 --- a/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_uninstalled/mappings.json +++ b/x-pack/test/functional/es_archives/endpoint/telemetry/endpoint_uninstalled/mappings.json @@ -66,7 +66,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2336,47 +2335,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/event_log_multiple_indicies/mappings.json b/x-pack/test/functional/es_archives/event_log_multiple_indicies/mappings.json index 15382fb2524fe..b28c558612770 100644 --- a/x-pack/test/functional/es_archives/event_log_multiple_indicies/mappings.json +++ b/x-pack/test/functional/es_archives/event_log_multiple_indicies/mappings.json @@ -71,7 +71,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", diff --git a/x-pack/test/functional/es_archives/fleet/agents/mappings.json b/x-pack/test/functional/es_archives/fleet/agents/mappings.json index e9a3a965c0523..8e4c7a912b75a 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/mappings.json +++ b/x-pack/test/functional/es_archives/fleet/agents/mappings.json @@ -65,7 +65,6 @@ "maps-telemetry": "bfd39d88aadadb4be597ea984d433dbe", "namespace": "2f4316de49999235636386fe51dc06c1", "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "config": "ae24d22d5986d04124cc6568f771066f", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215" @@ -2846,47 +2845,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/hybrid/kibana/mappings.json b/x-pack/test/functional/es_archives/hybrid/kibana/mappings.json index a352155899e79..af4ee5ed1e46f 100644 --- a/x-pack/test/functional/es_archives/hybrid/kibana/mappings.json +++ b/x-pack/test/functional/es_archives/hybrid/kibana/mappings.json @@ -35,7 +35,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "25de8c2deec044392922989cfcf24c54", "telemetry": "e1c8bc94e443aefd9458932cc0697a4d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", @@ -890,47 +889,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json b/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json index 63cc283f96d32..0d41e0ce86c14 100644 --- a/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json +++ b/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json @@ -143,47 +143,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/kibana_scripted_fields_on_logstash/mappings.json b/x-pack/test/functional/es_archives/kibana_scripted_fields_on_logstash/mappings.json index c0ce939234a9d..7479c525f6a07 100644 --- a/x-pack/test/functional/es_archives/kibana_scripted_fields_on_logstash/mappings.json +++ b/x-pack/test/functional/es_archives/kibana_scripted_fields_on_logstash/mappings.json @@ -50,7 +50,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2522,47 +2521,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/lens/basic/data.json.gz b/x-pack/test/functional/es_archives/lens/basic/data.json.gz deleted file mode 100644 index 5e43aa490e1db..0000000000000 Binary files a/x-pack/test/functional/es_archives/lens/basic/data.json.gz and /dev/null differ diff --git a/x-pack/test/functional/es_archives/lens/basic/mappings.json b/x-pack/test/functional/es_archives/lens/basic/mappings.json index 50230a5eff0c2..5ff0a0e4661c3 100644 --- a/x-pack/test/functional/es_archives/lens/basic/mappings.json +++ b/x-pack/test/functional/es_archives/lens/basic/mappings.json @@ -36,7 +36,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "e1c8bc94e443aefd9458932cc0697a4d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", @@ -1113,47 +1112,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/lens/reporting/mappings.json b/x-pack/test/functional/es_archives/lens/reporting/mappings.json index a71efbd3e59ec..907c5ee4fe70a 100644 --- a/x-pack/test/functional/es_archives/lens/reporting/mappings.json +++ b/x-pack/test/functional/es_archives/lens/reporting/mappings.json @@ -36,7 +36,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "e1c8bc94e443aefd9458932cc0697a4d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", @@ -1122,47 +1121,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/lens/rollup/config/mappings.json b/x-pack/test/functional/es_archives/lens/rollup/config/mappings.json index 50230a5eff0c2..5ff0a0e4661c3 100644 --- a/x-pack/test/functional/es_archives/lens/rollup/config/mappings.json +++ b/x-pack/test/functional/es_archives/lens/rollup/config/mappings.json @@ -36,7 +36,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "e1c8bc94e443aefd9458932cc0697a4d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", @@ -1113,47 +1112,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/lists/mappings.json b/x-pack/test/functional/es_archives/lists/mappings.json index 2c6e948cedf6c..134ed28bac2e7 100644 --- a/x-pack/test/functional/es_archives/lists/mappings.json +++ b/x-pack/test/functional/es_archives/lists/mappings.json @@ -71,7 +71,6 @@ "namespace": "2f4316de49999235636386fe51dc06c1", "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", "ingest-package-policies": "48e8bd97e488008e21c0b5a2367b83ad", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "config": "c63748b75f39d0c54de12d12c1ccbc20", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", @@ -2265,47 +2264,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/logstash/empty/mappings.json b/x-pack/test/functional/es_archives/logstash/empty/mappings.json index 98fb761389a24..6a3f685752258 100644 --- a/x-pack/test/functional/es_archives/logstash/empty/mappings.json +++ b/x-pack/test/functional/es_archives/logstash/empty/mappings.json @@ -209,48 +209,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/logstash/example_pipelines/mappings.json b/x-pack/test/functional/es_archives/logstash/example_pipelines/mappings.json index 56a2e144ff89c..b221ce613d72d 100644 --- a/x-pack/test/functional/es_archives/logstash/example_pipelines/mappings.json +++ b/x-pack/test/functional/es_archives/logstash/example_pipelines/mappings.json @@ -206,48 +206,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/monitoring/basic_6.3.x/data.json.gz b/x-pack/test/functional/es_archives/monitoring/basic_6.3.x/data.json.gz index e1e8437948dea..9c15082c70f02 100644 Binary files a/x-pack/test/functional/es_archives/monitoring/basic_6.3.x/data.json.gz and b/x-pack/test/functional/es_archives/monitoring/basic_6.3.x/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/observability/alerts/data.json.gz b/x-pack/test/functional/es_archives/observability/alerts/data.json.gz index 45da368188284..bcccaeda999c0 100644 Binary files a/x-pack/test/functional/es_archives/observability/alerts/data.json.gz and b/x-pack/test/functional/es_archives/observability/alerts/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/observability/alerts/mappings.json b/x-pack/test/functional/es_archives/observability/alerts/mappings.json index 88d12b7d797bb..63750ddafe329 100644 --- a/x-pack/test/functional/es_archives/observability/alerts/mappings.json +++ b/x-pack/test/functional/es_archives/observability/alerts/mappings.json @@ -65,8 +65,12 @@ } } }, - "id": { - "type": "keyword" + "instance": { + "properties": { + "id": { + "type": "keyword" + } + } }, "reason": { "type": "keyword" @@ -325,8 +329,12 @@ } } }, - "id": { - "type": "keyword" + "instance": { + "properties": { + "id": { + "type": "keyword" + } + } }, "reason": { "type": "keyword" @@ -561,8 +569,12 @@ } } }, - "id": { - "type": "keyword" + "instance": { + "properties": { + "id": { + "type": "keyword" + } + } }, "reason": { "type": "keyword" diff --git a/x-pack/test/functional/es_archives/packaging/mappings.json b/x-pack/test/functional/es_archives/packaging/mappings.json index 182d281c9a3de..0ec1e12567460 100644 --- a/x-pack/test/functional/es_archives/packaging/mappings.json +++ b/x-pack/test/functional/es_archives/packaging/mappings.json @@ -74,7 +74,6 @@ "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "tag": "83d55da58f6530f7055415717ec06474", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2283,47 +2282,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/reporting/canvas_disallowed_url/mappings.json b/x-pack/test/functional/es_archives/reporting/canvas_disallowed_url/mappings.json index aa097f9bb214e..5bffb9540e983 100644 --- a/x-pack/test/functional/es_archives/reporting/canvas_disallowed_url/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/canvas_disallowed_url/mappings.json @@ -61,7 +61,6 @@ "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -1971,47 +1970,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/mappings.json b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/mappings.json index 9d6c0ecf9898d..254184cbef584 100644 --- a/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/ecommerce_kibana_spaces/mappings.json @@ -66,7 +66,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2388,47 +2387,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json b/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json index 8580f216a06f6..1fdde9d9d208b 100644 --- a/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/hugedata/mappings.json @@ -2307,47 +2307,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/reporting/logs/mappings.json b/x-pack/test/functional/es_archives/reporting/logs/mappings.json index ffb8c85e8fd8b..2e1873e43ffcc 100644 --- a/x-pack/test/functional/es_archives/reporting/logs/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/logs/mappings.json @@ -195,48 +195,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/reporting/multi_index_kibana/mappings.json b/x-pack/test/functional/es_archives/reporting/multi_index_kibana/mappings.json index 97b9599bc86cc..f950877aea332 100644 --- a/x-pack/test/functional/es_archives/reporting/multi_index_kibana/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/multi_index_kibana/mappings.json @@ -41,7 +41,6 @@ "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -1836,47 +1835,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/functional/es_archives/reporting/nanos/mappings.json b/x-pack/test/functional/es_archives/reporting/nanos/mappings.json index 86451ee4303af..216b89e4bfbcf 100644 --- a/x-pack/test/functional/es_archives/reporting/nanos/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/nanos/mappings.json @@ -34,7 +34,6 @@ "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "space": "25de8c2deec044392922989cfcf24c54", "telemetry": "e1c8bc94e443aefd9458932cc0697a4d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", @@ -881,47 +880,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/reporting/sales/mappings.json b/x-pack/test/functional/es_archives/reporting/sales/mappings.json index dbb8d396eb496..317b185046ce1 100644 --- a/x-pack/test/functional/es_archives/reporting/sales/mappings.json +++ b/x-pack/test/functional/es_archives/reporting/sales/mappings.json @@ -195,48 +195,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/saved_objects_management/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/saved_objects_management/feature_controls/security/mappings.json index 05ca4d8e8307e..bb863dc24c585 100644 --- a/x-pack/test/functional/es_archives/saved_objects_management/feature_controls/security/mappings.json +++ b/x-pack/test/functional/es_archives/saved_objects_management/feature_controls/security/mappings.json @@ -358,47 +358,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/spaces/copy_saved_objects/mappings.json b/x-pack/test/functional/es_archives/spaces/copy_saved_objects/mappings.json index d860c19ceb64c..092f8a326d9df 100644 --- a/x-pack/test/functional/es_archives/spaces/copy_saved_objects/mappings.json +++ b/x-pack/test/functional/es_archives/spaces/copy_saved_objects/mappings.json @@ -178,47 +178,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/spaces/disabled_features/mappings.json b/x-pack/test/functional/es_archives/spaces/disabled_features/mappings.json index 23a63e372a855..5bda0101c653a 100644 --- a/x-pack/test/functional/es_archives/spaces/disabled_features/mappings.json +++ b/x-pack/test/functional/es_archives/spaces/disabled_features/mappings.json @@ -358,47 +358,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json b/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json index 9f3201d73abc1..f813fca64c328 100644 --- a/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json +++ b/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json @@ -180,48 +180,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json b/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json index 499c994780285..07dc66dd8ce94 100644 --- a/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json +++ b/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json @@ -177,48 +177,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/spaces/selector/mappings.json b/x-pack/test/functional/es_archives/spaces/selector/mappings.json index 499c994780285..07dc66dd8ce94 100644 --- a/x-pack/test/functional/es_archives/spaces/selector/mappings.json +++ b/x-pack/test/functional/es_archives/spaces/selector/mappings.json @@ -177,48 +177,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/task_manager_removed_types/mappings.json b/x-pack/test/functional/es_archives/task_manager_removed_types/mappings.json index 6ec81326d1ca4..f35681efb089d 100644 --- a/x-pack/test/functional/es_archives/task_manager_removed_types/mappings.json +++ b/x-pack/test/functional/es_archives/task_manager_removed_types/mappings.json @@ -75,7 +75,6 @@ "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "tag": "83d55da58f6530f7055415717ec06474", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", diff --git a/x-pack/test/functional/es_archives/timelion/feature_controls/data.json b/x-pack/test/functional/es_archives/timelion/feature_controls/data.json index 03fc0d57e9278..59da22294aa5a 100644 --- a/x-pack/test/functional/es_archives/timelion/feature_controls/data.json +++ b/x-pack/test/functional/es_archives/timelion/feature_controls/data.json @@ -29,53 +29,4 @@ } } } -} - -{ - "type": "doc", - "value": { - "index": ".kibana", - "id": "timelion-sheet:i-exist", - "source": { - "timelion-sheet": { - "title": "i-exist", - "hits": 0, - "description": "", - "timelion_sheet": [ - ".es(*)" - ], - "timelion_interval": "auto", - "timelion_chart_height": 275, - "timelion_columns": 2, - "timelion_rows": 2, - "version": 1 - }, - "type": "timelion-sheet" - } - } -} - -{ - "type": "doc", - "value": { - "index": ".kibana", - "id": "custom_space:timelion-sheet:i-exist", - "source": { - "namespace": "custom_space", - "timelion-sheet": { - "title": "i-exist", - "hits": 0, - "description": "", - "timelion_sheet": [ - ".es(*).label('custom space sheet')" - ], - "timelion_interval": "auto", - "timelion_chart_height": 275, - "timelion_columns": 2, - "timelion_rows": 2, - "version": 1 - }, - "type": "timelion-sheet" - } - } } \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/timelion/feature_controls/mappings.json b/x-pack/test/functional/es_archives/timelion/feature_controls/mappings.json index 23a63e372a855..5bda0101c653a 100644 --- a/x-pack/test/functional/es_archives/timelion/feature_controls/mappings.json +++ b/x-pack/test/functional/es_archives/timelion/feature_controls/mappings.json @@ -358,47 +358,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/es_archives/visualize/default/mappings.json b/x-pack/test/functional/es_archives/visualize/default/mappings.json index be3b287032911..00dddbcafdd1c 100644 --- a/x-pack/test/functional/es_archives/visualize/default/mappings.json +++ b/x-pack/test/functional/es_archives/visualize/default/mappings.json @@ -79,7 +79,6 @@ "spaces-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", "tag": "83d55da58f6530f7055415717ec06474", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "type": "2f4316de49999235636386fe51dc06c1", "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -2423,47 +2422,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/functional/fixtures/kbn_archiver/timelion/feature_controls.json b/x-pack/test/functional/fixtures/kbn_archiver/timelion/feature_controls.json deleted file mode 100644 index 323dbb67d54b8..0000000000000 --- a/x-pack/test/functional/fixtures/kbn_archiver/timelion/feature_controls.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "attributes": { - "buildNum": 9007199254740991, - "defaultIndex": "logstash-*" - }, - "coreMigrationVersion": "7.14.0", - "id": "7.0.0", - "migrationVersion": { - "config": "7.13.0" - }, - "references": [], - "type": "config", - "updated_at": "2019-01-22T19:32:02.235Z", - "version": "WzQsMl0=" -} - -{ - "attributes": { - "description": "", - "hits": 0, - "timelion_chart_height": 275, - "timelion_columns": 2, - "timelion_interval": "auto", - "timelion_rows": 2, - "timelion_sheet": [ - ".es(*)" - ], - "title": "i-exist", - "version": 1 - }, - "coreMigrationVersion": "7.14.0", - "id": "i-exist", - "references": [], - "type": "timelion-sheet", - "version": "WzYsMl0=" -} - -{ - "attributes": { - "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kilobytes\",\"type\":\"number\",\"count\":0,\"scripted\":true,\"script\":\"doc['bytes'].value / 1000\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"machine os raw\",\"type\":\"string\",\"count\":0,\"scripted\":true,\"script\":\"doc['machine.os.raw'].value\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]", - "timeFieldName": "@timestamp", - "title": "logstash-*" - }, - "coreMigrationVersion": "7.14.0", - "id": "logstash-*", - "migrationVersion": { - "index-pattern": "7.11.0" - }, - "references": [], - "type": "index-pattern", - "version": "WzUsMl0=" -} \ No newline at end of file diff --git a/x-pack/test/functional/fixtures/kbn_archiver/timelion/timelion_custom_space.json b/x-pack/test/functional/fixtures/kbn_archiver/timelion/timelion_custom_space.json deleted file mode 100644 index f27149f9d7eb6..0000000000000 --- a/x-pack/test/functional/fixtures/kbn_archiver/timelion/timelion_custom_space.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "attributes": { - "description": "", - "hits": 0, - "timelion_chart_height": 275, - "timelion_columns": 2, - "timelion_interval": "auto", - "timelion_rows": 2, - "timelion_sheet": [ - ".es(*).label('custom space sheet')" - ], - "title": "i-exist", - "version": 1 - }, - "coreMigrationVersion": "7.14.0", - "id": "i-exist", - "references": [], - "type": "timelion-sheet", - "version": "WzcsMl0=" -} \ No newline at end of file diff --git a/x-pack/test/functional/services/observability/alerts.ts b/x-pack/test/functional/services/observability/alerts.ts new file mode 100644 index 0000000000000..ba7f952b30c64 --- /dev/null +++ b/x-pack/test/functional/services/observability/alerts.ts @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import querystring from 'querystring'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper'; + +// Based on the x-pack/test/functional/es_archives/observability/alerts archive. +const DATE_WITH_DATA = { + rangeFrom: '2021-09-01T13:36:22.109Z', + rangeTo: '2021-09-03T13:36:22.109Z', +}; + +const ALERTS_FLYOUT_SELECTOR = 'alertsFlyout'; + +export function ObservabilityAlertsProvider({ getPageObjects, getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const flyoutService = getService('flyout'); + const pageObjects = getPageObjects(['common']); + const retry = getService('retry'); + + const navigateToTimeWithData = async () => { + return await pageObjects.common.navigateToUrlWithBrowserHistory( + 'observability', + '/alerts', + `?${querystring.stringify(DATE_WITH_DATA)}` + ); + }; + + const getTableCells = async () => { + // NOTE: This isn't ideal, but EuiDataGrid doesn't really have the concept of "rows" + return await testSubjects.findAll('dataGridRowCell'); + }; + + const getTableOrFail = async () => { + return await testSubjects.existOrFail('events-viewer-panel'); + }; + + const getNoDataStateOrFail = async () => { + return await testSubjects.existOrFail('tGridEmptyState'); + }; + + // Query Bar + const getQueryBar = async () => { + return await testSubjects.find('queryInput'); + }; + + const getQuerySubmitButton = async () => { + return await testSubjects.find('querySubmitButton'); + }; + + const clearQueryBar = async () => { + return await (await getQueryBar()).clearValueWithKeyboard({ charByChar: true }); + }; + + const typeInQueryBar = async (query: string) => { + return await (await getQueryBar()).type(query); + }; + + const submitQuery = async (query: string) => { + await typeInQueryBar(query); + return await (await getQuerySubmitButton()).click(); + }; + + // Flyout + const getOpenFlyoutButton = async () => { + return await testSubjects.find('openFlyoutButton'); + }; + + const openAlertsFlyout = async () => { + await (await getOpenFlyoutButton()).click(); + await retry.waitFor( + 'flyout open', + async () => await testSubjects.exists(ALERTS_FLYOUT_SELECTOR, { timeout: 2500 }) + ); + }; + + const getAlertsFlyout = async () => { + return await testSubjects.find(ALERTS_FLYOUT_SELECTOR); + }; + + const getAlertsFlyoutOrFail = async () => { + return await testSubjects.existOrFail(ALERTS_FLYOUT_SELECTOR); + }; + + const getAlertsFlyoutTitle = async () => { + return await testSubjects.find('alertsFlyoutTitle'); + }; + + const closeAlertsFlyout = async () => { + return await flyoutService.close(ALERTS_FLYOUT_SELECTOR); + }; + + const getAlertsFlyoutViewInAppButtonOrFail = async () => { + return await testSubjects.existOrFail('alertsFlyoutViewInAppButton'); + }; + + const getAlertsFlyoutDescriptionListTitles = async (): Promise => { + const flyout = await getAlertsFlyout(); + return await testSubjects.findAllDescendant('alertsFlyoutDescriptionListTitle', flyout); + }; + + const getAlertsFlyoutDescriptionListDescriptions = async (): Promise => { + const flyout = await getAlertsFlyout(); + return await testSubjects.findAllDescendant('alertsFlyoutDescriptionListDescription', flyout); + }; + + return { + clearQueryBar, + typeInQueryBar, + submitQuery, + getTableCells, + getTableOrFail, + getNoDataStateOrFail, + openAlertsFlyout, + getAlertsFlyout, + getAlertsFlyoutTitle, + closeAlertsFlyout, + navigateToTimeWithData, + getAlertsFlyoutOrFail, + getAlertsFlyoutViewInAppButtonOrFail, + getAlertsFlyoutDescriptionListTitles, + getAlertsFlyoutDescriptionListDescriptions, + }; +} diff --git a/x-pack/test/functional/services/observability/index.ts b/x-pack/test/functional/services/observability/index.ts index 14f931d93b56f..0d167ae5d516e 100644 --- a/x-pack/test/functional/services/observability/index.ts +++ b/x-pack/test/functional/services/observability/index.ts @@ -7,11 +7,14 @@ import { FtrProviderContext } from '../../ftr_provider_context'; import { ObservabilityUsersProvider } from './users'; +import { ObservabilityAlertsProvider } from './alerts'; export function ObservabilityProvider(context: FtrProviderContext) { const users = ObservabilityUsersProvider(context); + const alerts = ObservabilityAlertsProvider(context); return { users, + alerts, }; } diff --git a/x-pack/test/observability_functional/apps/observability/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/alerts/index.ts index ae60eff1859ba..2009f46f432d4 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/index.ts @@ -6,32 +6,29 @@ */ import expect from '@kbn/expect'; -import querystring from 'querystring'; import { FtrProviderContext } from '../../../ftr_provider_context'; -// Based on the x-pack/test/functional/es_archives/observability/alerts archive. -const DATE_WITH_DATA = { - rangeFrom: '2021-08-31T13:36:22.109Z', - rangeTo: '2021-09-01T13:36:22.109Z', -}; +async function asyncForEach(array: T[], callback: (item: T, index: number) => void) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index); + } +} export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); - // FLAKY: https://github.com/elastic/kibana/issues/110920 + // Failing: See https://github.com/elastic/kibana/issues/111907 describe.skip('Observability alerts', function () { this.tags('includeFirefox'); const pageObjects = getPageObjects(['common']); const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const observability = getService('observability'); before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); - await pageObjects.common.navigateToUrlWithBrowserHistory( - 'observability', - '/alerts', - `?${querystring.stringify(DATE_WITH_DATA)}` - ); + await observability.alerts.navigateToTimeWithData(); }); after(async () => { @@ -40,13 +37,123 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Alerts table', () => { it('Renders the table', async () => { - await testSubjects.existOrFail('events-viewer-panel'); + await observability.alerts.getTableOrFail(); }); it('Renders the correct number of cells', async () => { // NOTE: This isn't ideal, but EuiDataGrid doesn't really have the concept of "rows" - const cells = await testSubjects.findAll('dataGridRowCell'); - expect(cells.length).to.be(54); + const cells = await observability.alerts.getTableCells(); + expect(cells.length).to.be(72); + }); + + describe('Filtering', () => { + afterEach(async () => { + await observability.alerts.clearQueryBar(); + }); + + after(async () => { + // NOTE: We do this as the query bar takes the place of the datepicker when it is in focus, so we'll reset + // back to default. + await observability.alerts.submitQuery(''); + }); + + it('Autocompletion works', async () => { + await observability.alerts.typeInQueryBar('kibana.alert.s'); + await testSubjects.existOrFail('autocompleteSuggestion-field-kibana.alert.start-'); + await testSubjects.existOrFail('autocompleteSuggestion-field-kibana.alert.status-'); + }); + + it('Applies filters correctly', async () => { + await observability.alerts.submitQuery('kibana.alert.status: recovered'); + await retry.try(async () => { + const cells = await observability.alerts.getTableCells(); + expect(cells.length).to.be(24); + }); + }); + + it('Displays a no data state when filters produce zero results', async () => { + await observability.alerts.submitQuery('kibana.alert.consumer: uptime'); + await observability.alerts.getNoDataStateOrFail(); + }); + }); + + describe('Date selection', () => { + after(async () => { + await observability.alerts.navigateToTimeWithData(); + }); + + it('Correctly applies date picker selections', async () => { + await retry.try(async () => { + await (await testSubjects.find('superDatePickerToggleQuickMenuButton')).click(); + // We shouldn't expect any data for the last 15 minutes + await (await testSubjects.find('superDatePickerCommonlyUsed_Last_15 minutes')).click(); + }); + await observability.alerts.getNoDataStateOrFail(); + await pageObjects.common.waitUntilUrlIncludes('rangeFrom=now-15m&rangeTo=now'); + }); + }); + + describe('Flyout', () => { + it('Can be opened', async () => { + await observability.alerts.openAlertsFlyout(); + await observability.alerts.getAlertsFlyoutOrFail(); + }); + + it('Can be closed', async () => { + await observability.alerts.closeAlertsFlyout(); + await testSubjects.missingOrFail('alertsFlyout'); + }); + + describe('When open', async () => { + before(async () => { + await observability.alerts.openAlertsFlyout(); + }); + + after(async () => { + await observability.alerts.closeAlertsFlyout(); + }); + + it('Displays the correct title', async () => { + const titleText = await ( + await observability.alerts.getAlertsFlyoutTitle() + ).getVisibleText(); + expect(titleText).to.contain('Log threshold'); + }); + + it('Displays the correct content', async () => { + const flyoutTitles = await observability.alerts.getAlertsFlyoutDescriptionListTitles(); + const flyoutDescriptions = await observability.alerts.getAlertsFlyoutDescriptionListDescriptions(); + + const expectedTitles = [ + 'Status', + 'Last updated', + 'Duration', + 'Expected value', + 'Actual value', + 'Rule type', + ]; + const expectedDescriptions = [ + 'Active', + 'Sep 2, 2021 @ 12:54:09.674', + '15 minutes', + '100.25', + '1957', + 'Log threshold', + ]; + + await asyncForEach(flyoutTitles, async (title, index) => { + expect(await title.getVisibleText()).to.be(expectedTitles[index]); + }); + + await asyncForEach(flyoutDescriptions, async (description, index) => { + expect(await description.getVisibleText()).to.be(expectedDescriptions[index]); + }); + }); + + it('Displays a View in App button', async () => { + await observability.alerts.getAlertsFlyoutViewInAppButtonOrFail(); + }); + }); }); }); }); diff --git a/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json b/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json index 29c56a751c038..a4392cd88b356 100644 --- a/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json +++ b/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json @@ -386,47 +386,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/dashboard/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/dashboard/mappings.json index 47f23dc57e361..6caac5e31e1f6 100644 --- a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/dashboard/mappings.json +++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/dashboard/mappings.json @@ -22,7 +22,6 @@ "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", "type": "2f4316de49999235636386fe51dc06c1", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", @@ -406,47 +405,6 @@ } } }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "tsvb-validation-telemetry": { "properties": { "failedRequests": { diff --git a/x-pack/test/security_api_integration/fixtures/saml/idp_metadata_never_login.xml b/x-pack/test/security_api_integration/fixtures/saml/idp_metadata_never_login.xml new file mode 100644 index 0000000000000..44b2ede5060ff --- /dev/null +++ b/x-pack/test/security_api_integration/fixtures/saml/idp_metadata_never_login.xml @@ -0,0 +1,41 @@ + + + + + + + + MIIDOTCCAiGgAwIBAgIVANNWkg9lzNiLqNkMFhFKHcXyaZmqMA0GCSqGSIb3DQEB +CwUAMDQxMjAwBgNVBAMTKUVsYXN0aWMgQ2VydGlmaWNhdGUgVG9vbCBBdXRvZ2Vu +ZXJhdGVkIENBMCAXDTE5MTIyNzE3MDM0MloYDzIwNjkxMjE0MTcwMzQyWjARMQ8w +DQYDVQQDEwZraWJhbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCQ +wYYbQtbRBKJ4uNZc2+IgRU+7NNL21ZebQlEIMgK7jAqOMrsW2b5DATz41Fd+GQFU +FUYYjwo+PQj6sJHshOJo/gNb32HrydvMI7YPvevkszkuEGCfXxQ3Dw2RTACLgD0Q +OCkwHvn3TMf0loloV/ePGWaZDYZaXi3a5DdWi/HFFoJysgF0JV2f6XyKhJkGaEfJ +s9pWX269zH/XQvGNx4BEimJpYB8h4JnDYPFIiQdqj+sl2b+kS1hH9kL5gBAMXjFU +vcNnX+PmyTjyJrGo75k0ku+spBf1bMwuQt3uSmM+TQIXkvFDmS0DOVESrpA5EC1T +BUGRz6o/I88Xx4Mud771AgMBAAGjYzBhMB0GA1UdDgQWBBQLB1Eo23M3Ss8MsFaz +V+Twcb3PmDAfBgNVHSMEGDAWgBQa7SYOe8NGcF00EbwPHA91YCsHSTAUBgNVHREE +DTALgglsb2NhbGhvc3QwCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEAnEl/ +z5IElIjvkK4AgMPrNcRlvIGDt2orEik7b6Jsq6/RiJQ7cSsYTZf7xbqyxNsUOTxv ++frj47MEN448H2nRvUxH29YR3XygV5aEwADSAhwaQWn0QfWTCZbJTmSoNEDtDOzX +TGDlAoCD9s9Xz9S1JpxY4H+WWRZrBSDM6SC1c6CzuEeZRuScNAjYD5mh2v6fOlSy +b8xJWSg0AFlJPCa3ZsA2SKbNqI0uNfJTnkXRm88Z2NHcgtlADbOLKauWfCrpgsCk +cZgo6yAYkOM148h/8wGla1eX+iE1R72NUABGydu8MSQKvc0emWJkGsC1/KqPlf/O +eOUsdwn1yDKHRxDHyA== + + + + + + + + + + diff --git a/x-pack/test/security_api_integration/fixtures/saml/saml_provider/server/init_routes.ts b/x-pack/test/security_api_integration/fixtures/saml/saml_provider/server/init_routes.ts index 8d6529ad6213d..6d2a8c7b4b529 100644 --- a/x-pack/test/security_api_integration/fixtures/saml/saml_provider/server/init_routes.ts +++ b/x-pack/test/security_api_integration/fixtures/saml/saml_provider/server/init_routes.ts @@ -55,4 +55,25 @@ export function initRoutes(core: CoreSetup) { return response.redirected({ headers: { location: '/logout?SAMLResponse=something' } }); } ); + + let attemptsCounter = 0; + core.http.resources.register( + { + path: '/saml_provider/never_login', + validate: false, + options: { authRequired: false }, + }, + async (context, request, response) => { + return response.renderHtml({ + body: ` + + Kibana SAML Login + + + Attempt #${++attemptsCounter} + + `, + }); + } + ); } diff --git a/x-pack/test/security_api_integration/tests/login_selector/basic_functionality.ts b/x-pack/test/security_api_integration/tests/login_selector/basic_functionality.ts index 4c6db9ef258bb..0836897bb3f00 100644 --- a/x-pack/test/security_api_integration/tests/login_selector/basic_functionality.ts +++ b/x-pack/test/security_api_integration/tests/login_selector/basic_functionality.ts @@ -117,6 +117,30 @@ export default function ({ getService }: FtrProviderContext) { ); }); + it('preserves auth_provider_hint when accessing Kibana with intermediate authentication cookie', async () => { + const handshakeResponse = await supertest + .post('/internal/security/login') + .ca(CA_CERT) + .set('kbn-xsrf', 'xxx') + .send({ providerType: 'saml', providerName: 'saml1', currentURL: 'https://kibana.com/' }) + .expect(200); + + // The cookie that includes some state of the in-progress authentication, that doesn't allow + // to fully authenticate user yet. + const intermediateAuthCookie = parseCookie(handshakeResponse.headers['set-cookie'][0])!; + + // When user tries to access any other page in Kibana. + const response = await supertest + .get('/abc/xyz/handshake?one=two three&auth_provider_hint=saml1') + .ca(CA_CERT) + .set('Cookie', intermediateAuthCookie.cookieString()) + .expect(302); + expect(response.headers['set-cookie']).to.be(undefined); + expect(response.headers.location).to.be( + '/login?next=%2Fabc%2Fxyz%2Fhandshake%3Fone%3Dtwo%2520three%26auth_provider_hint%3Dsaml1&auth_provider_hint=saml1' + ); + }); + describe('SAML', () => { function createSAMLResponse(options = {}) { return getSAMLResponse({ diff --git a/x-pack/test/security_functional/login_selector.config.ts b/x-pack/test/security_functional/login_selector.config.ts index e30775af7e784..aa145e2ec6216 100644 --- a/x-pack/test/security_functional/login_selector.config.ts +++ b/x-pack/test/security_functional/login_selector.config.ts @@ -25,6 +25,10 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { __dirname, '../security_api_integration/fixtures/saml/saml_provider/metadata.xml' ); + const idpNeverLoginPath = resolve( + __dirname, + '../security_api_integration/fixtures/saml/idp_metadata_never_login.xml' + ); const samlIdPPlugin = resolve( __dirname, '../security_api_integration/fixtures/saml/saml_provider' @@ -53,6 +57,13 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `xpack.security.authc.realms.saml.saml1.sp.logout=http://localhost:${kibanaPort}/logout`, `xpack.security.authc.realms.saml.saml1.sp.acs=http://localhost:${kibanaPort}/api/security/saml/callback`, 'xpack.security.authc.realms.saml.saml1.attributes.principal=urn:oid:0.0.7', + 'xpack.security.authc.realms.saml.saml_never.order=2', + `xpack.security.authc.realms.saml.saml_never.idp.metadata.path=${idpNeverLoginPath}`, + 'xpack.security.authc.realms.saml.saml_never.idp.entity_id=http://www.elastic.co/saml1', + `xpack.security.authc.realms.saml.saml_never.sp.entity_id=http://localhost:${kibanaPort}`, + `xpack.security.authc.realms.saml.saml_never.sp.logout=http://localhost:${kibanaPort}/logout`, + `xpack.security.authc.realms.saml.saml_never.sp.acs=http://localhost:${kibanaPort}/api/security/saml/callback`, + 'xpack.security.authc.realms.saml.saml_never.attributes.principal=urn:oid:0.0.7', ], }, @@ -80,6 +91,12 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { description: 'Do-not-log-in-with-THIS-SAML', icon: 'logoAWS', }, + saml_never: { + order: 4, + realm: 'saml_never', + description: 'Never-log-in-with-SAML', + icon: 'logoKibana', + }, }, anonymous: { anonymous1: { diff --git a/x-pack/test/security_functional/tests/login_selector/auth_provider_hint.ts b/x-pack/test/security_functional/tests/login_selector/auth_provider_hint.ts index b94b93d920da4..b19af792d8af6 100644 --- a/x-pack/test/security_functional/tests/login_selector/auth_provider_hint.ts +++ b/x-pack/test/security_functional/tests/login_selector/auth_provider_hint.ts @@ -14,6 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const security = getService('security'); const deployment = getService('deployment'); + const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['security', 'common']); describe('Authentication provider hint', function () { @@ -88,6 +89,44 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); + it('re-initiates SSO handshake even with unauthenticated session', async () => { + // 1. Try to authenticate with SAML that never completes SAML handshake. In this case we end + // up with the cookie pointing to the intermediate unauthenticated session. + await PageObjects.common.navigateToUrlWithBrowserHistory( + 'management', + '/security/users', + '?auth_provider_hint=saml_never', + { ensureCurrentUrl: false, shouldLoginIfPrompted: false } + ); + await testSubjects.stringExistsInCodeBlockOrFail('idp-page', 'Attempt #1'); + + // 2. Now navigate to the same URL again and make sure we're still automatically redirected to IDP. + await PageObjects.common.navigateToUrlWithBrowserHistory( + 'management', + '/security/users', + '?auth_provider_hint=saml_never', + { ensureCurrentUrl: false, shouldLoginIfPrompted: false } + ); + await testSubjects.stringExistsInCodeBlockOrFail('idp-page', 'Attempt #2'); + + // 3. Finally try another SSO provider. + await PageObjects.common.navigateToUrlWithBrowserHistory( + 'management', + '/security/users', + '?auth_provider_hint=saml1', + { ensureCurrentUrl: false, shouldLoginIfPrompted: false } + ); + + await PageObjects.common.waitUntilUrlIncludes('/app/management/security/users'); + + const currentURL = parse(await browser.getCurrentUrl()); + expect(currentURL.pathname).to.eql('/app/management/security/users'); + expect((await PageObjects.security.getCurrentUser())?.authentication_provider).to.eql({ + type: 'saml', + name: 'saml1', + }); + }); + it('can login anonymously preserving original URL', async () => { await PageObjects.common.navigateToUrlWithBrowserHistory( 'management', diff --git a/x-pack/test/security_solution_cypress/es_archives/empty_kibana/mappings.json b/x-pack/test/security_solution_cypress/es_archives/empty_kibana/mappings.json index 499c994780285..07dc66dd8ce94 100644 --- a/x-pack/test/security_solution_cypress/es_archives/empty_kibana/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/empty_kibana/mappings.json @@ -177,48 +177,6 @@ "spaceId": { "type": "keyword" }, - "timelion-sheet": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, "type": { "type": "keyword" }, diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index 06398fdcd9658..a793582cb7295 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -306,7 +306,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, behavior_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, }, }, @@ -322,7 +322,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, behavior_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, }, }, @@ -348,11 +348,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, memory_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, behavior_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, ransomware: { enabled: true, @@ -530,7 +530,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, behavior_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, }, }, @@ -546,7 +546,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, behavior_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, }, }, @@ -572,11 +572,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, memory_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, behavior_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, ransomware: { enabled: true, @@ -751,7 +751,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, behavior_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, }, }, @@ -767,7 +767,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, behavior_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, }, }, @@ -793,11 +793,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, memory_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, behavior_protection: { enabled: true, - message: 'Elastic Security {action} {filename}', + message: 'Elastic Security {action} {rule}', }, ransomware: { enabled: true, diff --git a/yarn.lock b/yarn.lock index 368ffa0a712ac..0cb3876c0d2ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4913,11 +4913,6 @@ "@types/node" "*" "@types/responselike" "*" -"@types/caseless@*": - version "0.12.2" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" - integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== - "@types/chance@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.0.1.tgz#c10703020369602c40dd9428cc6e1437027116df" @@ -6024,16 +6019,6 @@ dependencies: "@types/prismjs" "*" -"@types/request@^2.48.2": - version "2.48.2" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.2.tgz#936374cbe1179d7ed529fc02543deb4597450fed" - integrity sha512-gP+PSFXAXMrd5PcD7SqHeUjdGshAI8vKQ3+AvpQr3ht9iQea+59LOKvKITcQI+Lg+1EIkDP6AFSBUJPWG8GDyA== - dependencies: - "@types/caseless" "*" - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.0" - "@types/resize-observer-browser@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.5.tgz#36d897708172ac2380cd486da7a3daf1161c1e23" @@ -6921,6 +6906,14 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +ajv@^4.9.1: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.5.5, ajv@^6.9.1: version "6.12.4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" @@ -6990,11 +6983,6 @@ angular-sanitize@^1.8.0: resolved "https://registry.yarnpkg.com/angular-sanitize/-/angular-sanitize-1.8.0.tgz#9f80782d3afeec3bcc0bb92b3ca6f1f421cfbca6" integrity sha512-j5GiOPCvfcDWK5svEOVoPb11X3UDVy/mdHPRWuy14Iyw86xaq+Bb+x/em2sAOa5MQQeY5ciLXbF3RRp8iCKcNg== -angular-sortable-view@^0.0.17: - version "0.0.17" - resolved "https://registry.yarnpkg.com/angular-sortable-view/-/angular-sortable-view-0.0.17.tgz#99e2679951a86b6ee6ff27b099022943c683fb4f" - integrity sha512-2WkhM0Lt/wyMyrX/+7ve9ejSegBd7A4eRBNHEIJz8XMBIOjt+3oM1WpcAm+qNThkmNmmQaDeaYv0TQZw/WDMBw== - angular@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/angular/-/angular-1.8.0.tgz#b1ec179887869215cab6dfd0df2e42caa65b1b51" @@ -7510,6 +7498,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ= + assert@^1.1.1: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" @@ -7740,11 +7733,21 @@ available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: dependencies: array-filter "^1.0.0" +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + integrity sha1-FDQt0428yU0OW4fXY81jYSwOeU8= + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= +aws4@^1.2.1: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + aws4@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" @@ -8500,6 +8503,13 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + integrity sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8= + dependencies: + hoek "2.x.x" + bottleneck@^2.15.3: version "2.18.0" resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.18.0.tgz#41fa63ae185b65435d789d1700334bc48222dacf" @@ -9902,9 +9912,9 @@ combine-source-map@^0.8.0, combine-source-map@~0.8.0: lodash.memoize "~3.0.3" source-map "~0.5.3" -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.5, combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.5, combined-stream@~1.0.6: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" @@ -10479,6 +10489,13 @@ crypt@~0.0.1: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + integrity sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g= + dependencies: + boom "2.x.x" + crypto-browserify@^3.0.0, crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -13427,7 +13444,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@~3.0.2: +extend@^3.0.0, extend@~3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -14093,7 +14110,7 @@ fork-ts-checker-webpack-plugin@4.1.6, fork-ts-checker-webpack-plugin@^4.1.4: tapable "^1.0.0" worker-rpc "^0.1.0" -form-data@^2.3.1, form-data@^2.5.0: +form-data@^2.3.1: version "2.5.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.0.tgz#094ec359dc4b55e7d62e0db4acd76e89fe874d37" integrity sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA== @@ -14120,6 +14137,15 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + integrity sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE= + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -15144,11 +15170,24 @@ handlebars@4.7.7, handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + integrity sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4= + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + integrity sha1-M0gdDxu/9gDdID11gSpqX7oALio= + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + har-validator@~5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" @@ -15408,6 +15447,16 @@ hat@0.0.3: resolved "https://registry.yarnpkg.com/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a" integrity sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo= +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + integrity sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ= + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + hdr-histogram-js@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hdr-histogram-js/-/hdr-histogram-js-1.2.0.tgz#1213c0b317f39b9c05bc4f208cb7931dbbc192ae" @@ -15467,6 +15516,11 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0= + hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.5, hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -15750,6 +15804,15 @@ http-proxy@^1.17.0, http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + integrity sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8= + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -19367,6 +19430,11 @@ mime-db@1.44.0, mime-db@1.x.x, "mime-db@>= 1.40.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== +mime-db@1.45.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" + integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== + mime-types@^2.0.1, mime-types@^2.1.12, mime-types@^2.1.26, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" @@ -19374,6 +19442,13 @@ mime-types@^2.0.1, mime-types@^2.1.12, mime-types@^2.1.26, mime-types@^2.1.27, m dependencies: mime-db "1.44.0" +mime-types@~2.1.7: + version "2.1.28" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" + integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== + dependencies: + mime-db "1.45.0" + mime@1.6.0, mime@^1.2.11, mime@^1.3.4, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -20491,6 +20566,11 @@ nyc@^15.0.1: test-exclude "^6.0.0" yargs "^15.0.2" +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -22448,7 +22528,7 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^1.2.4, punycode@^1.3.2: +punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= @@ -22530,6 +22610,11 @@ qs@^6.7.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM= + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -24189,7 +24274,35 @@ request-promise@^4.2.2: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@2.81.0, request@^2.44.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: +request@2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + integrity sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA= + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +request@^2.44.0, request@^2.87.0, request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -25266,6 +25379,13 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^2.0.0" +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + integrity sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg= + dependencies: + hoek "2.x.x" + sockjs-client@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" @@ -26012,6 +26132,11 @@ stringify-entities@^3.0.1: is-decimal "^1.0.2" is-hexadecimal "^1.0.0" +stringstream@~0.0.4: + version "0.0.6" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" + integrity sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA== + strip-ansi@*, strip-ansi@5.2.0, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -27087,6 +27212,13 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tough-cookie@~2.3.0: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" + integrity sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA== + dependencies: + punycode "^1.4.1" + tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" @@ -28138,7 +28270,7 @@ uuid@^2.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" integrity sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho= -uuid@^3.3.2, uuid@^3.3.3, uuid@^3.4.0: +uuid@^3.0.0, uuid@^3.3.2, uuid@^3.3.3, uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==