diff --git a/.backportrc.json b/.backportrc.json index 3f1d639e9a480..a97c82ca0efa9 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -3,6 +3,7 @@ "targetBranchChoices": [ { "name": "master", "checked": true }, { "name": "7.x", "checked": true }, + "7.10", "7.9", "7.8", "7.7", @@ -27,7 +28,7 @@ "targetPRLabels": ["backport"], "branchLabelMapping": { "^v8.0.0$": "master", - "^v7.10.0$": "7.x", + "^v7.11.0$": "7.x", "^v(\\d+).(\\d+).\\d+$": "$1.$2" } } diff --git a/NOTICE.txt b/NOTICE.txt index 0504b7f7d6db2..24940e232e88f 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -118,6 +118,212 @@ THE SOFTWARE. This product uses Noto fonts that are licensed under the SIL Open Font License, Version 1.1. +--- +We include the `firstValueFrom()` and `lastValueFrom()` helpers +extracted from the v7-beta.7 version of the RxJS library. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + --- Based on the scroll-into-view-if-necessary module from npm https://github.com/stipsan/compute-scroll-into-view/blob/master/src/index.ts#L269-L340 diff --git a/docs/api/saved-objects/bulk_create.asciidoc b/docs/api/saved-objects/bulk_create.asciidoc index 5149cef3d30c6..267ab3891d700 100644 --- a/docs/api/saved-objects/bulk_create.asciidoc +++ b/docs/api/saved-objects/bulk_create.asciidoc @@ -41,9 +41,10 @@ experimental[] Create multiple {kib} saved objects. `references`:: (Optional, array) Objects with `name`, `id`, and `type` properties that describe the other saved objects in the referenced object. To refer to the other saved object, use `name` in the attributes. Never use `id` to refer to the other saved object. `id` can be automatically updated during migrations, import, or export. -`namespaces`:: - (Optional, string array) Identifiers for the <> in which this object should be created. If this is not provided, the - object will be created in the current space. +`initialNamespaces`:: + (Optional, string array) Identifiers for the <> in which this object is created. If this is provided, the + object is created only in the explicitly defined spaces. If this is not provided, the object is created in the current space + (default behavior). `version`:: (Optional, number) Specifies the version. diff --git a/docs/api/saved-objects/create.asciidoc b/docs/api/saved-objects/create.asciidoc index c8cd9c8bfca27..50809a1bd5d4e 100644 --- a/docs/api/saved-objects/create.asciidoc +++ b/docs/api/saved-objects/create.asciidoc @@ -46,9 +46,10 @@ any data that you send to the API is properly formed. `references`:: (Optional, array) Objects with `name`, `id`, and `type` properties that describe the other saved objects that this object references. Use `name` in attributes to refer to the other saved object, but never the `id`, which can update automatically during migrations or import/export. -`namespaces`:: - (Optional, string array) Identifiers for the <> in which this object should be created. If this is not provided, the - object will be created in the current space. +`initialNamespaces`:: + (Optional, string array) Identifiers for the <> in which this object is created. If this is provided, the + object is created only in the explicitly defined spaces. If this is not provided, the object is created in the current space + (default behavior). [[saved-objects-api-create-request-codes]] ==== Response code diff --git a/docs/canvas/canvas-share-workpad.asciidoc b/docs/canvas/canvas-share-workpad.asciidoc index 4887eb6ca870d..f49e2a944c900 100644 --- a/docs/canvas/canvas-share-workpad.asciidoc +++ b/docs/canvas/canvas-share-workpad.asciidoc @@ -10,7 +10,7 @@ When you've finished your workpad, you can share it outside of {kib}. Create a JSON file of your workpad that you can export outside of {kib}. -Click *Share > Download as JSON*. +To begin, click *Share > Download as JSON*. [role="screenshot"] image::images/canvas-export-workpad.png[Export single workpad through JSON, from Share dropdown] @@ -23,7 +23,7 @@ Want to export multiple workpads? Go to the *Canvas* home page, select the workp If you have a subscription that supports the {report-features}, you can create a PDF copy of your workpad that you can save and share outside {kib}. -Click *Share > PDF reports > Generate PDF*. +To begin, click *Share > PDF reports > Generate PDF*. [role="screenshot"] image::images/canvas-generate-pdf.gif[Image showing how to generate a PDF] @@ -36,7 +36,7 @@ For more information, refer to <> or a script. -Click *Share > PDF reports > Copy POST URL*. +To begin, click *Share > PDF reports > Copy POST URL*. [role="screenshot"] image::images/canvas-create-URL.gif[Image showing how to create POST URL] diff --git a/docs/canvas/canvas-tutorial.asciidoc b/docs/canvas/canvas-tutorial.asciidoc index ea4d2c8cc6a83..312391541a777 100644 --- a/docs/canvas/canvas-tutorial.asciidoc +++ b/docs/canvas/canvas-tutorial.asciidoc @@ -2,7 +2,7 @@ [[canvas-tutorial]] == Tutorial: Create a workpad for monitoring sales -To get up and running with Canvas, use the following tutorial where you'll create a workpad for monitoring sales at an eCommerce store. +To get up and running with Canvas, add the Sample eCommerce orders data, then use the data to create a workpad for monitoring sales at an eCommerce store. [float] === Before you begin @@ -114,18 +114,16 @@ image::images/canvas-timefilter-element.png[Image showing Canvas workpad with fi To see how the data changes, set the time filter to *Last 7 days*. As you change the time filter options, the elements automatically update. -Your workpad is now complete! +Your workpad is complete! [float] -=== Next steps +=== What's next? Now that you know the Canvas basics, you're ready to explore on your own. Here are some things to try: * Play with the {kibana-ref}/add-sample-data.html[sample Canvas workpads]. -* Build presentations of your own live data with <>. - -* Learn more about <> — the building blocks of your workpad. +* Build presentations of your own data with <>. * Deep dive into the {kibana-ref}/canvas-function-reference.html[expression language and functions] that drive Canvas. diff --git a/docs/canvas/images/canvas-autoplay-interval.png b/docs/canvas/images/canvas-autoplay-interval.png index 68a7ca248d9ee..a7b1251efc808 100644 Binary files a/docs/canvas/images/canvas-autoplay-interval.png and b/docs/canvas/images/canvas-autoplay-interval.png differ diff --git a/docs/canvas/images/canvas-gs-example.png b/docs/canvas/images/canvas-gs-example.png index 90beccd322aa4..a9b960342709f 100644 Binary files a/docs/canvas/images/canvas-gs-example.png and b/docs/canvas/images/canvas-gs-example.png differ diff --git a/docs/canvas/images/canvas-refresh-interval.png b/docs/canvas/images/canvas-refresh-interval.png index 62e88ad4bf7d0..c097d950a7ec7 100644 Binary files a/docs/canvas/images/canvas-refresh-interval.png and b/docs/canvas/images/canvas-refresh-interval.png differ diff --git a/docs/canvas/images/canvas-zoom-controls.png b/docs/canvas/images/canvas-zoom-controls.png index 5c72d118041e4..1407ca3cd8627 100644 Binary files a/docs/canvas/images/canvas-zoom-controls.png and b/docs/canvas/images/canvas-zoom-controls.png differ diff --git a/docs/developer/contributing/development-documentation.asciidoc b/docs/developer/contributing/development-documentation.asciidoc index 99e55963f57af..70dd756ca808e 100644 --- a/docs/developer/contributing/development-documentation.asciidoc +++ b/docs/developer/contributing/development-documentation.asciidoc @@ -26,6 +26,13 @@ README for getting the docs tooling set up. ```bash node scripts/docs.js --open ``` +[discrete] +==== REST APIs + +REST APIs should be documented using the following recommended formats: + +* https://raw.githubusercontent.com/elastic/docs/master/shared/api-ref-ex.asciidoc[API doc templaate] +* https://raw.githubusercontent.com/elastic/docs/master/shared/api-definitions-ex.asciidoc[API object definition template] [discrete] === General developer documentation and guidelines diff --git a/docs/developer/contributing/index.asciidoc b/docs/developer/contributing/index.asciidoc index ecb37ffe9c97b..ba4ab89d17c27 100644 --- a/docs/developer/contributing/index.asciidoc +++ b/docs/developer/contributing/index.asciidoc @@ -49,7 +49,7 @@ The Release Notes summarize what the PRs accomplish in language that is meaningf The text that appears in the Release Notes is pulled directly from your PR title, or a single paragraph of text that you specify in the PR description. -To use a single paragraph of text, enter `Release note:` or a `## Release note` header in the PR description, followed by your text. For example, refer to this https://github.com/elastic/kibana/pull/65796[PR] that uses the `## Release note` header. +To use a single paragraph of text, enter a `Release note:` or `## Release note` header in the PR description ("dev docs" works too), followed by your text. For example, refer to this https://github.com/elastic/kibana/pull/65796[PR] that uses the `## Release note` header. When you create the Release Notes text, use the following best practices: diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.initialnamespaces.md similarity index 68% rename from docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md rename to docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.initialnamespaces.md index 7db1c53c67b52..3db8bbadfbd6b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.initialnamespaces.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsBulkCreateObject](./kibana-plugin-core-server.savedobjectsbulkcreateobject.md) > [namespaces](./kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md) +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsBulkCreateObject](./kibana-plugin-core-server.savedobjectsbulkcreateobject.md) > [initialNamespaces](./kibana-plugin-core-server.savedobjectsbulkcreateobject.initialnamespaces.md) -## SavedObjectsBulkCreateObject.namespaces property +## SavedObjectsBulkCreateObject.initialNamespaces property Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md). @@ -11,5 +11,5 @@ Note: this can only be used for multi-namespace object types. Signature: ```typescript -namespaces?: string[]; +initialNamespaces?: string[]; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md index aabbfeeff75af..5ac5f6d9807bd 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md @@ -17,8 +17,8 @@ export interface SavedObjectsBulkCreateObject | --- | --- | --- | | [attributes](./kibana-plugin-core-server.savedobjectsbulkcreateobject.attributes.md) | T | | | [id](./kibana-plugin-core-server.savedobjectsbulkcreateobject.id.md) | string | | +| [initialNamespaces](./kibana-plugin-core-server.savedobjectsbulkcreateobject.initialnamespaces.md) | string[] | Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md).Note: this can only be used for multi-namespace object types. | | [migrationVersion](./kibana-plugin-core-server.savedobjectsbulkcreateobject.migrationversion.md) | SavedObjectsMigrationVersion | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | -| [namespaces](./kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md) | string[] | Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md).Note: this can only be used for multi-namespace object types. | | [originId](./kibana-plugin-core-server.savedobjectsbulkcreateobject.originid.md) | string | Optional ID of the original saved object, if this object's id was regenerated | | [references](./kibana-plugin-core-server.savedobjectsbulkcreateobject.references.md) | SavedObjectReference[] | | | [type](./kibana-plugin-core-server.savedobjectsbulkcreateobject.type.md) | string | | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.initialnamespaces.md similarity index 69% rename from docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md rename to docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.initialnamespaces.md index 67804999dfd44..262b0997cb905 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.initialnamespaces.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md) > [namespaces](./kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md) +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md) > [initialNamespaces](./kibana-plugin-core-server.savedobjectscreateoptions.initialnamespaces.md) -## SavedObjectsCreateOptions.namespaces property +## SavedObjectsCreateOptions.initialNamespaces property Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md). @@ -11,5 +11,5 @@ Note: this can only be used for multi-namespace object types. Signature: ```typescript -namespaces?: string[]; +initialNamespaces?: string[]; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md index 63aebf6c5e791..e6d306784f8ae 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md @@ -16,8 +16,8 @@ export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions | Property | Type | Description | | --- | --- | --- | | [id](./kibana-plugin-core-server.savedobjectscreateoptions.id.md) | string | (not recommended) Specify an id for the document | +| [initialNamespaces](./kibana-plugin-core-server.savedobjectscreateoptions.initialnamespaces.md) | string[] | Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md).Note: this can only be used for multi-namespace object types. | | [migrationVersion](./kibana-plugin-core-server.savedobjectscreateoptions.migrationversion.md) | SavedObjectsMigrationVersion | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | -| [namespaces](./kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md) | string[] | Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md).Note: this can only be used for multi-namespace object types. | | [originId](./kibana-plugin-core-server.savedobjectscreateoptions.originid.md) | string | Optional ID of the original saved object, if this object's id was regenerated | | [overwrite](./kibana-plugin-core-server.savedobjectscreateoptions.overwrite.md) | boolean | Overwrite existing documents (defaults to false) | | [references](./kibana-plugin-core-server.savedobjectscreateoptions.references.md) | SavedObjectReference[] | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternselectprops.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternselectprops.md index 1fe551def29ba..5cfd5e1bc9929 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternselectprops.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternselectprops.md @@ -7,10 +7,10 @@ Signature: ```typescript -export declare type IndexPatternSelectProps = Required, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions'>, 'onChange' | 'placeholder'> & { +export declare type IndexPatternSelectProps = Required, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions' | 'onChange'>, 'placeholder'> & { + onChange: (indexPatternId?: string) => void; indexPatternId: string; fieldTypes?: string[]; onNoIndexPatterns?: () => void; - savedObjectsClient: SavedObjectsClientContract; }; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.start.md index d35dc3aa11000..e7c331bad64e8 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.start.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.start.md @@ -8,7 +8,7 @@ ```typescript start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps): { - indexPatternsServiceFactory: (kibanaRequest: KibanaRequest) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract) => Promise; }; ``` @@ -22,6 +22,6 @@ start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps): Returns: `{ - indexPatternsServiceFactory: (kibanaRequest: KibanaRequest) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract) => Promise; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md index e44cb5c657747..215eac9829451 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md @@ -12,7 +12,7 @@ start(core: CoreStart): { fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick) => Promise; }; search: ISearchStart>; }; @@ -31,7 +31,7 @@ start(core: CoreStart): { fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick) => Promise; }; search: ISearchStart>; }` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attribute_service_key.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attribute_service_key.md new file mode 100644 index 0000000000000..9504d50cf92d3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attribute_service_key.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [ATTRIBUTE\_SERVICE\_KEY](./kibana-plugin-plugins-embeddable-public.attribute_service_key.md) + +## ATTRIBUTE\_SERVICE\_KEY variable + +The attribute service is a shared, generic service that embeddables can use to provide the functionality required to fulfill the requirements of the ReferenceOrValueEmbeddable interface. The attribute\_service can also be used as a higher level wrapper to transform an embeddable input shape that references a saved object into an embeddable input shape that contains that saved object's attributes by value. + +Signature: + +```typescript +ATTRIBUTE_SERVICE_KEY = "attributes" +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice._constructor_.md new file mode 100644 index 0000000000000..930250be2018b --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice._constructor_.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AttributeService](./kibana-plugin-plugins-embeddable-public.attributeservice.md) > [(constructor)](./kibana-plugin-plugins-embeddable-public.attributeservice._constructor_.md) + +## AttributeService.(constructor) + +Constructs a new instance of the `AttributeService` class + +Signature: + +```typescript +constructor(type: string, showSaveModal: (saveModal: React.ReactElement, I18nContext: I18nStart['Context']) => void, i18nContext: I18nStart['Context'], toasts: NotificationsStart['toasts'], options: AttributeServiceOptions, getEmbeddableFactory?: (embeddableFactoryId: string) => EmbeddableFactory); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | | +| showSaveModal | (saveModal: React.ReactElement, I18nContext: I18nStart['Context']) => void | | +| i18nContext | I18nStart['Context'] | | +| toasts | NotificationsStart['toasts'] | | +| options | AttributeServiceOptions<SavedObjectAttributes> | | +| getEmbeddableFactory | (embeddableFactoryId: string) => EmbeddableFactory | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.getexplicitinputfromembeddable.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.getexplicitinputfromembeddable.md new file mode 100644 index 0000000000000..e3f27723e1a70 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.getexplicitinputfromembeddable.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AttributeService](./kibana-plugin-plugins-embeddable-public.attributeservice.md) > [getExplicitInputFromEmbeddable](./kibana-plugin-plugins-embeddable-public.attributeservice.getexplicitinputfromembeddable.md) + +## AttributeService.getExplicitInputFromEmbeddable() method + +Signature: + +```typescript +getExplicitInputFromEmbeddable(embeddable: IEmbeddable): ValType | RefType; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| embeddable | IEmbeddable | | + +Returns: + +`ValType | RefType` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.getinputasreftype.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.getinputasreftype.md new file mode 100644 index 0000000000000..7908327c594d8 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.getinputasreftype.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AttributeService](./kibana-plugin-plugins-embeddable-public.attributeservice.md) > [getInputAsRefType](./kibana-plugin-plugins-embeddable-public.attributeservice.getinputasreftype.md) + +## AttributeService.getInputAsRefType property + +Signature: + +```typescript +getInputAsRefType: (input: ValType | RefType, saveOptions?: { + showSaveModal: boolean; + saveModalTitle?: string | undefined; + } | { + title: string; + } | undefined) => Promise; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.getinputasvaluetype.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.getinputasvaluetype.md new file mode 100644 index 0000000000000..939194575cbb7 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.getinputasvaluetype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AttributeService](./kibana-plugin-plugins-embeddable-public.attributeservice.md) > [getInputAsValueType](./kibana-plugin-plugins-embeddable-public.attributeservice.getinputasvaluetype.md) + +## AttributeService.getInputAsValueType property + +Signature: + +```typescript +getInputAsValueType: (input: ValType | RefType) => Promise; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.inputisreftype.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.inputisreftype.md new file mode 100644 index 0000000000000..c17ad97c3eeed --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.inputisreftype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AttributeService](./kibana-plugin-plugins-embeddable-public.attributeservice.md) > [inputIsRefType](./kibana-plugin-plugins-embeddable-public.attributeservice.inputisreftype.md) + +## AttributeService.inputIsRefType property + +Signature: + +```typescript +inputIsRefType: (input: ValType | RefType) => input is RefType; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.md new file mode 100644 index 0000000000000..b63516c909d3c --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.md @@ -0,0 +1,40 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AttributeService](./kibana-plugin-plugins-embeddable-public.attributeservice.md) + +## AttributeService class + +Signature: + +```typescript +export declare class AttributeService +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(type, showSaveModal, i18nContext, toasts, options, getEmbeddableFactory)](./kibana-plugin-plugins-embeddable-public.attributeservice._constructor_.md) | | Constructs a new instance of the AttributeService class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [getInputAsRefType](./kibana-plugin-plugins-embeddable-public.attributeservice.getinputasreftype.md) | | (input: ValType | RefType, saveOptions?: {
showSaveModal: boolean;
saveModalTitle?: string | undefined;
} | {
title: string;
} | undefined) => Promise<RefType> | | +| [getInputAsValueType](./kibana-plugin-plugins-embeddable-public.attributeservice.getinputasvaluetype.md) | | (input: ValType | RefType) => Promise<ValType> | | +| [inputIsRefType](./kibana-plugin-plugins-embeddable-public.attributeservice.inputisreftype.md) | | (input: ValType | RefType) => input is RefType | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [getExplicitInputFromEmbeddable(embeddable)](./kibana-plugin-plugins-embeddable-public.attributeservice.getexplicitinputfromembeddable.md) | | | +| [unwrapAttributes(input)](./kibana-plugin-plugins-embeddable-public.attributeservice.unwrapattributes.md) | | | +| [wrapAttributes(newAttributes, useRefType, input)](./kibana-plugin-plugins-embeddable-public.attributeservice.wrapattributes.md) | | | + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.unwrapattributes.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.unwrapattributes.md new file mode 100644 index 0000000000000..f08736a2240a3 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.unwrapattributes.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AttributeService](./kibana-plugin-plugins-embeddable-public.attributeservice.md) > [unwrapAttributes](./kibana-plugin-plugins-embeddable-public.attributeservice.unwrapattributes.md) + +## AttributeService.unwrapAttributes() method + +Signature: + +```typescript +unwrapAttributes(input: RefType | ValType): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | RefType | ValType | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.wrapattributes.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.wrapattributes.md new file mode 100644 index 0000000000000..e22a2ec3faeb4 --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.attributeservice.wrapattributes.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [AttributeService](./kibana-plugin-plugins-embeddable-public.attributeservice.md) > [wrapAttributes](./kibana-plugin-plugins-embeddable-public.attributeservice.wrapattributes.md) + +## AttributeService.wrapAttributes() method + +Signature: + +```typescript +wrapAttributes(newAttributes: SavedObjectAttributes, useRefType: boolean, input?: ValType | RefType): Promise>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| newAttributes | SavedObjectAttributes | | +| useRefType | boolean | | +| input | ValType | RefType | | + +Returns: + +`Promise>` + diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getattributeservice.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getattributeservice.md new file mode 100644 index 0000000000000..ca75b756d199e --- /dev/null +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.getattributeservice.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [EmbeddableStart](./kibana-plugin-plugins-embeddable-public.embeddablestart.md) > [getAttributeService](./kibana-plugin-plugins-embeddable-public.embeddablestart.getattributeservice.md) + +## EmbeddableStart.getAttributeService property + +Signature: + +```typescript +getAttributeService: (type: string, options: AttributeServiceOptions) => AttributeService; +``` diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md index f8e0028d8344b..541575566d3f7 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddablestart.md @@ -15,6 +15,7 @@ export interface EmbeddableStart extends PersistableState | Property | Type | Description | | --- | --- | --- | | [EmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablestart.embeddablepanel.md) | EmbeddablePanelHOC | | +| [getAttributeService](./kibana-plugin-plugins-embeddable-public.embeddablestart.getattributeservice.md) | <A extends {
title: string;
}, V extends EmbeddableInput & {
[ATTRIBUTE_SERVICE_KEY]: A;
} = EmbeddableInput & {
[ATTRIBUTE_SERVICE_KEY]: A;
}, R extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput>(type: string, options: AttributeServiceOptions<A>) => AttributeService<A, V, R> | | | [getEmbeddableFactories](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactories.md) | () => IterableIterator<EmbeddableFactory> | | | [getEmbeddableFactory](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablefactory.md) | <I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, E extends IEmbeddable<I, O> = IEmbeddable<I, O>>(embeddableFactoryId: string) => EmbeddableFactory<I, O, E> | undefined | | | [getEmbeddablePanel](./kibana-plugin-plugins-embeddable-public.embeddablestart.getembeddablepanel.md) | (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC | | diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md index 64dfdd1c6dc22..df67eda5074b9 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.md @@ -9,6 +9,7 @@ | Class | Description | | --- | --- | | [AddPanelAction](./kibana-plugin-plugins-embeddable-public.addpanelaction.md) | | +| [AttributeService](./kibana-plugin-plugins-embeddable-public.attributeservice.md) | | | [Container](./kibana-plugin-plugins-embeddable-public.container.md) | | | [EditPanelAction](./kibana-plugin-plugins-embeddable-public.editpanelaction.md) | | | [Embeddable](./kibana-plugin-plugins-embeddable-public.embeddable.md) | | @@ -71,6 +72,7 @@ | --- | --- | | [ACTION\_ADD\_PANEL](./kibana-plugin-plugins-embeddable-public.action_add_panel.md) | | | [ACTION\_EDIT\_PANEL](./kibana-plugin-plugins-embeddable-public.action_edit_panel.md) | | +| [ATTRIBUTE\_SERVICE\_KEY](./kibana-plugin-plugins-embeddable-public.attribute_service_key.md) | The attribute service is a shared, generic service that embeddables can use to provide the functionality required to fulfill the requirements of the ReferenceOrValueEmbeddable interface. The attribute\_service can also be used as a higher level wrapper to transform an embeddable input shape that references a saved object into an embeddable input shape that contains that saved object's attributes by value. | | [CONTEXT\_MENU\_TRIGGER](./kibana-plugin-plugins-embeddable-public.context_menu_trigger.md) | | | [contextMenuTrigger](./kibana-plugin-plugins-embeddable-public.contextmenutrigger.md) | | | [defaultEmbeddableFactoryProvider](./kibana-plugin-plugins-embeddable-public.defaultembeddablefactoryprovider.md) | | diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.getattributeservice.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.getattributeservice.md new file mode 100644 index 0000000000000..9cd77ca6e3a36 --- /dev/null +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.getattributeservice.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [EmbeddableSetup](./kibana-plugin-plugins-embeddable-server.embeddablesetup.md) > [getAttributeService](./kibana-plugin-plugins-embeddable-server.embeddablesetup.getattributeservice.md) + +## EmbeddableSetup.getAttributeService property + +Signature: + +```typescript +getAttributeService: any; +``` diff --git a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.md b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.md index 59ca4a2bbca75..bd024095e80be 100644 --- a/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.md +++ b/docs/development/plugins/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablesetup.md @@ -14,6 +14,7 @@ export interface EmbeddableSetup | Property | Type | Description | | --- | --- | --- | +| [getAttributeService](./kibana-plugin-plugins-embeddable-server.embeddablesetup.getattributeservice.md) | any | | | [registerEmbeddableFactory](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerembeddablefactory.md) | (factory: EmbeddableRegistryDefinition) => void | | | [registerEnhancement](./kibana-plugin-plugins-embeddable-server.embeddablesetup.registerenhancement.md) | (enhancement: EnhancementRegistryDefinition) => void | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.extract.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.extract.md new file mode 100644 index 0000000000000..6f30bd49013b0 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.extract.md @@ -0,0 +1,28 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [extract](./kibana-plugin-plugins-expressions-public.executor.extract.md) + +## Executor.extract() method + +Signature: + +```typescript +extract(ast: ExpressionAstExpression): { + state: ExpressionAstExpression; + references: SavedObjectReference[]; + }; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstExpression | | + +Returns: + +`{ + state: ExpressionAstExpression; + references: SavedObjectReference[]; + }` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.inject.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.inject.md new file mode 100644 index 0000000000000..8f5a8a3e06724 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.inject.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [inject](./kibana-plugin-plugins-expressions-public.executor.inject.md) + +## Executor.inject() method + +Signature: + +```typescript +inject(ast: ExpressionAstExpression, references: SavedObjectReference[]): ExpressionAstExpression; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstExpression | | +| references | SavedObjectReference[] | | + +Returns: + +`ExpressionAstExpression` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md index b71c8c79c068f..2f96ad6e040bd 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md @@ -7,7 +7,7 @@ Signature: ```typescript -export declare class Executor = Record> +export declare class Executor = Record> implements PersistableState ``` ## Constructors @@ -32,12 +32,15 @@ export declare class Executor = Recordstatic | | | [extendContext(extraContext)](./kibana-plugin-plugins-expressions-public.executor.extendcontext.md) | | | +| [extract(ast)](./kibana-plugin-plugins-expressions-public.executor.extract.md) | | | | [fork()](./kibana-plugin-plugins-expressions-public.executor.fork.md) | | | | [getFunction(name)](./kibana-plugin-plugins-expressions-public.executor.getfunction.md) | | | | [getFunctions()](./kibana-plugin-plugins-expressions-public.executor.getfunctions.md) | | | | [getType(name)](./kibana-plugin-plugins-expressions-public.executor.gettype.md) | | | | [getTypes()](./kibana-plugin-plugins-expressions-public.executor.gettypes.md) | | | +| [inject(ast, references)](./kibana-plugin-plugins-expressions-public.executor.inject.md) | | | | [registerFunction(functionDefinition)](./kibana-plugin-plugins-expressions-public.executor.registerfunction.md) | | | | [registerType(typeDefinition)](./kibana-plugin-plugins-expressions-public.executor.registertype.md) | | | | [run(ast, input, context)](./kibana-plugin-plugins-expressions-public.executor.run.md) | | Execute expression and return result. | +| [telemetry(ast, telemetryData)](./kibana-plugin-plugins-expressions-public.executor.telemetry.md) | | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.telemetry.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.telemetry.md new file mode 100644 index 0000000000000..de4c640f4fe97 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.telemetry.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [telemetry](./kibana-plugin-plugins-expressions-public.executor.telemetry.md) + +## Executor.telemetry() method + +Signature: + +```typescript +telemetry(ast: ExpressionAstExpression, telemetryData: Record): Record; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstExpression | | +| telemetryData | Record<string, any> | | + +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md deleted file mode 100644 index b50ac83036ffe..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-public.expressionastexpression.md) > [chain](./kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md) - -## ExpressionAstExpression.chain property - -Signature: - -```typescript -chain: ExpressionAstFunction[]; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.md index 537659c51dce8..623c49bf08cdd 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.md @@ -2,18 +2,13 @@ [Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-public.expressionastexpression.md) -## ExpressionAstExpression interface +## ExpressionAstExpression type Signature: ```typescript -export interface ExpressionAstExpression +export declare type ExpressionAstExpression = { + type: 'expression'; + chain: ExpressionAstFunction[]; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [chain](./kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md) | ExpressionAstFunction[] | | -| [type](./kibana-plugin-plugins-expressions-public.expressionastexpression.type.md) | 'expression' | | - diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.type.md deleted file mode 100644 index 34a86e235a911..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-public.expressionastexpression.md) > [type](./kibana-plugin-plugins-expressions-public.expressionastexpression.type.md) - -## ExpressionAstExpression.type property - -Signature: - -```typescript -type: 'expression'; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md deleted file mode 100644 index 72b44e8319542..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) > [arguments](./kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md) - -## ExpressionAstFunction.arguments property - -Signature: - -```typescript -arguments: Record; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md deleted file mode 100644 index 36101a110979a..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) > [debug](./kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md) - -## ExpressionAstFunction.debug property - -Debug information added to each function when expression is executed in \*debug mode\*. - -Signature: - -```typescript -debug?: ExpressionAstFunctionDebug; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.function.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.function.md deleted file mode 100644 index 1840fff4b625f..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.function.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) > [function](./kibana-plugin-plugins-expressions-public.expressionastfunction.function.md) - -## ExpressionAstFunction.function property - -Signature: - -```typescript -function: string; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.md index 1004e58759806..d21f2c1750161 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.md @@ -2,20 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) -## ExpressionAstFunction interface +## ExpressionAstFunction type Signature: ```typescript -export interface ExpressionAstFunction +export declare type ExpressionAstFunction = { + type: 'function'; + function: string; + arguments: Record; + debug?: ExpressionAstFunctionDebug; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [arguments](./kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md) | Record<string, ExpressionAstArgument[]> | | -| [debug](./kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md) | ExpressionAstFunctionDebug | Debug information added to each function when expression is executed in \*debug mode\*. | -| [function](./kibana-plugin-plugins-expressions-public.expressionastfunction.function.md) | string | | -| [type](./kibana-plugin-plugins-expressions-public.expressionastfunction.type.md) | 'function' | | - diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.disabled.md similarity index 52% rename from docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.type.md rename to docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.disabled.md index f7f8786430191..f07d5b3b36d04 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.type.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.disabled.md @@ -1,11 +1,11 @@ -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) > [type](./kibana-plugin-plugins-expressions-public.expressionastfunction.type.md) +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [disabled](./kibana-plugin-plugins-expressions-public.expressionfunction.disabled.md) -## ExpressionAstFunction.type property +## ExpressionFunction.disabled property Signature: ```typescript -type: 'function'; +disabled: boolean; ``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.extract.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.extract.md new file mode 100644 index 0000000000000..c5d726849cdc2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.extract.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [extract](./kibana-plugin-plugins-expressions-public.expressionfunction.extract.md) + +## ExpressionFunction.extract property + +Signature: + +```typescript +extract: (state: ExpressionAstFunction['arguments']) => { + state: ExpressionAstFunction['arguments']; + references: SavedObjectReference[]; + }; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.inject.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.inject.md new file mode 100644 index 0000000000000..6f27a6fbab96a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.inject.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [inject](./kibana-plugin-plugins-expressions-public.expressionfunction.inject.md) + +## ExpressionFunction.inject property + +Signature: + +```typescript +inject: (state: ExpressionAstFunction['arguments'], references: SavedObjectReference[]) => ExpressionAstFunction['arguments']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.md index 5ca67e40c93ec..1815d63d804b1 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.md @@ -7,7 +7,7 @@ Signature: ```typescript -export declare class ExpressionFunction +export declare class ExpressionFunction implements PersistableState ``` ## Constructors @@ -23,9 +23,13 @@ export declare class ExpressionFunction | [accepts](./kibana-plugin-plugins-expressions-public.expressionfunction.accepts.md) | | (type: string) => boolean | | | [aliases](./kibana-plugin-plugins-expressions-public.expressionfunction.aliases.md) | | string[] | Aliases that can be used instead of name. | | [args](./kibana-plugin-plugins-expressions-public.expressionfunction.args.md) | | Record<string, ExpressionFunctionParameter> | Specification of expression function parameters. | +| [disabled](./kibana-plugin-plugins-expressions-public.expressionfunction.disabled.md) | | boolean | | +| [extract](./kibana-plugin-plugins-expressions-public.expressionfunction.extract.md) | | (state: ExpressionAstFunction['arguments']) => {
state: ExpressionAstFunction['arguments'];
references: SavedObjectReference[];
} | | | [fn](./kibana-plugin-plugins-expressions-public.expressionfunction.fn.md) | | (input: ExpressionValue, params: Record<string, any>, handlers: object) => ExpressionValue | Function to run function (context, args) | | [help](./kibana-plugin-plugins-expressions-public.expressionfunction.help.md) | | string | A short help text. | +| [inject](./kibana-plugin-plugins-expressions-public.expressionfunction.inject.md) | | (state: ExpressionAstFunction['arguments'], references: SavedObjectReference[]) => ExpressionAstFunction['arguments'] | | | [inputTypes](./kibana-plugin-plugins-expressions-public.expressionfunction.inputtypes.md) | | string[] | undefined | Type of inputs that this function supports. | | [name](./kibana-plugin-plugins-expressions-public.expressionfunction.name.md) | | string | Name of function | +| [telemetry](./kibana-plugin-plugins-expressions-public.expressionfunction.telemetry.md) | | (state: ExpressionAstFunction['arguments'], telemetryData: Record<string, any>) => Record<string, any> | | | [type](./kibana-plugin-plugins-expressions-public.expressionfunction.type.md) | | string | Return type of function. This SHOULD be supplied. We use it for UI and autocomplete hinting. We may also use it for optimizations in the future. | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.telemetry.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.telemetry.md new file mode 100644 index 0000000000000..249c99f50fc7b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.telemetry.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [telemetry](./kibana-plugin-plugins-expressions-public.expressionfunction.telemetry.md) + +## ExpressionFunction.telemetry property + +Signature: + +```typescript +telemetry: (state: ExpressionAstFunction['arguments'], telemetryData: Record) => Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.disabled.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.disabled.md new file mode 100644 index 0000000000000..e6aefd17fceb2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.disabled.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) > [disabled](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.disabled.md) + +## ExpressionFunctionDefinition.disabled property + +if set to true function will be disabled (but its migrate function will still be available) + +Signature: + +```typescript +disabled?: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md index bc801542f81ac..449cc66cb3335 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md @@ -9,7 +9,7 @@ Signature: ```typescript -export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> +export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> extends PersistableStateDefinition ``` ## Properties @@ -19,6 +19,7 @@ export interface ExpressionFunctionDefinitionstring[] | What is this? | | [args](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.args.md) | {
[key in keyof Arguments]: ArgumentType<Arguments[key]>;
} | Specification of arguments that function supports. This list will also be used for autocomplete functionality when your function is being edited. | | [context](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.context.md) | {
types: AnyExpressionFunctionDefinition['inputTypes'];
} | | +| [disabled](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.disabled.md) | boolean | if set to true function will be disabled (but its migrate function will still be available) | | [help](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.help.md) | string | Help text displayed in the Expression editor. This text should be internationalized. | | [inputTypes](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.inputtypes.md) | Array<TypeToString<Input>> | List of allowed type names for input value of this function. If this property is set the input of function will be cast to the first possible type in this list. If this property is missing the input will be provided to the function as-is. | | [name](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.name.md) | Name | The name of the function, as will be used in expression. | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.extract.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.extract.md new file mode 100644 index 0000000000000..90f1f59c90dea --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.extract.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [extract](./kibana-plugin-plugins-expressions-public.expressionsservice.extract.md) + +## ExpressionsService.extract property + +Extracts saved object references from expression AST + +Signature: + +```typescript +readonly extract: (state: ExpressionAstExpression) => { + state: ExpressionAstExpression; + references: SavedObjectReference[]; + }; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.inject.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.inject.md new file mode 100644 index 0000000000000..8ccc673ef24db --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.inject.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [inject](./kibana-plugin-plugins-expressions-public.expressionsservice.inject.md) + +## ExpressionsService.inject property + +Injects saved object references into expression AST + +Signature: + +```typescript +readonly inject: (state: ExpressionAstExpression, references: SavedObjectReference[]) => ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md index fa93435bffc38..041d66b22dd50 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md @@ -15,7 +15,7 @@ so that JSDoc appears in developers IDE when they use those `plugins.expressions Signature: ```typescript -export declare class ExpressionsService +export declare class ExpressionsService implements PersistableState ``` ## Constructors @@ -30,6 +30,7 @@ export declare class ExpressionsService | --- | --- | --- | --- | | [execute](./kibana-plugin-plugins-expressions-public.expressionsservice.execute.md) | | ExpressionsServiceStart['execute'] | | | [executor](./kibana-plugin-plugins-expressions-public.expressionsservice.executor.md) | | Executor | | +| [extract](./kibana-plugin-plugins-expressions-public.expressionsservice.extract.md) | | (state: ExpressionAstExpression) => {
state: ExpressionAstExpression;
references: SavedObjectReference[];
} | Extracts saved object references from expression AST | | [fork](./kibana-plugin-plugins-expressions-public.expressionsservice.fork.md) | | () => ExpressionsService | | | [getFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunction.md) | | ExpressionsServiceStart['getFunction'] | | | [getFunctions](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunctions.md) | | () => ReturnType<Executor['getFunctions']> | Returns POJO map of all registered expression functions, where keys are names of the functions and values are ExpressionFunction instances. | @@ -37,6 +38,7 @@ export declare class ExpressionsService | [getRenderers](./kibana-plugin-plugins-expressions-public.expressionsservice.getrenderers.md) | | () => ReturnType<ExpressionRendererRegistry['toJS']> | Returns POJO map of all registered expression renderers, where keys are names of the renderers and values are ExpressionRenderer instances. | | [getType](./kibana-plugin-plugins-expressions-public.expressionsservice.gettype.md) | | ExpressionsServiceStart['getType'] | | | [getTypes](./kibana-plugin-plugins-expressions-public.expressionsservice.gettypes.md) | | () => ReturnType<Executor['getTypes']> | Returns POJO map of all registered expression types, where keys are names of the types and values are ExpressionType instances. | +| [inject](./kibana-plugin-plugins-expressions-public.expressionsservice.inject.md) | | (state: ExpressionAstExpression, references: SavedObjectReference[]) => ExpressionAstExpression | Injects saved object references into expression AST | | [registerFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.registerfunction.md) | | (functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)) => void | Register an expression function, which will be possible to execute as part of the expression pipeline.Below we register a function which simply sleeps for given number of milliseconds to delay the execution and outputs its input as-is. ```ts expressions.registerFunction({ @@ -61,6 +63,7 @@ The actual function is defined in the fn key. The function can be \ | [registerType](./kibana-plugin-plugins-expressions-public.expressionsservice.registertype.md) | | (typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)) => void | | | [renderers](./kibana-plugin-plugins-expressions-public.expressionsservice.renderers.md) | | ExpressionRendererRegistry | | | [run](./kibana-plugin-plugins-expressions-public.expressionsservice.run.md) | | ExpressionsServiceStart['run'] | | +| [telemetry](./kibana-plugin-plugins-expressions-public.expressionsservice.telemetry.md) | | (state: ExpressionAstExpression, telemetryData?: Record<string, any>) => Record<string, any> | Extracts telemetry from expression AST | ## Methods diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.telemetry.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.telemetry.md new file mode 100644 index 0000000000000..5f28eb732e389 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.telemetry.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [telemetry](./kibana-plugin-plugins-expressions-public.expressionsservice.telemetry.md) + +## ExpressionsService.telemetry property + +Extracts telemetry from expression AST + +Signature: + +```typescript +readonly telemetry: (state: ExpressionAstExpression, telemetryData?: Record) => Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueerror.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueerror.md index 4a714fe62424f..1dee4a139c660 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueerror.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueerror.md @@ -8,13 +8,7 @@ ```typescript export declare type ExpressionValueError = ExpressionValueBoxed<'error', { - error: { - message: string; - type?: string; - name?: string; - stack?: string; - original?: Error; - }; - info?: unknown; + error: ErrorLike; + info?: SerializableState; }>; ``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md index 9dbd18ae687b4..ab0273be71402 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md @@ -18,5 +18,6 @@ export interface IInterpreterRenderHandlers | [event](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.event.md) | (event: any) => void | | | [onDestroy](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void | | | [reload](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.reload.md) | () => void | | +| [uiState](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md) | PersistedState | | | [update](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.update.md) | (params: any) => void | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md new file mode 100644 index 0000000000000..8d74c8e555fee --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) > [uiState](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md) + +## IInterpreterRenderHandlers.uiState property + +Signature: + +```typescript +uiState?: PersistedState; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md index ead6f14e0d1d7..b0c732188a46e 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md @@ -55,9 +55,7 @@ | [ExecutionParams](./kibana-plugin-plugins-expressions-public.executionparams.md) | | | [ExecutionState](./kibana-plugin-plugins-expressions-public.executionstate.md) | | | [ExecutorState](./kibana-plugin-plugins-expressions-public.executorstate.md) | | -| [ExpressionAstExpression](./kibana-plugin-plugins-expressions-public.expressionastexpression.md) | | | [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md) | | -| [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) | | | [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) | | | [ExpressionExecutor](./kibana-plugin-plugins-expressions-public.expressionexecutor.md) | | | [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) | ExpressionFunctionDefinition is the interface plugins have to implement to register a function in expressions plugin. | @@ -102,6 +100,8 @@ | [ExecutionContainer](./kibana-plugin-plugins-expressions-public.executioncontainer.md) | | | [ExecutorContainer](./kibana-plugin-plugins-expressions-public.executorcontainer.md) | | | [ExpressionAstArgument](./kibana-plugin-plugins-expressions-public.expressionastargument.md) | | +| [ExpressionAstExpression](./kibana-plugin-plugins-expressions-public.expressionastexpression.md) | | +| [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) | | | [ExpressionAstNode](./kibana-plugin-plugins-expressions-public.expressionastnode.md) | | | [ExpressionFunctionKibana](./kibana-plugin-plugins-expressions-public.expressionfunctionkibana.md) | | | [ExpressionRendererComponent](./kibana-plugin-plugins-expressions-public.expressionrenderercomponent.md) | | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.extract.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.extract.md new file mode 100644 index 0000000000000..0829824732e74 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.extract.md @@ -0,0 +1,28 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [extract](./kibana-plugin-plugins-expressions-server.executor.extract.md) + +## Executor.extract() method + +Signature: + +```typescript +extract(ast: ExpressionAstExpression): { + state: ExpressionAstExpression; + references: SavedObjectReference[]; + }; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstExpression | | + +Returns: + +`{ + state: ExpressionAstExpression; + references: SavedObjectReference[]; + }` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.inject.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.inject.md new file mode 100644 index 0000000000000..bbc5f7a3cece7 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.inject.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [inject](./kibana-plugin-plugins-expressions-server.executor.inject.md) + +## Executor.inject() method + +Signature: + +```typescript +inject(ast: ExpressionAstExpression, references: SavedObjectReference[]): ExpressionAstExpression; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstExpression | | +| references | SavedObjectReference[] | | + +Returns: + +`ExpressionAstExpression` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md index 7e6bb8c7ded5e..ec4e0bdcc4569 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md @@ -7,7 +7,7 @@ Signature: ```typescript -export declare class Executor = Record> +export declare class Executor = Record> implements PersistableState ``` ## Constructors @@ -32,12 +32,15 @@ export declare class Executor = Recordstatic | | | [extendContext(extraContext)](./kibana-plugin-plugins-expressions-server.executor.extendcontext.md) | | | +| [extract(ast)](./kibana-plugin-plugins-expressions-server.executor.extract.md) | | | | [fork()](./kibana-plugin-plugins-expressions-server.executor.fork.md) | | | | [getFunction(name)](./kibana-plugin-plugins-expressions-server.executor.getfunction.md) | | | | [getFunctions()](./kibana-plugin-plugins-expressions-server.executor.getfunctions.md) | | | | [getType(name)](./kibana-plugin-plugins-expressions-server.executor.gettype.md) | | | | [getTypes()](./kibana-plugin-plugins-expressions-server.executor.gettypes.md) | | | +| [inject(ast, references)](./kibana-plugin-plugins-expressions-server.executor.inject.md) | | | | [registerFunction(functionDefinition)](./kibana-plugin-plugins-expressions-server.executor.registerfunction.md) | | | | [registerType(typeDefinition)](./kibana-plugin-plugins-expressions-server.executor.registertype.md) | | | | [run(ast, input, context)](./kibana-plugin-plugins-expressions-server.executor.run.md) | | Execute expression and return result. | +| [telemetry(ast, telemetryData)](./kibana-plugin-plugins-expressions-server.executor.telemetry.md) | | | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.telemetry.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.telemetry.md new file mode 100644 index 0000000000000..68100c38cfa5b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.telemetry.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [telemetry](./kibana-plugin-plugins-expressions-server.executor.telemetry.md) + +## Executor.telemetry() method + +Signature: + +```typescript +telemetry(ast: ExpressionAstExpression, telemetryData: Record): Record; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstExpression | | +| telemetryData | Record<string, any> | | + +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md deleted file mode 100644 index cc8006b918dec..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-server.expressionastexpression.md) > [chain](./kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md) - -## ExpressionAstExpression.chain property - -Signature: - -```typescript -chain: ExpressionAstFunction[]; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.md index b5f83d1af7cb7..9606cb9e36960 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.md @@ -2,18 +2,13 @@ [Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-server.expressionastexpression.md) -## ExpressionAstExpression interface +## ExpressionAstExpression type Signature: ```typescript -export interface ExpressionAstExpression +export declare type ExpressionAstExpression = { + type: 'expression'; + chain: ExpressionAstFunction[]; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [chain](./kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md) | ExpressionAstFunction[] | | -| [type](./kibana-plugin-plugins-expressions-server.expressionastexpression.type.md) | 'expression' | | - diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.type.md deleted file mode 100644 index 46cd60cecaa84..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-server.expressionastexpression.md) > [type](./kibana-plugin-plugins-expressions-server.expressionastexpression.type.md) - -## ExpressionAstExpression.type property - -Signature: - -```typescript -type: 'expression'; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md deleted file mode 100644 index 052cadffb9bdb..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) > [arguments](./kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md) - -## ExpressionAstFunction.arguments property - -Signature: - -```typescript -arguments: Record; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md deleted file mode 100644 index b3227c2ac5822..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) > [debug](./kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md) - -## ExpressionAstFunction.debug property - -Debug information added to each function when expression is executed in \*debug mode\*. - -Signature: - -```typescript -debug?: ExpressionAstFunctionDebug; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.function.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.function.md deleted file mode 100644 index 9964409f49119..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.function.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) > [function](./kibana-plugin-plugins-expressions-server.expressionastfunction.function.md) - -## ExpressionAstFunction.function property - -Signature: - -```typescript -function: string; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.md index 1d49de44b571d..7fbcf2dcfd141 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.md @@ -2,20 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) -## ExpressionAstFunction interface +## ExpressionAstFunction type Signature: ```typescript -export interface ExpressionAstFunction +export declare type ExpressionAstFunction = { + type: 'function'; + function: string; + arguments: Record; + debug?: ExpressionAstFunctionDebug; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [arguments](./kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md) | Record<string, ExpressionAstArgument[]> | | -| [debug](./kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md) | ExpressionAstFunctionDebug | Debug information added to each function when expression is executed in \*debug mode\*. | -| [function](./kibana-plugin-plugins-expressions-server.expressionastfunction.function.md) | string | | -| [type](./kibana-plugin-plugins-expressions-server.expressionastfunction.type.md) | 'function' | | - diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.disabled.md similarity index 52% rename from docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.type.md rename to docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.disabled.md index 3fd10524c1599..8ae51645f5df9 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.type.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.disabled.md @@ -1,11 +1,11 @@ -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) > [type](./kibana-plugin-plugins-expressions-server.expressionastfunction.type.md) +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [disabled](./kibana-plugin-plugins-expressions-server.expressionfunction.disabled.md) -## ExpressionAstFunction.type property +## ExpressionFunction.disabled property Signature: ```typescript -type: 'function'; +disabled: boolean; ``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.extract.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.extract.md new file mode 100644 index 0000000000000..e7ecad4a6c9e4 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.extract.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [extract](./kibana-plugin-plugins-expressions-server.expressionfunction.extract.md) + +## ExpressionFunction.extract property + +Signature: + +```typescript +extract: (state: ExpressionAstFunction['arguments']) => { + state: ExpressionAstFunction['arguments']; + references: SavedObjectReference[]; + }; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.inject.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.inject.md new file mode 100644 index 0000000000000..85c98ef9193da --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.inject.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [inject](./kibana-plugin-plugins-expressions-server.expressionfunction.inject.md) + +## ExpressionFunction.inject property + +Signature: + +```typescript +inject: (state: ExpressionAstFunction['arguments'], references: SavedObjectReference[]) => ExpressionAstFunction['arguments']; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.md index aac3878b8c859..7fcda94968d13 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.md @@ -7,7 +7,7 @@ Signature: ```typescript -export declare class ExpressionFunction +export declare class ExpressionFunction implements PersistableState ``` ## Constructors @@ -23,9 +23,13 @@ export declare class ExpressionFunction | [accepts](./kibana-plugin-plugins-expressions-server.expressionfunction.accepts.md) | | (type: string) => boolean | | | [aliases](./kibana-plugin-plugins-expressions-server.expressionfunction.aliases.md) | | string[] | Aliases that can be used instead of name. | | [args](./kibana-plugin-plugins-expressions-server.expressionfunction.args.md) | | Record<string, ExpressionFunctionParameter> | Specification of expression function parameters. | +| [disabled](./kibana-plugin-plugins-expressions-server.expressionfunction.disabled.md) | | boolean | | +| [extract](./kibana-plugin-plugins-expressions-server.expressionfunction.extract.md) | | (state: ExpressionAstFunction['arguments']) => {
state: ExpressionAstFunction['arguments'];
references: SavedObjectReference[];
} | | | [fn](./kibana-plugin-plugins-expressions-server.expressionfunction.fn.md) | | (input: ExpressionValue, params: Record<string, any>, handlers: object) => ExpressionValue | Function to run function (context, args) | | [help](./kibana-plugin-plugins-expressions-server.expressionfunction.help.md) | | string | A short help text. | +| [inject](./kibana-plugin-plugins-expressions-server.expressionfunction.inject.md) | | (state: ExpressionAstFunction['arguments'], references: SavedObjectReference[]) => ExpressionAstFunction['arguments'] | | | [inputTypes](./kibana-plugin-plugins-expressions-server.expressionfunction.inputtypes.md) | | string[] | undefined | Type of inputs that this function supports. | | [name](./kibana-plugin-plugins-expressions-server.expressionfunction.name.md) | | string | Name of function | +| [telemetry](./kibana-plugin-plugins-expressions-server.expressionfunction.telemetry.md) | | (state: ExpressionAstFunction['arguments'], telemetryData: Record<string, any>) => Record<string, any> | | | [type](./kibana-plugin-plugins-expressions-server.expressionfunction.type.md) | | string | Return type of function. This SHOULD be supplied. We use it for UI and autocomplete hinting. We may also use it for optimizations in the future. | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.telemetry.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.telemetry.md new file mode 100644 index 0000000000000..2894486847b27 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.telemetry.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [telemetry](./kibana-plugin-plugins-expressions-server.expressionfunction.telemetry.md) + +## ExpressionFunction.telemetry property + +Signature: + +```typescript +telemetry: (state: ExpressionAstFunction['arguments'], telemetryData: Record) => Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.disabled.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.disabled.md new file mode 100644 index 0000000000000..88456c8700aec --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.disabled.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) > [disabled](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.disabled.md) + +## ExpressionFunctionDefinition.disabled property + +if set to true function will be disabled (but its migrate function will still be available) + +Signature: + +```typescript +disabled?: boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md index 6463c6ac537b9..51240f094b181 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md @@ -9,7 +9,7 @@ Signature: ```typescript -export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> +export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> extends PersistableStateDefinition ``` ## Properties @@ -19,6 +19,7 @@ export interface ExpressionFunctionDefinitionstring[] | What is this? | | [args](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.args.md) | {
[key in keyof Arguments]: ArgumentType<Arguments[key]>;
} | Specification of arguments that function supports. This list will also be used for autocomplete functionality when your function is being edited. | | [context](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.context.md) | {
types: AnyExpressionFunctionDefinition['inputTypes'];
} | | +| [disabled](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.disabled.md) | boolean | if set to true function will be disabled (but its migrate function will still be available) | | [help](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.help.md) | string | Help text displayed in the Expression editor. This text should be internationalized. | | [inputTypes](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.inputtypes.md) | Array<TypeToString<Input>> | List of allowed type names for input value of this function. If this property is set the input of function will be cast to the first possible type in this list. If this property is missing the input will be provided to the function as-is. | | [name](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.name.md) | Name | The name of the function, as will be used in expression. | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueerror.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueerror.md index b90e4360e055a..c8132948a8993 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueerror.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueerror.md @@ -8,13 +8,7 @@ ```typescript export declare type ExpressionValueError = ExpressionValueBoxed<'error', { - error: { - message: string; - type?: string; - name?: string; - stack?: string; - original?: Error; - }; - info?: unknown; + error: ErrorLike; + info?: SerializableState; }>; ``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md index cbaffa04bae8f..ccf6271f712b9 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md @@ -18,5 +18,6 @@ export interface IInterpreterRenderHandlers | [event](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.event.md) | (event: any) => void | | | [onDestroy](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void | | | [reload](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.reload.md) | () => void | | +| [uiState](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md) | PersistedState | | | [update](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.update.md) | (params: any) => void | | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md new file mode 100644 index 0000000000000..b09433c6454ad --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) > [uiState](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md) + +## IInterpreterRenderHandlers.uiState property + +Signature: + +```typescript +uiState?: PersistedState; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md index c9fed2e00c66c..dd7c7af466bd0 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md @@ -52,9 +52,7 @@ | [ExecutionParams](./kibana-plugin-plugins-expressions-server.executionparams.md) | | | [ExecutionState](./kibana-plugin-plugins-expressions-server.executionstate.md) | | | [ExecutorState](./kibana-plugin-plugins-expressions-server.executorstate.md) | | -| [ExpressionAstExpression](./kibana-plugin-plugins-expressions-server.expressionastexpression.md) | | | [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md) | | -| [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) | | | [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) | | | [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) | ExpressionFunctionDefinition is the interface plugins have to implement to register a function in expressions plugin. | | [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) | A mapping of ExpressionFunctionDefinitions for functions which the Expressions services provides out-of-the-box. Any new functions registered by the Expressions plugin should have their types added here. | @@ -86,6 +84,8 @@ | [ExecutionContainer](./kibana-plugin-plugins-expressions-server.executioncontainer.md) | | | [ExecutorContainer](./kibana-plugin-plugins-expressions-server.executorcontainer.md) | | | [ExpressionAstArgument](./kibana-plugin-plugins-expressions-server.expressionastargument.md) | | +| [ExpressionAstExpression](./kibana-plugin-plugins-expressions-server.expressionastexpression.md) | | +| [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) | | | [ExpressionAstNode](./kibana-plugin-plugins-expressions-server.expressionastnode.md) | | | [ExpressionFunctionKibana](./kibana-plugin-plugins-expressions-server.expressionfunctionkibana.md) | | | [ExpressionsServerSetup](./kibana-plugin-plugins-expressions-server.expressionsserversetup.md) | | diff --git a/docs/ingest_manager/ingest-manager.asciidoc b/docs/fleet/fleet.asciidoc similarity index 67% rename from docs/ingest_manager/ingest-manager.asciidoc rename to docs/fleet/fleet.asciidoc index 8f6e8036c68cd..7039468f4b185 100644 --- a/docs/ingest_manager/ingest-manager.asciidoc +++ b/docs/fleet/fleet.asciidoc @@ -1,11 +1,11 @@ [chapter] [role="xpack"] -[[ingest-manager]] -= {ingest-manager} +[[fleet]] += {fleet} experimental[] -{ingest-manager} in {kib} enables you to add and manage integrations for popular +{fleet} in {kib} enables you to add and manage integrations for popular services and platforms, as well as manage {elastic-agent} installations in standalone or {fleet} mode. @@ -17,11 +17,13 @@ Standalone mode requires you to manually configure and manage the agent locally. * An overview of the data ingest in your {es} cluster. * Multiple integrations to collect and transform data. +//TODO: Redo screen capture. + [role="screenshot"] -image::ingest_manager/images/ingest-manager-start.png[{ingest-manager} app in {kib}] +image::fleet/images/fleet-start.png[{fleet} app in {kib}] [float] == Get started -To get started with {ingest-management}, refer to the +To get started with {fleet}, refer to the {ingest-guide}/index.html[Ingest Management Guide]. diff --git a/docs/fleet/images/fleet-start.png b/docs/fleet/images/fleet-start.png new file mode 100644 index 0000000000000..60e5416fde127 Binary files /dev/null and b/docs/fleet/images/fleet-start.png differ diff --git a/docs/getting-started/images/add-sample-data.png b/docs/getting-started/images/add-sample-data.png index 1878550bc3169..b8c2002b9c4cd 100644 Binary files a/docs/getting-started/images/add-sample-data.png and b/docs/getting-started/images/add-sample-data.png differ diff --git a/docs/getting-started/images/gs_maps_time_filter.png b/docs/getting-started/images/gs_maps_time_filter.png deleted file mode 100644 index 83e20c279906e..0000000000000 Binary files a/docs/getting-started/images/gs_maps_time_filter.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-dashboard.png b/docs/getting-started/images/tutorial-dashboard.png deleted file mode 100644 index 8193d410bc5f1..0000000000000 Binary files a/docs/getting-started/images/tutorial-dashboard.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-discover-2.png b/docs/getting-started/images/tutorial-discover-2.png index 681e4834de830..cf217562c37fd 100644 Binary files a/docs/getting-started/images/tutorial-discover-2.png and b/docs/getting-started/images/tutorial-discover-2.png differ diff --git a/docs/getting-started/images/tutorial-discover-3.png b/docs/getting-started/images/tutorial-discover-3.png index bbab47acaf9d4..b024ad6dc39fe 100644 Binary files a/docs/getting-started/images/tutorial-discover-3.png and b/docs/getting-started/images/tutorial-discover-3.png differ diff --git a/docs/getting-started/images/tutorial-discover-4.png b/docs/getting-started/images/tutorial-discover-4.png new file mode 100644 index 0000000000000..945a6155c02cd Binary files /dev/null and b/docs/getting-started/images/tutorial-discover-4.png differ diff --git a/docs/getting-started/images/tutorial-final-dashboard.gif b/docs/getting-started/images/tutorial-final-dashboard.gif new file mode 100644 index 0000000000000..53b7bc04c5f65 Binary files /dev/null and b/docs/getting-started/images/tutorial-final-dashboard.gif differ diff --git a/docs/getting-started/images/tutorial-full-inspect1.png b/docs/getting-started/images/tutorial-full-inspect1.png deleted file mode 100644 index 94c9f2566f624..0000000000000 Binary files a/docs/getting-started/images/tutorial-full-inspect1.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-pattern-1.png b/docs/getting-started/images/tutorial-pattern-1.png deleted file mode 100644 index 0026b18775518..0000000000000 Binary files a/docs/getting-started/images/tutorial-pattern-1.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-sample-dashboard.png b/docs/getting-started/images/tutorial-sample-dashboard.png index ccce8c3bb3208..9f287640f201c 100644 Binary files a/docs/getting-started/images/tutorial-sample-dashboard.png and b/docs/getting-started/images/tutorial-sample-dashboard.png differ diff --git a/docs/getting-started/images/tutorial-sample-discover1.png b/docs/getting-started/images/tutorial-sample-discover1.png deleted file mode 100644 index 1bad8774ba584..0000000000000 Binary files a/docs/getting-started/images/tutorial-sample-discover1.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-sample-discover2.png b/docs/getting-started/images/tutorial-sample-discover2.png deleted file mode 100644 index a439f1d76a991..0000000000000 Binary files a/docs/getting-started/images/tutorial-sample-discover2.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-sample-edit1.png b/docs/getting-started/images/tutorial-sample-edit1.png deleted file mode 100644 index b5ae56b5c0d83..0000000000000 Binary files a/docs/getting-started/images/tutorial-sample-edit1.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-sample-edit2.png b/docs/getting-started/images/tutorial-sample-edit2.png deleted file mode 100644 index 17a029a17e1b4..0000000000000 Binary files a/docs/getting-started/images/tutorial-sample-edit2.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-sample-filter.png b/docs/getting-started/images/tutorial-sample-filter.png index 770e26e951b3a..7c1d041448557 100644 Binary files a/docs/getting-started/images/tutorial-sample-filter.png and b/docs/getting-started/images/tutorial-sample-filter.png differ diff --git a/docs/getting-started/images/tutorial-sample-filter2.png b/docs/getting-started/images/tutorial-sample-filter2.png new file mode 100644 index 0000000000000..21402feacdecd Binary files /dev/null and b/docs/getting-started/images/tutorial-sample-filter2.png differ diff --git a/docs/getting-started/images/tutorial-sample-inspect1.png b/docs/getting-started/images/tutorial-sample-inspect1.png deleted file mode 100644 index 6a3d41ae03584..0000000000000 Binary files a/docs/getting-started/images/tutorial-sample-inspect1.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-sample-query.png b/docs/getting-started/images/tutorial-sample-query.png index 847542c0b17ff..4f1ca24924b28 100644 Binary files a/docs/getting-started/images/tutorial-sample-query.png and b/docs/getting-started/images/tutorial-sample-query.png differ diff --git a/docs/getting-started/images/tutorial-sample-query2.png b/docs/getting-started/images/tutorial-sample-query2.png new file mode 100644 index 0000000000000..0e91e1069a201 Binary files /dev/null and b/docs/getting-started/images/tutorial-sample-query2.png differ diff --git a/docs/getting-started/images/tutorial-treemap.png b/docs/getting-started/images/tutorial-treemap.png new file mode 100644 index 0000000000000..32e14fd2308e3 Binary files /dev/null and b/docs/getting-started/images/tutorial-treemap.png differ diff --git a/docs/getting-started/images/tutorial-visualization-dropdown.png b/docs/getting-started/images/tutorial-visualization-dropdown.png new file mode 100644 index 0000000000000..29d1b99700964 Binary files /dev/null and b/docs/getting-started/images/tutorial-visualization-dropdown.png differ diff --git a/docs/getting-started/images/tutorial-visualize-bar-1.5.png b/docs/getting-started/images/tutorial-visualize-bar-1.5.png deleted file mode 100644 index 009152f9407e4..0000000000000 Binary files a/docs/getting-started/images/tutorial-visualize-bar-1.5.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-visualize-map-2.png b/docs/getting-started/images/tutorial-visualize-map-2.png deleted file mode 100644 index ed2fd47cb27de..0000000000000 Binary files a/docs/getting-started/images/tutorial-visualize-map-2.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-visualize-md-2.png b/docs/getting-started/images/tutorial-visualize-md-2.png deleted file mode 100644 index af56faa3b0516..0000000000000 Binary files a/docs/getting-started/images/tutorial-visualize-md-2.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-visualize-pie-2.png b/docs/getting-started/images/tutorial-visualize-pie-2.png deleted file mode 100644 index ca8f5e92146bc..0000000000000 Binary files a/docs/getting-started/images/tutorial-visualize-pie-2.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-visualize-pie-3.png b/docs/getting-started/images/tutorial-visualize-pie-3.png deleted file mode 100644 index 59fce360096c0..0000000000000 Binary files a/docs/getting-started/images/tutorial-visualize-pie-3.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial-visualize-wizard-step-1.png b/docs/getting-started/images/tutorial-visualize-wizard-step-1.png deleted file mode 100644 index afc9dda648265..0000000000000 Binary files a/docs/getting-started/images/tutorial-visualize-wizard-step-1.png and /dev/null differ diff --git a/docs/getting-started/images/tutorial_index_patterns.png b/docs/getting-started/images/tutorial_index_patterns.png deleted file mode 100644 index 430baf898b612..0000000000000 Binary files a/docs/getting-started/images/tutorial_index_patterns.png and /dev/null differ diff --git a/docs/getting-started/quick-start-guide.asciidoc b/docs/getting-started/quick-start-guide.asciidoc new file mode 100644 index 0000000000000..6386feac5ab49 --- /dev/null +++ b/docs/getting-started/quick-start-guide.asciidoc @@ -0,0 +1,142 @@ +[[get-started]] +== Quick start + +To quickly get up and running with {kib}, set up on Cloud, then add a sample data set that you can explore and analyze. + +When you've finished, you'll know how to: + +* <> + +* <> + +[float] +=== Before you begin +When security is enabled, you must have `read`, `write`, and `manage` privileges on the `kibana_sample_data_*` indices. For more information, refer to {ref}/security-privileges.html[Security privileges]. + +[float] +[[set-up-on-cloud]] +== Set up on cloud + +include::{docs-root}/shared/cloud/ess-getting-started.asciidoc[] + +[float] +[[gs-get-data-into-kibana]] +== Add the sample data + +Sample data sets come with sample visualizations, dashboards, and more to help you explore {kib} without adding your own data. + +. From the home page, click *Try our sample data*. + +. On the *Sample eCommerce orders* card, click *Add data*. ++ +[role="screenshot"] +image::getting-started/images/add-sample-data.png[] + +[float] +[[explore-the-data]] +== Explore the data + +*Discover* displays an interactive histogram that shows the distribution of of data, or documents, over time, and a table that lists the fields for each document that matches the index. By default, all fields are shown for each matching document. + +. Open the menu, then click *Discover*. + +. Change the <> to *Last 7 days*. ++ +[role="screenshot"] +image::images/tutorial-discover-2.png[] + +. To focus in on the documents you want to view, use the <>. In the *KQL* search field, enter: ++ +[source,text] +products.taxless_price >= 60 AND category : Women's Clothing ++ +The query returns the women's clothing orders for $60 and more. ++ +[role="screenshot"] +image::images/tutorial-discover-4.png[] + +. Hover over the list of *Available fields*, then click *+* next to the fields you want to view in the table. ++ +For example, when you add the *category* field, the table displays the product categories for the orders. ++ +[role="screenshot"] +image::images/tutorial-discover-3.png[] ++ +For more information, refer to <>. + +[float] +[[view-and-analyze-the-data]] +== View and analyze the data + +A dashboard is a collection of panels that you can use to view and analyze the data. Panels contain visualizations, interactive controls, Markdown, and more. + +. Open the menu, then click *Dashboard*. + +. Click *[eCommerce] Revenue Dashboard*. ++ +[role="screenshot"] +image::getting-started/images/tutorial-sample-dashboard.png[] + +[float] +[[filter-and-query-the-data]] +=== Filter the data + +To focus in on the data you want to view on the dashboard, use filters. + +. From the *Controls* visualization, make a selection from the *Manufacturer* and *Category* dropdowns, then click *Apply changes*. ++ +For example, the following dashboard shows the data for women's clothing from Gnomehouse. ++ +[role="screenshot"] +image::getting-started/images/tutorial-sample-filter.png[] + +. To manually add a filter, click *Add filter*, then specify the options. ++ +For example, to view the orders for Wednesday, select *day_of_week* from the *Field* dropdown, select *is* from the *Operator* dropdown, then select *Wednesday* from the *Value* dropdown. ++ +[role="screenshot"] +image::getting-started/images/tutorial-sample-filter2.png[] + +. When you are done, remove the filters. ++ +For more information, refer to <>. + +[float] +[[create-a-visualization]] +=== Create a visualization + +To create a treemap that shows the top regions and manufacturers, use *Lens*, then add the treemap to the dashboard. + +. From the {kib} toolbar, click *Edit*, then click *Create new*. + +. On the *New Visualization* window, click *Lens*. + +. From the *Available fields* list, drag and drop the following fields to the visualization builder: + +* *geoip.city_name* + +* *manufacturer.keyword* ++ +. From the visualization dropdown, select *Treemap*. ++ +[role="screenshot"] +image::getting-started/images/tutorial-visualization-dropdown.png[Visualization dropdown with Treemap selected] + +. Click *Save*. + +. On the *Save Lens visualization*, enter a title and make sure *Add to Dashboard after saving* is selected, then click *Save and return*. ++ +The treemap appears as the last visualization on the dashboard. ++ +[role="screenshot"] +image::getting-started/images/tutorial-final-dashboard.gif[Final dashboard with new treemap visualization] ++ +For more information, refer to <>. + +[float] +[[quick-start-whats-next]] +== What's next? + +If you are you ready to add your own data, refer to <>. + +If you want to ingest your data, refer to {ingest-guide}/ingest-management-getting-started.html[Quick start: Get logs and metrics into the Elastic Stack]. diff --git a/docs/getting-started/tutorial-define-index.asciidoc b/docs/getting-started/tutorial-define-index.asciidoc deleted file mode 100644 index 215952c2d3595..0000000000000 --- a/docs/getting-started/tutorial-define-index.asciidoc +++ /dev/null @@ -1,56 +0,0 @@ -[[tutorial-define-index]] -=== Define your index patterns - -Index patterns tell {kib} which {es} indices you want to explore. -An index pattern can match the name of a single index, or include a wildcard -(*) to match multiple indices. - -For example, Logstash typically creates a -series of indices in the format `logstash-YYYY.MMM.DD`. To explore all -of the log data from May 2018, you could specify the index pattern -`logstash-2018.05*`. - -[float] -==== Create the index patterns - -First you'll create index patterns for the Shakespeare data set, which has an -index named `shakespeare,` and the accounts data set, which has an index named -`bank`. These data sets don't contain time series data. - -. Open the menu, then go to *Stack Management > {kib} > Index Patterns*. - -. If this is your first index pattern, the *Create index pattern* page opens. - -. In the *Index pattern name* field, enter `shakes*`. -+ -[role="screenshot"] -image::images/tutorial-pattern-1.png[Image showing how to enter shakes* in Index Pattern Name field] - -. Click *Next step*. - -. On the *Configure settings* page, *Create index pattern*. -+ -You’re presented a table of all fields and associated data types in the index. - -. Create a second index pattern named `ba*`. - -[float] -==== Create an index pattern for the time series data - -Create an index pattern for the Logstash index, which -contains the time series data. - -. Create an index pattern named `logstash*`, then click *Next step*. - -. From the *Time field* dropdown, select *@timestamp, then click *Create index pattern*. -+ -[role="screenshot"] -image::images/tutorial_index_patterns.png[Image showing how to create an index pattern] - -NOTE: When you define an index pattern, the indices that match that pattern must -exist in Elasticsearch and they must contain data. To check if the indices are -available, open the menu, go to *Dev Tools > Console*, then enter `GET _cat/indices`. Alternately, use -`curl -XGET "http://localhost:9200/_cat/indices"`. -For Windows, run `Invoke-RestMethod -Uri "http://localhost:9200/_cat/indices"` in Powershell. - - diff --git a/docs/getting-started/tutorial-discovering.asciidoc b/docs/getting-started/tutorial-discovering.asciidoc deleted file mode 100644 index 99a07acf98791..0000000000000 --- a/docs/getting-started/tutorial-discovering.asciidoc +++ /dev/null @@ -1,35 +0,0 @@ -[[explore-your-data]] -=== Explore your data - -With *Discover*, you use {ref}/query-dsl-query-string-query.html#query-string-syntax[Elasticsearch -queries] to explore your data and narrow the results with filters. - -. Open the menu, then go to *Discover*. -+ -The `shakes*` index pattern appears. - -. To make `ba*` the index, click the *Change Index Pattern* dropdown, then select `ba*`. -+ -By default, all fields are shown for each matching document. - -. In the *Search* field, enter the following, then click *Update*: -+ -[source,text] -account_number<100 AND balance>47500 -+ -The search returns all account numbers between zero and 99 with balances in -excess of 47,500. Results appear for account numbers 8, 32, 78, 85, and 97. -+ -[role="screenshot"] -image::images/tutorial-discover-2.png[Image showing the search results for account numbers between zero and 99, with balances in excess of 47,500] -+ -. Hover over the list of *Available fields*, then -click *Add* next to each field you want include in the table. -+ -For example, when you add the `account_number` field, the display changes to a list of five -account numbers. -+ -[role="screenshot"] -image::images/tutorial-discover-3.png[Image showing a dropdown with five account numbers, which match the previous query for account balance] - -Now that you know what your documents contain, it's time to gain insight into your data with visualizations. diff --git a/docs/getting-started/tutorial-full-experience.asciidoc b/docs/getting-started/tutorial-full-experience.asciidoc deleted file mode 100644 index a7d5412ae0632..0000000000000 --- a/docs/getting-started/tutorial-full-experience.asciidoc +++ /dev/null @@ -1,219 +0,0 @@ -[[create-your-own-dashboard]] -== Create your own dashboard - -Ready to add data to {kib} and create your own dashboard? In this tutorial, you'll use three types of data sets that'll help you learn to: - -* <> -* <> -* <> -* <> - -[float] -[[download-the-data]] -=== Download the data - -To complete the tutorial, you'll download and use the following data sets: - -* The complete works of William Shakespeare, suitably parsed into fields -* A set of fictitious bank accounts with randomly generated data -* A set of randomly generated log files - -Create a new working directory where you want to download the files. From that directory, run the following commands: - -[source,shell] -curl -O https://download.elastic.co/demos/kibana/gettingstarted/8.x/shakespeare.json -curl -O https://download.elastic.co/demos/kibana/gettingstarted/8.x/accounts.zip -curl -O https://download.elastic.co/demos/kibana/gettingstarted/8.x/logs.jsonl.gz - -Alternatively, for Windows users, run the following commands in Powershell: - -[source,shell] -Invoke-RestMethod https://download.elastic.co/demos/kibana/gettingstarted/8.x/shakespeare.json -OutFile shakespeare.json -Invoke-RestMethod https://download.elastic.co/demos/kibana/gettingstarted/8.x/accounts.zip -OutFile accounts.zip -Invoke-RestMethod https://download.elastic.co/demos/kibana/gettingstarted/8.x/logs.jsonl.gz -OutFile logs.jsonl.gz - -Two of the data sets are compressed. To extract the files, use these commands: - -[source,shell] -unzip accounts.zip -gunzip logs.jsonl.gz - -[float] -==== Structure of the data sets - -The Shakespeare data set has the following structure: - -[source,json] -{ - "line_id": INT, - "play_name": "String", - "speech_number": INT, - "line_number": "String", - "speaker": "String", - "text_entry": "String", -} - -The accounts data set has the following structure: - -[source,json] -{ - "account_number": INT, - "balance": INT, - "firstname": "String", - "lastname": "String", - "age": INT, - "gender": "M or F", - "address": "String", - "employer": "String", - "email": "String", - "city": "String", - "state": "String" -} - -The logs data set has dozens of different fields. The notable fields include the following: - -[source,json] -{ - "memory": INT, - "geo.coordinates": "geo_point" - "@timestamp": "date" -} - -[float] -==== Set up mappings - -Before you load the Shakespeare and logs data sets, you must set up {ref}/mapping.html[_mappings_] for the fields. -Mappings divide the documents in the index into logical groups and specify the characteristics -of the fields. These characteristics include the searchability of the field -and whether it's _tokenized_, or broken up into separate words. - -NOTE: If security is enabled, you must have the `all` Kibana privilege to run this tutorial. -You must also have the `create`, `manage` `read`, `write,` and `delete` -index privileges. See {ref}/security-privileges.html[Security privileges] -for more information. - -Open the menu, then go to *Dev Tools*. On the *Console* page, set up a mapping for the Shakespeare data set: - -[source,js] -PUT /shakespeare -{ - "mappings": { - "properties": { - "speaker": {"type": "keyword"}, - "play_name": {"type": "keyword"}, - "line_id": {"type": "integer"}, - "speech_number": {"type": "integer"} - } - } -} - -//CONSOLE - -The mapping specifies field characteristics for the data set: - -* The `speaker` and `play_name` fields are keyword fields. These fields are not analyzed. -The strings are treated as a single unit even if they contain multiple words. - -* The `line_id` and `speech_number` fields are integers. - -The logs data set requires a mapping to label the latitude and longitude pairs -as geographic locations by applying the `geo_point` type. - -[source,js] -PUT /logstash-2015.05.18 -{ - "mappings": { - "properties": { - "geo": { - "properties": { - "coordinates": { - "type": "geo_point" - } - } - } - } - } -} - -//CONSOLE - -[source,js] -PUT /logstash-2015.05.19 -{ - "mappings": { - "properties": { - "geo": { - "properties": { - "coordinates": { - "type": "geo_point" - } - } - } - } - } -} - -//CONSOLE - -[source,js] -PUT /logstash-2015.05.20 -{ - "mappings": { - "properties": { - "geo": { - "properties": { - "coordinates": { - "type": "geo_point" - } - } - } - } - } -} - -//CONSOLE - -The accounts data set doesn't require any mappings. - -[float] -[[load-the-data-sets]] -==== Load the data sets - -At this point, you're ready to use the Elasticsearch {ref}/docs-bulk.html[bulk] -API to load the data sets: - -[source,shell] -curl -u elastic -H 'Content-Type: application/x-ndjson' -XPOST ':/bank/_bulk?pretty' --data-binary @accounts.json -curl -u elastic -H 'Content-Type: application/x-ndjson' -XPOST ':/shakespeare/_bulk?pretty' --data-binary @shakespeare.json -curl -u elastic -H 'Content-Type: application/x-ndjson' -XPOST ':/_bulk?pretty' --data-binary @logs.jsonl - -Or for Windows users, in Powershell: -[source,shell] -Invoke-RestMethod "http://:/bank/account/_bulk?pretty" -Method Post -ContentType 'application/x-ndjson' -InFile "accounts.json" -Invoke-RestMethod "http://:/shakespeare/_bulk?pretty" -Method Post -ContentType 'application/x-ndjson' -InFile "shakespeare.json" -Invoke-RestMethod "http://:/_bulk?pretty" -Method Post -ContentType 'application/x-ndjson' -InFile "logs.jsonl" - -These commands might take some time to execute, depending on the available computing resources. - -When you define an index pattern, the indices that match the pattern must -exist in {es} and contain data. - -To verify the availability of the indices, open the menu, go to *Dev Tools > Console*, then enter: - -[source,js] -GET /_cat/indices?v - -Alternately, use: - -[source,shell] -`curl -XGET "http://localhost:9200/_cat/indices"`. - -The output should look similar to: - -[source,shell] -health status index pri rep docs.count docs.deleted store.size pri.store.size -yellow open bank 1 1 1000 0 418.2kb 418.2kb -yellow open shakespeare 1 1 111396 0 17.6mb 17.6mb -yellow open logstash-2015.05.18 1 1 4631 0 15.6mb 15.6mb -yellow open logstash-2015.05.19 1 1 4624 0 15.7mb 15.7mb -yellow open logstash-2015.05.20 1 1 4750 0 16.4mb 16.4mb diff --git a/docs/getting-started/tutorial-sample-data.asciidoc b/docs/getting-started/tutorial-sample-data.asciidoc deleted file mode 100644 index 18ef862272f85..0000000000000 --- a/docs/getting-started/tutorial-sample-data.asciidoc +++ /dev/null @@ -1,159 +0,0 @@ -[[explore-kibana-using-sample-data]] -== Explore {kib} using sample data - -Ready to get some hands-on experience with {kib}? -In this tutorial, you’ll work with {kib} sample data and learn to: - -* <> - -* <> - -* <> - -NOTE: If security is enabled, you must have `read`, `write`, and `manage` privileges -on the `kibana_sample_data_*` indices. For more information, refer to -{ref}/security-privileges.html[Security privileges]. - -[float] -[[add-the-sample-data]] -=== Add the sample data - -Add the *Sample flight data*. - -. On the home page, click *Load a data set and a {kib} dashboard*. - -. On the *Sample flight data* card, click *Add data*. - -[float] -[[explore-the-data]] -=== Explore the data - -Explore the documents in the index that -match the selected index pattern. The index pattern tells {kib} which {es} index you want to -explore. - -. Open the menu, then go to *Discover*. - -. Make sure `kibana_sample_data_flights` is the current index pattern. -You might need to click *New* in the {kib} toolbar to refresh the data. -+ -You'll see a histogram that shows the distribution of -documents over time. A table lists the fields for -each document that matches the index. By default, all fields are shown. -+ -[role="screenshot"] -image::getting-started/images/tutorial-sample-discover1.png[] - -. Hover over the list of *Available fields*, then click *Add* next -to each field you want explore in the table. -+ -[role="screenshot"] -image::getting-started/images/tutorial-sample-discover2.png[] - -[float] -[[view-and-analyze-the-data]] -=== View and analyze the data - -A _dashboard_ is a collection of panels that provide you with an overview of your data that you can -use to analyze your data. Panels contain everything you need, including visualizations, -interactive controls, Markdown, and more. - -To open the *Global Flight* dashboard, open the menu, then go to *Dashboard*. - -[role="screenshot"] -image::getting-started/images/tutorial-sample-dashboard.png[] - -[float] -[[change-the-panel-data]] -==== Change the panel data - -To gain insights into your data, change the appearance and behavior of the panels. -For example, edit the metric panel to find the airline that has the lowest average fares. - -. In the {kib} toolbar, click *Edit*. - -. In the *Average Ticket Price* metric panel, open the panel menu, then select *Edit visualization*. - -. To change the data on the panel, use an {es} {ref}/search-aggregations.html[bucket aggregation], -which sorts the documents that match your search criteria into different categories or buckets. - -.. In the *Buckets* pane, select *Add > Split group*. - -.. From the *Aggregation* dropdown, select *Terms*. - -.. From the *Field* dropdown, select *Carrier*. - -.. Set *Descending* to *4*, then click *Update*. -+ -The average ticket price for all four airlines appear in the visualization builder. -+ -[role="screenshot"] -image::getting-started/images/tutorial-sample-edit1.png[] - -. To save your changes, click *Save and return* in the {kib} toolbar. - -. To save the dashboard, click *Save* in the {kib} toolbar. -+ -[role="screenshot"] -image::getting-started/images/tutorial-sample-edit2.png[] - -[float] -[[filter-and-query-the-data]] -==== Filter and query the data - -To focus in on the data you want to explore, use filters and queries. -For more information, refer to -{ref}/query-filter-context.html[Query and filter context]. - -To filter the data: - -. In the *Controls* visualization, select an *Origin City* and *Destination City*, then click *Apply changes*. -+ -The `OriginCityName` and the `DestCityName` fields filter the data in the panels. -+ -For example, the following dashboard shows the data for flights from London to Milan. -+ -[role="screenshot"] -image::getting-started/images/tutorial-sample-filter.png[] - -. To manually add a filter, click *Add filter*, -then specify the data you want to view. - -. When you are finished experimenting, remove all filters. - -[[query-the-data]] -To query the data: - -. To view all flights out of Rome, enter the following in the *KQL* query bar, then click *Update*: -+ -[source,text] -OriginCityName: Rome - -. For a more complex query with AND and OR, enter: -+ -[source,text] -OriginCityName:Rome AND (Carrier:JetBeats OR Carrier:"Kibana Airlines") -+ -The dashboard panels update to display the flights out of Rome on JetBeats and -{kib} Airlines. -+ -[role="screenshot"] -image::getting-started/images/tutorial-sample-query.png[] - -. When you are finished exploring, remove the query by -clearing the contents in the *KQL* query bar, then click *Update*. - -[float] -=== Next steps - -Now that you know the {kib} basics, try out the <> tutorial, where you'll learn to: - -* Add a data set to {kib} - -* Define an index pattern - -* Discover and explore data - -* Create and add panels to a dashboard - - diff --git a/docs/getting-started/tutorial-visualizing.asciidoc b/docs/getting-started/tutorial-visualizing.asciidoc deleted file mode 100644 index a53c8cb6bc23d..0000000000000 --- a/docs/getting-started/tutorial-visualizing.asciidoc +++ /dev/null @@ -1,193 +0,0 @@ -[[tutorial-visualizing]] -=== Visualize your data - -Shape your data using a variety -of {kib} supported visualizations, tables, and more. In this tutorial, you'll create four -visualizations that you'll use to create a dashboard. - -To begin, open the menu, go to *Dashboard*, then click *Create new dashboard*. - -[float] -[[compare-the-number-of-speaking-parts-in-the-play]] -=== Compare the number of speaking parts in the plays - -To visualize the Shakespeare data and compare the number of speaking parts in the plays, create a bar chart using *Lens*. - -. Click *Create new*, then click *Lens* on the *New Visualization* window. -+ -[role="screenshot"] -image::images/tutorial-visualize-wizard-step-1.png[Image showing different options for your new visualization] - -. Make sure the index pattern is *shakes*. - -. Display the play data along the x-axis. - -.. From the *Available fields* list, drag and drop *play_name* to the *X-axis* field. - -.. Click *Top values of play_name*. - -.. From the *Order direction* dropdown, select *Ascending*. - -.. In the *Label* field, enter `Play Name`. - -. Display the number of speaking parts per play along the y-axis. - -.. From the *Available fields* list, drag and drop *speaker* to the *Y-axis* field. - -.. Click *Unique count of speaker*. - -.. In the *Label* field, enter `Speaking Parts`. -+ -[role="screenshot"] -image::images/tutorial-visualize-bar-1.5.png[Bar chart showing the speaking parts data] - -. *Save* the chart with the name `Bar Example`. -+ -To show a tooltip with the number of speaking parts for that play, hover over a bar. -+ -Notice how the individual play names show up as whole phrases, instead of -broken up into individual words. This is the result of the mapping -you did at the beginning of the tutorial, when you marked the `play_name` field -as `not analyzed`. - -[float] -[[view-the-average-account-balance-by-age]] -=== View the average account balance by age - -To gain insight into the account balances in the bank account data, create a pie chart. In this tutorial, you'll use the {es} -{ref}/search-aggregations.html[bucket aggregation] to specify the pie slices to display. The bucket aggregation sorts the documents that match your search criteria into different -categories and establishes multiple ranges of account balances so that you can find how many accounts fall into each range. - -. Click *Create new*, then click *Pie* on the *New Visualization* window. - -. On the *Choose a source* window, select `ba*`. -+ -Since the default search matches all documents, the pie contains a single slice. - -. In the *Buckets* pane, click *Add > Split slices.* - -.. From the *Aggregation* dropdown, select *Range*. - -.. From the *Field* dropdown, select *balance*. - -.. Click *Add range* until there are six rows of fields, then define the following ranges: -+ -[source,text] -0 999 -1000 2999 -3000 6999 -7000 14999 -15000 30999 -31000 50000 - -. Click *Update*. -+ -The pie chart displays the proportion of the 1,000 accounts that fall into each of the ranges. -+ -[role="screenshot"] -image::images/tutorial-visualize-pie-2.png[Pie chart displaying accounts that fall into each of the ranges, scaled to 1000 accounts] - -. Add another bucket aggregation that displays the ages of the account holders. - -.. In the *Buckets* pane, click *Add*, then click *Split slices*. - -.. From the *Sub aggregation* dropdown, select *Terms*. - -.. From the *Field* dropdown, select *age*, then click *Update*. -+ -The break down of the ages of the account holders are displayed -in a ring around the balance ranges. -+ -[role="screenshot"] -image::images/tutorial-visualize-pie-3.png[Final pie chart showing all of the changes] - -. Click *Save*, then enter `Pie Example` in the *Title* field. - -[float] -[role="xpack"] -[[visualize-geographic-information]] -=== Visualize geographic information - -To visualize geographic information in the log file data, use <>. - -. Click *Create new*, then click *Maps* on the *New Visualization* window. - -. To change the time, use the time filter. - -.. Set the *Start date* to `May 18, 2015 @ 12:00:00.000`. - -.. Set the *End date* to `May 20, 2015 @ 12:00:00.000`. -+ -[role="screenshot"] -image::images/gs_maps_time_filter.png[Image showing the time filter for Maps tutorial] - -.. Click *Update* - -. Map the geo coordinates from the log files. - -.. Click *Add layer > Clusters and grids*. - -.. From the *Index pattern* dropdown, select *logstash*. - -.. Click *Add layer*. - -. Specify the *Layer Style*. - -.. From the *Fill color* dropdown, select the yellow to red color ramp. - -.. In the *Border width* field, enter `3`. - -.. From the *Border color* dropdown, select *#FFF*, then click *Save & close*. -+ -[role="screenshot"] -image::images/tutorial-visualize-map-2.png[Example of a map visualization] - -. Click *Save*, then enter `Map Example` in the *Title* field. - -. Add the map to your dashboard. - -.. Open the menu, go to *Dashboard*, then click *Add*. - -.. On the *Add panels* flyout, click *Map Example*. - -[float] -[[tutorial-visualize-markdown]] -=== Add context to your visualizations with Markdown - -Add context to your new visualizations with Markdown text. - -. Click *Create new*, then click *Markdown* on the *New Visualization* window. - -. In the *Markdown* text field, enter: -+ -[source,markdown] -# This is a tutorial dashboard! -The Markdown widget uses **markdown** syntax. -> Blockquotes in Markdown use the > character. - -. Click *Update*. -+ -The Markdown renders in the preview pane. -+ -[role="screenshot"] -image::images/tutorial-visualize-md-2.png[Image showing example markdown editing field] - -. Click *Save*, then enter `Markdown Example` in the *Title* field. - -[role="screenshot"] -image::images/tutorial-dashboard.png[Final visualization with bar chart, pie chart, map, and markdown text field] - -[float] -=== Next steps - -Now that you have the basics, you're ready to start exploring your own system data with {kib}. - -* To add your own data to {kib}, refer to <>. - -* To search and filter your data, refer to {kibana-ref}/discover.html[Discover]. - -* To create a dashboard with your own data, refer to <>. - -* To create maps that you can add to your dashboards, refer to <>. - -* To create presentations of your live data, refer to <>. diff --git a/docs/ingest_manager/images/ingest-manager-start.png b/docs/ingest_manager/images/ingest-manager-start.png deleted file mode 100644 index 89174686a9768..0000000000000 Binary files a/docs/ingest_manager/images/ingest-manager-start.png and /dev/null differ diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 1bae04cc2e58b..8e8d0e5bf996e 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -454,9 +454,6 @@ The opacity of the chart items that are dimmed when highlighting another element of the chart. The lower this number, the more the highlighted element stands out. This must be a number between 0 and 1. -[[visualization-loadingdelay]]`visualization:loadingDelay`:: -The time to wait before dimming visualizations during a query. - [[visualization-regionmap-showwarnings]]`visualization:regionmap:showWarnings`:: Shows a warning in a region map when terms cannot be joined to a shape. diff --git a/docs/management/rollups/create_and_manage_rollups.asciidoc b/docs/management/rollups/create_and_manage_rollups.asciidoc index e20f384b5ed18..7324f45594bd7 100644 --- a/docs/management/rollups/create_and_manage_rollups.asciidoc +++ b/docs/management/rollups/create_and_manage_rollups.asciidoc @@ -67,7 +67,7 @@ You can read more at {ref}/rollup-job-config.html[rollup job configuration]. === Try it: Create and visualize rolled up data This example creates a rollup job to capture log data from sample web logs. -To follow along, add the <>. +To follow along, add the sample web logs data set. In this example, you want data that is older than 7 days in the target index pattern `kibana_sample_data_logs` to roll up once a day into the index `rollup_logstash`. You’ll bucket the diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index 5067bc08bec99..92e02de89f181 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -59,7 +59,7 @@ This page has moved. Please see <>. [role="exclude",id="add-sample-data"] == Add sample data -This page has moved. Please see <>. +This page has moved. Please see <>. [role="exclude",id="tilemap"] == Coordinate map @@ -112,3 +112,9 @@ This content has moved. See This content has moved. See {ref}/ccr-getting-started.html#ccr-getting-started-remote-cluster[Connect to a remote cluster]. + +[role="exclude",id="tutorial-define-index"] +== Define your index patterns + +This content has moved. See +<>. diff --git a/docs/settings/ingest-manager-settings.asciidoc b/docs/settings/fleet-settings.asciidoc similarity index 68% rename from docs/settings/ingest-manager-settings.asciidoc rename to docs/settings/fleet-settings.asciidoc index 9fa83fc242b4a..9c28d28003175 100644 --- a/docs/settings/ingest-manager-settings.asciidoc +++ b/docs/settings/fleet-settings.asciidoc @@ -1,29 +1,29 @@ [role="xpack"] -[[ingest-manager-settings-kb]] -=== {ingest-manager} settings in {kib} +[[fleet-settings-kb]] +=== {fleet} settings in {kib} ++++ -{ingest-manager} settings +{fleet} settings ++++ experimental[] You can configure `xpack.fleet` settings in your `kibana.yml`. -By default, {ingest-manager} is enabled. To use {fleet}, you also need to configure {kib} and {es} hosts. +By default, {fleet} is enabled. To use {fleet}, you also need to configure {kib} and {es} hosts. See the {ingest-guide}/index.html[Ingest Management] docs for more information. -[[general-ingest-manager-settings-kb]] -==== General {ingest-manager} settings +[[general-fleet-settings-kb]] +==== General {fleet} settings [cols="2*<"] |=== | `xpack.fleet.enabled` {ess-icon} - | Set to `true` (default) to enable {ingest-manager}. + | Set to `true` (default) to enable {fleet}. | `xpack.fleet.agents.enabled` {ess-icon} | Set to `true` (default) to enable {fleet}. |=== -[[ingest-manager-data-visualizer-settings]] +[[fleet-data-visualizer-settings]] ==== {package-manager} settings diff --git a/docs/settings/settings-xkb.asciidoc b/docs/settings/settings-xkb.asciidoc index 04fed0d6204b7..9d9cc92401896 100644 --- a/docs/settings/settings-xkb.asciidoc +++ b/docs/settings/settings-xkb.asciidoc @@ -20,4 +20,4 @@ include::ml-settings.asciidoc[] include::reporting-settings.asciidoc[] include::spaces-settings.asciidoc[] include::i18n-settings.asciidoc[] -include::ingest-manager-settings.asciidoc[] +include::fleet-settings.asciidoc[] diff --git a/docs/setup/connect-to-elasticsearch.asciidoc b/docs/setup/connect-to-elasticsearch.asciidoc index ea02afb8a9fda..0daa3f1e0e55e 100644 --- a/docs/setup/connect-to-elasticsearch.asciidoc +++ b/docs/setup/connect-to-elasticsearch.asciidoc @@ -11,7 +11,7 @@ To start working with your data in {kib}, you can: * Connect {kib} with existing {es} indices. -If you're not ready to use your own data, you can add a <> +If you're not ready to use your own data, you can add a <> to see all that you can do in {kib}. [float] diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 903bb59cef380..2f2c87ca9c7d4 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -638,7 +638,7 @@ include::{kib-repo-dir}/settings/alert-action-settings.asciidoc[] include::{kib-repo-dir}/settings/apm-settings.asciidoc[] include::{kib-repo-dir}/settings/dev-settings.asciidoc[] include::{kib-repo-dir}/settings/graph-settings.asciidoc[] -include::{kib-repo-dir}/settings/ingest-manager-settings.asciidoc[] +include::{kib-repo-dir}/settings/fleet-settings.asciidoc[] include::{kib-repo-dir}/settings/i18n-settings.asciidoc[] include::{kib-repo-dir}/settings/logs-ui-settings.asciidoc[] include::{kib-repo-dir}/settings/infrastructure-ui-settings.asciidoc[] diff --git a/docs/user/canvas.asciidoc b/docs/user/canvas.asciidoc index 0b0eb7a318495..297dfac5b10bd 100644 --- a/docs/user/canvas.asciidoc +++ b/docs/user/canvas.asciidoc @@ -17,6 +17,8 @@ With Canvas, you can: * Focus the data you want to display with filters. +To begin, open the menu, then go to *Canvas*. + [role="screenshot"] image::images/canvas-gs-example.png[Getting started example] @@ -26,7 +28,8 @@ For a quick overview of Canvas, watch link:https://www.youtube.com/watch?v=ZqvF_ [[create-workpads]] == Create workpads -A _workpad_ provides you with a space where you can build presentations of your live data. +A _workpad_ provides you with a space where you can build presentations of your live data. With Canvas, +you can create a workpad from scratch, start with a preconfigured workpad, import an existing workpad, or use a sample data workpad. [float] [[start-with-a-blank-workpad]] @@ -34,19 +37,15 @@ A _workpad_ provides you with a space where you can build presentations of your To use the background colors, images, and data of your choice, start with a blank workpad. -. Open the menu, then go to *Canvas*. - -. On the *Canvas workpads* view, click *Create workpad*. +. On the *Canvas workpads* page, click *Create workpad*. -. Add a *Name* to your workpad. +. Specify the *Workpad settings*. -. In the *Width* and *Height* fields, specify the size. +.. Add a *Name* to your workpad. -. Select the layout. -+ -For example, click *720p* for a traditional presentation layout. +.. In the *Width* and *Height* fields, specify the size, or select one of default layouts. -. Click the *Background color* picker, then select the background color for your workpad. +.. Click the *Background* color picker, then select the color for your workpad. + [role="screenshot"] image::images/canvas-background-color-picker.png[Canvas color picker] @@ -57,9 +56,7 @@ image::images/canvas-background-color-picker.png[Canvas color picker] If you're unsure about where to start, you can use one of the preconfigured templates that come with Canvas. -. Open the menu, then go to *Canvas*. - -. On the *Canvas workpads* view, select *Templates*. +. On the *Canvas workpads* page, select *Templates*. . Click the preconfigured template that you want to use. @@ -69,17 +66,15 @@ If you're unsure about where to start, you can use one of the preconfigured temp [[import-existing-workpads]] === Import existing workpads -When you want to use a workpad that someone else has already started, import the JSON file into Canvas. - -. Open the menu, then go to *Canvas*. +When you want to use a workpad that someone else has already started, import the JSON file. -. On the *Canvas workpads* view, click and drag the file to the *Import workpad JSON file* field. +To begin, drag the file to the *Import workpad JSON file* field on the *Canvas workpads* page. [float] [[use-sample-data-workpads]] === Use sample data workpads -Each of the sample data sets comes with a Canvas workpad that you can use for your own workpad inspiration. +Each of the {kib} sample data sets comes with a workpad that you can use for your own workpad inspiration. . Add a {kibana-ref}/add-sample-data.html[sample data set]. @@ -123,12 +118,12 @@ To save a group of elements, press and hold Shift, select the elements you want Elements are saved in *Add element > My elements*. [float] -[[add-existing-visuualizations]] -=== Add existing visualizations +[[add-saved-objects]] +=== Add saved objects Add <> to your workpad, such as maps and visualizations. -. Click *Add element > Add from Visualize Library*. +. Click *Add element > Add from {kib}*. . Select the saved object you want to add. + diff --git a/docs/user/dashboard/dashboard-drilldown.asciidoc b/docs/user/dashboard/dashboard-drilldown.asciidoc index e50c1281beede..5e928fd731bb4 100644 --- a/docs/user/dashboard/dashboard-drilldown.asciidoc +++ b/docs/user/dashboard/dashboard-drilldown.asciidoc @@ -39,7 +39,7 @@ Create the *Host Overview* drilldown shown above. *Set up the dashboards* -. Add the <> data set. +. Add the sample web logs data set. . Create a new dashboard, called `Host Overview`, and include these visualizations from the sample data set: diff --git a/docs/user/dashboard/url-drilldown.asciidoc b/docs/user/dashboard/url-drilldown.asciidoc index 620a2d2056bf1..b71dfb016c765 100644 --- a/docs/user/dashboard/url-drilldown.asciidoc +++ b/docs/user/dashboard/url-drilldown.asciidoc @@ -36,7 +36,7 @@ The following panels support URL drilldowns: This example shows how to create the "Show on Github" drilldown shown above. -. Add the <> data set. +. Add the sample web logs data set. . Open the *[Logs] Web traffic* dashboard. This isn’t data from Github, but it should work for demonstration purposes. . In the dashboard menu bar, click *Edit*. . In *[Logs] Visitors by OS*, open the panel menu, and then select *Create drilldown*. diff --git a/docs/user/dashboard/vega-reference.asciidoc b/docs/user/dashboard/vega-reference.asciidoc index 6fd30690b988e..378f7a53a6650 100644 --- a/docs/user/dashboard/vega-reference.asciidoc +++ b/docs/user/dashboard/vega-reference.asciidoc @@ -64,16 +64,17 @@ Override it by providing a different `stroke`, `fill`, or `color` (Vega-Lite) va [[vega-queries]] ==== Writing {es} queries in Vega -experimental[] {kib} extends the Vega https://vega.github.io/vega/docs/data/[data] elements -with support for direct {es} queries specified as a `url`. +{kib} extends the Vega https://vega.github.io/vega/docs/data/[data] elements +with support for direct {es} queries specified as `url`. -Because of this, {kib} is **unable to support dynamically loaded data**, +{kib} is **unable to support dynamically loaded data**, which would otherwise work in Vega. All data is fetched before it's passed to the Vega renderer. -To define an {es} query in Vega, set the `url` to an object. {kib} will parse +To define an {es} query in Vega, set the `url` to an object. {kib} parses the object looking for special tokens that allow your query to integrate with {kib}. -These tokens are: + +Tokens include the following: * `%context%: true`: Set at the top level, and replaces the `query` section with filters from dashboard * `%timefield%: `: Set at the top level, integrates the query with the dashboard time filter @@ -87,8 +88,7 @@ These tokens are: * `"%dashboard_context-filter_clause%"`: String replaced by an object containing filters * `"%dashboard_context-must_not_clause%"`: String replaced by an object containing filters -Putting this together, an example query that counts the number of documents in -a specific index: +For example, the following query counts the number of documents in a specific index: [source,yaml] ---- diff --git a/docs/user/getting-started.asciidoc b/docs/user/getting-started.asciidoc deleted file mode 100644 index a877f6a66a79a..0000000000000 --- a/docs/user/getting-started.asciidoc +++ /dev/null @@ -1,61 +0,0 @@ -[[get-started]] -= Get started - -[partintro] --- - -Ready to try out {kib} and see what it can do? The quickest way to get started with {kib} is to set up on Cloud, then add a sample data set to explore the full range of {kib} features. - -[float] -[[set-up-on-cloud]] -== Set up on cloud - -include::{docs-root}/shared/cloud/ess-getting-started.asciidoc[] - -[float] -[[gs-get-data-into-kibana]] -== Get data into {kib} - -The easiest way to get data into {kib} is to add a sample data set. - -{kib} has several sample data sets that you can use before loading your own data: - -* *Sample eCommerce orders* includes visualizations for tracking product-related information, -such as cost, revenue, and price. - -* *Sample flight data* includes visualizations for monitoring flight routes. - -* *Sample web logs* includes visualizations for monitoring website traffic. - -To use the sample data sets: - -. Go to the home page. - -. Click *Load a data set and a {kib} dashboard*. - -. Click *View data* and view the prepackaged dashboards, maps, and more. - -[role="screenshot"] -image::getting-started/images/add-sample-data.png[] - -NOTE: The timestamps in the sample data sets are relative to when they are installed. -If you uninstall and reinstall a data set, the timestamps change to reflect the most recent installation. - -[float] -== Next steps - -* To get a hands-on experience creating visualizations, follow the <> tutorial. - -* If you're ready to load an actual data set and build a dashboard, follow the <> tutorial. - --- - -include::{kib-repo-dir}/getting-started/tutorial-sample-data.asciidoc[] - -include::{kib-repo-dir}/getting-started/tutorial-full-experience.asciidoc[] - -include::{kib-repo-dir}/getting-started/tutorial-define-index.asciidoc[] - -include::{kib-repo-dir}/getting-started/tutorial-discovering.asciidoc[] - -include::{kib-repo-dir}/getting-started/tutorial-visualizing.asciidoc[] diff --git a/docs/user/index.asciidoc b/docs/user/index.asciidoc index e909626c5779c..c186704dd2f16 100644 --- a/docs/user/index.asciidoc +++ b/docs/user/index.asciidoc @@ -2,6 +2,8 @@ include::introduction.asciidoc[] include::whats-new.asciidoc[] +include::{kib-repo-dir}/getting-started/quick-start-guide.asciidoc[] + include::setup.asciidoc[] include::monitoring/configuring-monitoring.asciidoc[leveloffset=+1] @@ -11,8 +13,6 @@ include::monitoring/monitoring-kibana.asciidoc[leveloffset=+2] include::security/securing-kibana.asciidoc[] -include::getting-started.asciidoc[] - include::discover.asciidoc[] include::dashboard/dashboard.asciidoc[] @@ -43,7 +43,7 @@ include::monitoring/index.asciidoc[] include::management.asciidoc[] -include::{kib-repo-dir}/ingest_manager/ingest-manager.asciidoc[] +include::{kib-repo-dir}/fleet/fleet.asciidoc[] include::reporting/index.asciidoc[] diff --git a/docs/user/introduction.asciidoc b/docs/user/introduction.asciidoc index 079d183dd959d..7e5dc59b03a2c 100644 --- a/docs/user/introduction.asciidoc +++ b/docs/user/introduction.asciidoc @@ -155,6 +155,6 @@ and start exploring data in minutes. You can also <> — no code, no additional infrastructure required. -Our <> and in-product guidance can +Our <> and in-product guidance can help you get up and running, faster. Click the help icon image:images/intro-help-icon.png[] in the top navigation bar for help with questions or to provide feedback. diff --git a/docs/user/security/rbac_tutorial.asciidoc b/docs/user/security/rbac_tutorial.asciidoc index cc4af9041bcd9..bf7be6284b1a9 100644 --- a/docs/user/security/rbac_tutorial.asciidoc +++ b/docs/user/security/rbac_tutorial.asciidoc @@ -28,7 +28,7 @@ To complete this tutorial, you'll need the following: * **A space**: In this tutorial, use `Dev Mortgage` as the space name. See <> for details on creating a space. -* **Data**: You can use <> or +* **Data**: You can use <> or live data. In the following steps, Filebeat and Metricbeat data are used. [float] diff --git a/examples/embeddable_examples/kibana.json b/examples/embeddable_examples/kibana.json index 223b8c55a5fde..6025d24665901 100644 --- a/examples/embeddable_examples/kibana.json +++ b/examples/embeddable_examples/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["embeddable", "uiActions", "dashboard", "savedObjects"], + "requiredPlugins": ["embeddable", "uiActions", "savedObjects", "dashboard"], "optionalPlugins": [], "extraPublicDirs": ["public/todo", "public/hello_world", "public/todo/todo_ref_embeddable"], "requiredBundles": ["kibanaReact"] diff --git a/examples/embeddable_examples/public/book/book_embeddable.tsx b/examples/embeddable_examples/public/book/book_embeddable.tsx index 65ec22a2759e2..8d633a892ec6d 100644 --- a/examples/embeddable_examples/public/book/book_embeddable.tsx +++ b/examples/embeddable_examples/public/book/book_embeddable.tsx @@ -26,10 +26,10 @@ import { EmbeddableOutput, SavedObjectEmbeddableInput, ReferenceOrValueEmbeddable, + AttributeService, } from '../../../../src/plugins/embeddable/public'; import { BookSavedObjectAttributes } from '../../common'; import { BookEmbeddableComponent } from './book_component'; -import { AttributeService } from '../../../../src/plugins/dashboard/public'; export const BOOK_EMBEDDABLE = 'book'; export type BookEmbeddableInput = BookByValueInput | BookByReferenceInput; diff --git a/examples/embeddable_examples/public/book/book_embeddable_factory.tsx b/examples/embeddable_examples/public/book/book_embeddable_factory.tsx index a535552282150..f569e2e8d154d 100644 --- a/examples/embeddable_examples/public/book/book_embeddable_factory.tsx +++ b/examples/embeddable_examples/public/book/book_embeddable_factory.tsx @@ -25,6 +25,8 @@ import { EmbeddableFactoryDefinition, IContainer, EmbeddableFactory, + EmbeddableStart, + AttributeService, } from '../../../../src/plugins/embeddable/public'; import { BookEmbeddable, @@ -38,11 +40,10 @@ import { SavedObjectsClientContract, SimpleSavedObject, } from '../../../../src/core/public'; -import { DashboardStart, AttributeService } from '../../../../src/plugins/dashboard/public'; import { checkForDuplicateTitle, OnSaveProps } from '../../../../src/plugins/saved_objects/public'; interface StartServices { - getAttributeService: DashboardStart['getAttributeService']; + getAttributeService: EmbeddableStart['getAttributeService']; openModal: OverlayStart['openModal']; savedObjectsClient: SavedObjectsClientContract; overlays: OverlayStart; diff --git a/examples/embeddable_examples/public/book/edit_book_action.tsx b/examples/embeddable_examples/public/book/edit_book_action.tsx index 77035b6887734..e2133a8d51ea2 100644 --- a/examples/embeddable_examples/public/book/edit_book_action.tsx +++ b/examples/embeddable_examples/public/book/edit_book_action.tsx @@ -22,7 +22,11 @@ import { i18n } from '@kbn/i18n'; import { BookSavedObjectAttributes, BOOK_SAVED_OBJECT } from '../../common'; import { createAction } from '../../../../src/plugins/ui_actions/public'; import { toMountPoint } from '../../../../src/plugins/kibana_react/public'; -import { ViewMode, SavedObjectEmbeddableInput } from '../../../../src/plugins/embeddable/public'; +import { + ViewMode, + SavedObjectEmbeddableInput, + EmbeddableStart, +} from '../../../../src/plugins/embeddable/public'; import { BookEmbeddable, BOOK_EMBEDDABLE, @@ -30,13 +34,12 @@ import { BookByValueInput, } from './book_embeddable'; import { CreateEditBookComponent } from './create_edit_book_component'; -import { DashboardStart } from '../../../../src/plugins/dashboard/public'; import { OnSaveProps } from '../../../../src/plugins/saved_objects/public'; import { SavedObjectsClientContract } from '../../../../src/core/target/types/public/saved_objects'; interface StartServices { openModal: OverlayStart['openModal']; - getAttributeService: DashboardStart['getAttributeService']; + getAttributeService: EmbeddableStart['getAttributeService']; savedObjectsClient: SavedObjectsClientContract; } diff --git a/examples/embeddable_examples/public/plugin.ts b/examples/embeddable_examples/public/plugin.ts index 6d1b119e741bd..9b9770e40611e 100644 --- a/examples/embeddable_examples/public/plugin.ts +++ b/examples/embeddable_examples/public/plugin.ts @@ -62,7 +62,6 @@ import { ACTION_ADD_BOOK_TO_LIBRARY, createAddBookToLibraryAction, } from './book/add_book_to_library_action'; -import { DashboardStart } from '../../../src/plugins/dashboard/public'; import { ACTION_UNLINK_BOOK_FROM_LIBRARY, createUnlinkBookFromLibraryAction, @@ -75,7 +74,6 @@ export interface EmbeddableExamplesSetupDependencies { export interface EmbeddableExamplesStartDependencies { embeddable: EmbeddableStart; - dashboard: DashboardStart; savedObjectsClient: SavedObjectsClient; } @@ -157,7 +155,7 @@ export class EmbeddableExamplesPlugin this.exampleEmbeddableFactories.getBookEmbeddableFactory = deps.embeddable.registerEmbeddableFactory( BOOK_EMBEDDABLE, new BookEmbeddableFactoryDefinition(async () => ({ - getAttributeService: (await core.getStartServices())[1].dashboard.getAttributeService, + getAttributeService: (await core.getStartServices())[1].embeddable.getAttributeService, openModal: (await core.getStartServices())[0].overlays.openModal, savedObjectsClient: (await core.getStartServices())[0].savedObjects.client, overlays: (await core.getStartServices())[0].overlays, @@ -165,7 +163,7 @@ export class EmbeddableExamplesPlugin ); const editBookAction = createEditBookAction(async () => ({ - getAttributeService: (await core.getStartServices())[1].dashboard.getAttributeService, + getAttributeService: (await core.getStartServices())[1].embeddable.getAttributeService, openModal: (await core.getStartServices())[0].overlays.openModal, savedObjectsClient: (await core.getStartServices())[0].savedObjects.client, })); diff --git a/examples/search_examples/public/components/app.tsx b/examples/search_examples/public/components/app.tsx index 61e50d3379d03..2425f3bbad8a9 100644 --- a/examples/search_examples/public/components/app.tsx +++ b/examples/search_examples/public/components/app.tsx @@ -232,7 +232,6 @@ export const SearchExamplesApp = ({ Index Pattern )` +#### `CiStatsReporter#metrics(metrics: Metric[])` Use this method to record metrics in the Kibana CI Stats service. +```ts +interface Metric { + group: string, + id: string, + value: number, + // optional limit, values which exceed the limit will fail PRs + limit?: number + // optional path, relative to the root of the repo, where config values + // are defined. Will be linked to in PRs which have overages. + limitConfigPath?: string +} +``` + Example: ```ts diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts index b0378ab6c5cd5..a2f3b63daec50 100644 --- a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts +++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts @@ -29,7 +29,13 @@ interface Config { buildId: string; } -export type CiStatsMetrics = Array<{ group: string; id: string; value: number }>; +export type CiStatsMetrics = Array<{ + group: string; + id: string; + value: number; + limit?: number; + limitConfigPath?: string; +}>; function parseConfig(log: ToolingLog) { const configJson = process.env.KIBANA_CI_STATS_CONFIG; diff --git a/packages/kbn-optimizer/README.md b/packages/kbn-optimizer/README.md index a666907f02678..3fdf915e84c21 100644 --- a/packages/kbn-optimizer/README.md +++ b/packages/kbn-optimizer/README.md @@ -84,9 +84,9 @@ const config = OptimizerConfig.create({ dist: true }); -await runOptimizer(config) - .pipe(logOptimizerState(log, config)) - .toPromise(); +await lastValueFrom( + runOptimizer(config).pipe(logOptimizerState(log, config)) +); ``` This is essentially what we're doing in [`script/build_kibana_platform_plugins`][Cli] and the new [build system task][BuildTask]. diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml new file mode 100644 index 0000000000000..b075a678bff38 --- /dev/null +++ b/packages/kbn-optimizer/limits.yml @@ -0,0 +1,100 @@ +pageLoadAssetSize: + advancedSettings: 27596 + alerts: 106936 + apm: 64385 + apmOss: 18996 + beatsManagement: 188135 + bfetch: 41874 + canvas: 1066647 + charts: 159211 + cloud: 21076 + console: 46091 + core: 692106 + crossClusterReplication: 65408 + dashboard: 374194 + dashboardEnhanced: 65646 + dashboardMode: 22716 + data: 1170713 + dataEnhanced: 50420 + devTools: 38637 + discover: 105145 + discoverEnhanced: 42730 + embeddable: 312874 + embeddableEnhanced: 41145 + enterpriseSearch: 35741 + esUiShared: 326654 + expressions: 224136 + features: 31211 + fileUpload: 24717 + globalSearch: 43548 + globalSearchBar: 62888 + globalSearchProviders: 25554 + graph: 31504 + grokdebugger: 26779 + home: 41661 + indexLifecycleManagement: 107090 + indexManagement: 140608 + indexPatternManagement: 154222 + infra: 197873 + ingestManager: 415829 + ingestPipelines: 58003 + inputControlVis: 172675 + inspector: 148711 + kibanaLegacy: 107711 + kibanaOverview: 56279 + kibanaReact: 161921 + kibanaUtils: 198829 + lens: 96624 + licenseManagement: 41817 + licensing: 39008 + lists: 183665 + logstash: 53548 + management: 46112 + maps: 183610 + mapsLegacy: 116817 + mapsLegacyLicensing: 20214 + ml: 82187 + monitoring: 268612 + navigation: 37269 + newsfeed: 42228 + observability: 89709 + painlessLab: 179748 + regionMap: 66098 + remoteClusters: 51327 + reporting: 183418 + rollup: 97204 + savedObjects: 108518 + savedObjectsManagement: 100503 + searchprofiler: 67080 + security: 189428 + securityOss: 30806 + securitySolution: 622387 + share: 99061 + snapshotRestore: 79032 + spaces: 387915 + telemetry: 91832 + telemetryManagementSection: 52443 + tileMap: 65337 + timelion: 29920 + transform: 41007 + triggersActionsUi: 170001 + uiActions: 97717 + uiActionsEnhanced: 349511 + upgradeAssistant: 81241 + uptime: 40825 + urlDrilldown: 34174 + urlForwarding: 32579 + usageCollection: 39762 + visDefaultEditor: 50178 + visTypeMarkdown: 30896 + visTypeMetric: 42790 + visTypeTable: 94934 + visTypeTagcloud: 37575 + visTypeTimelion: 51933 + visTypeTimeseries: 155203 + visTypeVega: 153573 + visTypeVislib: 242838 + visTypeXy: 20255 + visualizations: 295025 + visualize: 57431 + watcher: 43598 diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index 52f9349aec696..c9e414dbc5177 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -14,6 +14,7 @@ "@babel/core": "^7.11.6", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", + "@kbn/std": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", "autoprefixer": "^9.7.4", "babel-loader": "^8.0.6", @@ -22,6 +23,7 @@ "cpy": "^8.0.0", "core-js": "^3.6.5", "css-loader": "^3.4.2", + "dedent": "^0.7.0", "del": "^5.1.0", "execa": "^4.0.2", "file-loader": "^4.2.0", @@ -38,6 +40,7 @@ "postcss-loader": "^3.0.0", "raw-loader": "^3.1.0", "rxjs": "^6.5.5", + "js-yaml": "^3.14.0", "sass-loader": "^8.0.2", "source-map-support": "^0.5.19", "style-loader": "^1.1.3", diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts index dcfb56be66efd..a822773052cae 100644 --- a/packages/kbn-optimizer/src/cli.ts +++ b/packages/kbn-optimizer/src/cli.ts @@ -22,12 +22,14 @@ import 'source-map-support/register'; import Path from 'path'; import { REPO_ROOT } from '@kbn/utils'; +import { lastValueFrom } from '@kbn/std'; import { run, createFlagError, CiStatsReporter } from '@kbn/dev-utils'; import { logOptimizerState } from './log_optimizer_state'; import { OptimizerConfig } from './optimizer'; import { reportOptimizerStats } from './report_optimizer_stats'; import { runOptimizer } from './run_optimizer'; +import { validateLimitsForAllBundles, updateBundleLimits } from './limits'; run( async ({ log, flags }) => { @@ -93,14 +95,24 @@ run( throw createFlagError('expected --filter to be one or more strings'); } + const validateLimits = flags['validate-limits'] ?? false; + if (typeof validateLimits !== 'boolean') { + throw createFlagError('expected --validate-limits to have no value'); + } + + const updateLimits = flags['update-limits'] ?? false; + if (typeof updateLimits !== 'boolean') { + throw createFlagError('expected --update-limits to have no value'); + } + const config = OptimizerConfig.create({ repoRoot: REPO_ROOT, watch, maxWorkerCount, - oss, - dist, + oss: oss && !(validateLimits || updateLimits), + dist: dist || updateLimits, cache, - examples, + examples: examples && !(validateLimits || updateLimits), profileWebpack, extraPluginScanDirs, inspectWorkers, @@ -108,6 +120,11 @@ run( filter, }); + if (validateLimits) { + validateLimitsForAllBundles(log, config); + return; + } + let update$ = runOptimizer(config); if (reportStats) { @@ -120,7 +137,11 @@ run( update$ = update$.pipe(reportOptimizerStats(reporter, config, log)); } - await update$.pipe(logOptimizerState(log, config)).toPromise(); + await lastValueFrom(update$.pipe(logOptimizerState(log, config))); + + if (updateLimits) { + updateBundleLimits(log, config); + } }, { flags: { @@ -134,6 +155,8 @@ run( 'profile', 'inspect-workers', 'report-stats', + 'validate-limits', + 'update-limits', ], string: ['workers', 'scan-dir', 'filter'], default: { @@ -152,10 +175,12 @@ run( --no-cache disable the cache --filter comma-separated list of bundle id filters, results from multiple flags are merged, * and ! are supported --no-examples don't build the example plugins - --dist create bundles that are suitable for inclusion in the Kibana distributable + --dist create bundles that are suitable for inclusion in the Kibana distributable, enabled when running with --update-limits --scan-dir add a directory to the list of directories scanned for plugins (specify as many times as necessary) --no-inspect-workers when inspecting the parent process, don't inspect the workers --report-stats attempt to report stats about this execution of the build to the kibana-ci-stats service using this name + --validate-limits validate the limits.yml config to ensure that there are limits defined for every bundle + --update-limits run a build and rewrite the limits file to include the current bundle sizes +5kb `, }, } diff --git a/packages/kbn-optimizer/src/common/event_stream_helpers.test.ts b/packages/kbn-optimizer/src/common/event_stream_helpers.test.ts index 7458fa13eccb3..8f5a01a4e4a5d 100644 --- a/packages/kbn-optimizer/src/common/event_stream_helpers.test.ts +++ b/packages/kbn-optimizer/src/common/event_stream_helpers.test.ts @@ -18,20 +18,21 @@ */ import * as Rx from 'rxjs'; -import { toArray, take } from 'rxjs/operators'; +import { take } from 'rxjs/operators'; +import { allValuesFrom } from './rxjs_helpers'; import { summarizeEventStream } from './event_stream_helpers'; it('emits each state with each event, ignoring events when summarizer returns undefined', async () => { const event$ = Rx.of(1, 2, 3, 4, 5); const initial = 0; - const values = await summarizeEventStream(event$, initial, (state, event) => { - if (event % 2) { - return state + event; - } - }) - .pipe(toArray()) - .toPromise(); + const values = await allValuesFrom( + summarizeEventStream(event$, initial, (state, event) => { + if (event % 2) { + return state + event; + } + }) + ); expect(values).toMatchInlineSnapshot(` Array [ @@ -57,15 +58,15 @@ it('emits each state with each event, ignoring events when summarizer returns un it('interleaves injected events when source is synchronous', async () => { const event$ = Rx.of(1, 7); const initial = 0; - const values = await summarizeEventStream(event$, initial, (state, event, injectEvent) => { - if (event < 5) { - injectEvent(event + 2); - } + const values = await allValuesFrom( + summarizeEventStream(event$, initial, (state, event, injectEvent) => { + if (event < 5) { + injectEvent(event + 2); + } - return state + event; - }) - .pipe(toArray()) - .toPromise(); + return state + event; + }) + ); expect(values).toMatchInlineSnapshot(` Array [ @@ -95,15 +96,15 @@ it('interleaves injected events when source is synchronous', async () => { it('interleaves injected events when source is asynchronous', async () => { const event$ = Rx.of(1, 7, Rx.asyncScheduler); const initial = 0; - const values = await summarizeEventStream(event$, initial, (state, event, injectEvent) => { - if (event < 5) { - injectEvent(event + 2); - } + const values = await allValuesFrom( + summarizeEventStream(event$, initial, (state, event, injectEvent) => { + if (event < 5) { + injectEvent(event + 2); + } - return state + event; - }) - .pipe(toArray()) - .toPromise(); + return state + event; + }) + ); expect(values).toMatchInlineSnapshot(` Array [ @@ -133,17 +134,17 @@ it('interleaves injected events when source is asynchronous', async () => { it('interleaves mulitple injected events in order', async () => { const event$ = Rx.of(1); const initial = 0; - const values = await summarizeEventStream(event$, initial, (state, event, injectEvent) => { - if (event < 10) { - injectEvent(10); - injectEvent(20); - injectEvent(30); - } - - return state + event; - }) - .pipe(toArray()) - .toPromise(); + const values = await allValuesFrom( + summarizeEventStream(event$, initial, (state, event, injectEvent) => { + if (event < 10) { + injectEvent(10); + injectEvent(20); + injectEvent(30); + } + + return state + event; + }) + ); expect(values).toMatchInlineSnapshot(` Array [ @@ -179,9 +180,9 @@ it('stops an infinite stream when unsubscribed', async () => { return prev + event; }); - const values = await summarizeEventStream(event$, initial, summarize) - .pipe(take(11), toArray()) - .toPromise(); + const values = await allValuesFrom( + summarizeEventStream(event$, initial, summarize).pipe(take(11)) + ); expect(values).toMatchInlineSnapshot(` Array [ diff --git a/packages/kbn-optimizer/src/common/rxjs_helpers.test.ts b/packages/kbn-optimizer/src/common/rxjs_helpers.test.ts index dda66c999b8f1..457da9290bbd0 100644 --- a/packages/kbn-optimizer/src/common/rxjs_helpers.test.ts +++ b/packages/kbn-optimizer/src/common/rxjs_helpers.test.ts @@ -19,6 +19,7 @@ import * as Rx from 'rxjs'; import { toArray, map } from 'rxjs/operators'; +import { lastValueFrom } from '@kbn/std'; import { pipeClosure, debounceTimeBuffer, maybeMap, maybe } from './rxjs_helpers'; @@ -36,21 +37,21 @@ describe('pipeClosure()', () => { toArray() ); - await expect(foo$.toPromise()).resolves.toMatchInlineSnapshot(` + await expect(lastValueFrom(foo$)).resolves.toMatchInlineSnapshot(` Array [ 1, 2, 3, ] `); - await expect(foo$.toPromise()).resolves.toMatchInlineSnapshot(` + await expect(lastValueFrom(foo$)).resolves.toMatchInlineSnapshot(` Array [ 2, 4, 6, ] `); - await expect(foo$.toPromise()).resolves.toMatchInlineSnapshot(` + await expect(lastValueFrom(foo$)).resolves.toMatchInlineSnapshot(` Array [ 3, 6, @@ -64,7 +65,7 @@ describe('maybe()', () => { it('filters out undefined values from the stream', async () => { const foo$ = Rx.of(1, undefined, 2, undefined, 3).pipe(maybe(), toArray()); - await expect(foo$.toPromise()).resolves.toEqual([1, 2, 3]); + await expect(lastValueFrom(foo$)).resolves.toEqual([1, 2, 3]); }); }); @@ -75,7 +76,7 @@ describe('maybeMap()', () => { toArray() ); - await expect(foo$.toPromise()).resolves.toEqual([1, 3, 5]); + await expect(lastValueFrom(foo$)).resolves.toEqual([1, 3, 5]); }); }); diff --git a/packages/kbn-optimizer/src/common/rxjs_helpers.ts b/packages/kbn-optimizer/src/common/rxjs_helpers.ts index c6385c22518aa..49bf2d8f145dd 100644 --- a/packages/kbn-optimizer/src/common/rxjs_helpers.ts +++ b/packages/kbn-optimizer/src/common/rxjs_helpers.ts @@ -18,7 +18,8 @@ */ import * as Rx from 'rxjs'; -import { mergeMap, tap, debounceTime, map } from 'rxjs/operators'; +import { mergeMap, tap, debounceTime, map, toArray } from 'rxjs/operators'; +import { firstValueFrom } from '@kbn/std'; type Operator = (source: Rx.Observable) => Rx.Observable; type MapFn = (item: T1, index: number) => T2; @@ -73,3 +74,6 @@ export const debounceTimeBuffer = (ms: number) => }) ); }); + +export const allValuesFrom = (observable: Rx.Observable) => + firstValueFrom(observable.pipe(toArray())); diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts index 549e4b13a4ac0..c522ff770d369 100644 --- a/packages/kbn-optimizer/src/index.ts +++ b/packages/kbn-optimizer/src/index.ts @@ -22,3 +22,4 @@ export * from './run_optimizer'; export * from './log_optimizer_state'; export * from './report_optimizer_stats'; export * from './node'; +export * from './limits'; diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index 038beca703720..cb5bb1e8fc529 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -57,6 +57,7 @@ OptimizerConfig { "cache": true, "dist": false, "inspectWorkers": false, + "limits": "", "maxWorkerCount": 1, "plugins": Array [ Object { diff --git a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts index de3838eb92975..a89f84e5c543d 100644 --- a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts @@ -24,10 +24,18 @@ import { inspect } from 'util'; import cpy from 'cpy'; import del from 'del'; -import { toArray, tap, filter } from 'rxjs/operators'; +import { tap, filter } from 'rxjs/operators'; import { REPO_ROOT } from '@kbn/utils'; import { ToolingLog } from '@kbn/dev-utils'; -import { runOptimizer, OptimizerConfig, OptimizerUpdate, logOptimizerState } from '@kbn/optimizer'; +import { + runOptimizer, + OptimizerConfig, + OptimizerUpdate, + logOptimizerState, + readLimits, +} from '@kbn/optimizer'; + +import { allValuesFrom } from '../common'; const TMP_DIR = Path.resolve(__dirname, '../__fixtures__/__tmp__'); const MOCK_REPO_SRC = Path.resolve(__dirname, '../__fixtures__/mock_repo'); @@ -72,15 +80,17 @@ it('builds expected bundles, saves bundle counts to metadata', async () => { dist: false, }); + expect(config.limits).toEqual(readLimits()); + (config as any).limits = ''; + expect(config).toMatchSnapshot('OptimizerConfig'); - const msgs = await runOptimizer(config) - .pipe( + const msgs = await allValuesFrom( + runOptimizer(config).pipe( logOptimizerState(log, config), - filter((x) => x.event?.type !== 'worker stdio'), - toArray() + filter((x) => x.event?.type !== 'worker stdio') ) - .toPromise(); + ); const assert = (statement: string, truth: boolean, altStates?: OptimizerUpdate[]) => { if (!truth) { @@ -199,17 +209,16 @@ it('uses cache on second run and exist cleanly', async () => { dist: false, }); - const msgs = await runOptimizer(config) - .pipe( + const msgs = await allValuesFrom( + runOptimizer(config).pipe( tap((state) => { if (state.event?.type === 'worker stdio') { // eslint-disable-next-line no-console console.log('worker', state.event.stream, state.event.line); } - }), - toArray() + }) ) - .toPromise(); + ); expect(msgs.map((m) => m.state.phase)).toMatchInlineSnapshot(` Array [ @@ -231,7 +240,7 @@ it('prepares assets for distribution', async () => { dist: true, }); - await runOptimizer(config).pipe(logOptimizerState(log, config), toArray()).toPromise(); + await allValuesFrom(runOptimizer(config).pipe(logOptimizerState(log, config))); expectFileMatchesSnapshotWithCompression('plugins/foo/target/public/foo.plugin.js', 'foo bundle'); expectFileMatchesSnapshotWithCompression( diff --git a/packages/kbn-optimizer/src/integration_tests/bundle_cache.test.ts b/packages/kbn-optimizer/src/integration_tests/bundle_cache.test.ts index 48cab508954a0..00e6782128dd9 100644 --- a/packages/kbn-optimizer/src/integration_tests/bundle_cache.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/bundle_cache.test.ts @@ -21,12 +21,11 @@ import Path from 'path'; import cpy from 'cpy'; import del from 'del'; -import { toArray } from 'rxjs/operators'; import { createAbsolutePathSerializer } from '@kbn/dev-utils'; import { getMtimes } from '../optimizer/get_mtimes'; import { OptimizerConfig } from '../optimizer/optimizer_config'; -import { Bundle } from '../common/bundle'; +import { allValuesFrom, Bundle } from '../common'; import { getBundleCacheEvent$ } from '../optimizer/bundle_cache'; const TMP_DIR = Path.resolve(__dirname, '../__fixtures__/__tmp__'); @@ -78,9 +77,7 @@ it('emits "bundle cached" event when everything is updated', async () => { bundleRefExportIds: [], }); - const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) - .pipe(toArray()) - .toPromise(); + const cacheEvents = await allValuesFrom(getBundleCacheEvent$(config, optimizerCacheKey)); expect(cacheEvents).toMatchInlineSnapshot(` Array [ @@ -119,9 +116,7 @@ it('emits "bundle not cached" event when cacheKey is up to date but caching is d bundleRefExportIds: [], }); - const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) - .pipe(toArray()) - .toPromise(); + const cacheEvents = await allValuesFrom(getBundleCacheEvent$(config, optimizerCacheKey)); expect(cacheEvents).toMatchInlineSnapshot(` Array [ @@ -160,9 +155,7 @@ it('emits "bundle not cached" event when optimizerCacheKey is missing', async () bundleRefExportIds: [], }); - const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) - .pipe(toArray()) - .toPromise(); + const cacheEvents = await allValuesFrom(getBundleCacheEvent$(config, optimizerCacheKey)); expect(cacheEvents).toMatchInlineSnapshot(` Array [ @@ -201,9 +194,7 @@ it('emits "bundle not cached" event when optimizerCacheKey is outdated, includes bundleRefExportIds: [], }); - const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) - .pipe(toArray()) - .toPromise(); + const cacheEvents = await allValuesFrom(getBundleCacheEvent$(config, optimizerCacheKey)); expect(cacheEvents).toMatchInlineSnapshot(` Array [ @@ -247,9 +238,7 @@ it('emits "bundle not cached" event when bundleRefExportIds is outdated, include bundleRefExportIds: ['plugin/bar/public'], }); - const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) - .pipe(toArray()) - .toPromise(); + const cacheEvents = await allValuesFrom(getBundleCacheEvent$(config, optimizerCacheKey)); expect(cacheEvents).toMatchInlineSnapshot(` Array [ @@ -292,9 +281,7 @@ it('emits "bundle not cached" event when cacheKey is missing', async () => { bundleRefExportIds: [], }); - const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) - .pipe(toArray()) - .toPromise(); + const cacheEvents = await allValuesFrom(getBundleCacheEvent$(config, optimizerCacheKey)); expect(cacheEvents).toMatchInlineSnapshot(` Array [ @@ -333,9 +320,7 @@ it('emits "bundle not cached" event when cacheKey is outdated', async () => { jest.spyOn(bundle, 'createCacheKey').mockImplementation(() => 'new'); - const cacheEvents = await getBundleCacheEvent$(config, optimizerCacheKey) - .pipe(toArray()) - .toPromise(); + const cacheEvents = await allValuesFrom(getBundleCacheEvent$(config, optimizerCacheKey)); expect(cacheEvents).toMatchInlineSnapshot(` Array [ diff --git a/packages/kbn-optimizer/src/integration_tests/watch_bundles_for_changes.test.ts b/packages/kbn-optimizer/src/integration_tests/watch_bundles_for_changes.test.ts index 176b17c979da9..00f3c780adc0a 100644 --- a/packages/kbn-optimizer/src/integration_tests/watch_bundles_for_changes.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/watch_bundles_for_changes.test.ts @@ -20,6 +20,7 @@ import * as Rx from 'rxjs'; import { map } from 'rxjs/operators'; import ActualWatchpack from 'watchpack'; +import { lastValueFrom } from '@kbn/std'; import { Bundle, ascending } from '../common'; import { watchBundlesForChanges$ } from '../optimizer/watch_bundles_for_changes'; @@ -78,8 +79,8 @@ afterEach(async () => { it('notifies of changes and completes once all bundles have changed', async () => { expect.assertions(18); - const promise = watchBundlesForChanges$(bundleCacheEvent$, Date.now()) - .pipe( + const promise = lastValueFrom( + watchBundlesForChanges$(bundleCacheEvent$, Date.now()).pipe( map((event, i) => { // each time we trigger a change event we get a 'changed detected' event if (i === 0 || i === 2 || i === 4 || i === 6) { @@ -116,7 +117,7 @@ it('notifies of changes and completes once all bundles have changed', async () = } }) ) - .toPromise(); + ); expect(MockWatchPack.mock.instances).toHaveLength(1); const [watcher] = (MockWatchPack.mock.instances as any) as Array>; diff --git a/packages/kbn-optimizer/src/limits.ts b/packages/kbn-optimizer/src/limits.ts new file mode 100644 index 0000000000000..b0fae0901251d --- /dev/null +++ b/packages/kbn-optimizer/src/limits.ts @@ -0,0 +1,103 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Fs from 'fs'; + +import dedent from 'dedent'; +import Yaml from 'js-yaml'; +import { createFailError, ToolingLog } from '@kbn/dev-utils'; + +import { OptimizerConfig, getMetrics, Limits } from './optimizer'; + +const LIMITS_PATH = require.resolve('../limits.yml'); +const DEFAULT_BUDGET = 15000; + +const diff = (a: T[], b: T[]): T[] => a.filter((item) => !b.includes(item)); + +export function readLimits(): Limits { + let yaml; + try { + yaml = Fs.readFileSync(LIMITS_PATH, 'utf8'); + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } + } + + return yaml ? Yaml.safeLoad(yaml) : {}; +} + +export function validateLimitsForAllBundles(log: ToolingLog, config: OptimizerConfig) { + const limitBundleIds = Object.keys(config.limits.pageLoadAssetSize || {}); + const configBundleIds = config.bundles.map((b) => b.id); + + const missingBundleIds = diff(configBundleIds, limitBundleIds); + const extraBundleIds = diff(limitBundleIds, configBundleIds); + + const issues = []; + if (missingBundleIds.length) { + issues.push(`missing: ${missingBundleIds.join(', ')}`); + } + if (extraBundleIds.length) { + issues.push(`extra: ${extraBundleIds.join(', ')}`); + } + if (issues.length) { + throw createFailError( + dedent` + The limits defined in packages/kbn-optimizer/limits.yml are outdated. Please update + this file with a limit (in bytes) for every production bundle. + + ${issues.join('\n ')} + + To automatically update the limits file locally run: + + node scripts/build_kibana_platform_plugins.js --update-limits + + To validate your changes locally run: + + node scripts/build_kibana_platform_plugins.js --validate-limits + ` + '\n' + ); + } + + log.success('limits.yml file valid'); +} + +export function updateBundleLimits(log: ToolingLog, config: OptimizerConfig) { + const metrics = getMetrics(log, config); + + const pageLoadAssetSize: NonNullable = {}; + + for (const metric of metrics.sort((a, b) => a.id.localeCompare(b.id))) { + if (metric.group === 'page load bundle size') { + const existingLimit = config.limits.pageLoadAssetSize?.[metric.id]; + pageLoadAssetSize[metric.id] = + existingLimit != null && existingLimit >= metric.value + ? existingLimit + : metric.value + DEFAULT_BUDGET; + } + } + + const newLimits: Limits = { + pageLoadAssetSize, + }; + + Fs.writeFileSync(LIMITS_PATH, Yaml.safeDump(newLimits)); + log.success(`wrote updated limits to ${LIMITS_PATH}`); +} diff --git a/packages/kbn-optimizer/src/optimizer/get_mtimes.ts b/packages/kbn-optimizer/src/optimizer/get_mtimes.ts index 07777c323637a..b6c3678709880 100644 --- a/packages/kbn-optimizer/src/optimizer/get_mtimes.ts +++ b/packages/kbn-optimizer/src/optimizer/get_mtimes.ts @@ -20,7 +20,8 @@ import Fs from 'fs'; import * as Rx from 'rxjs'; -import { mergeMap, toArray, map, catchError } from 'rxjs/operators'; +import { mergeMap, map, catchError } from 'rxjs/operators'; +import { allValuesFrom } from '../common'; const stat$ = Rx.bindNodeCallback(Fs.stat); @@ -28,20 +29,22 @@ const stat$ = Rx.bindNodeCallback(Fs.stat); * get mtimes of referenced paths concurrently, limit concurrency to 100 */ export async function getMtimes(paths: Iterable) { - return await Rx.from(paths) - .pipe( - // map paths to [path, mtimeMs] entries with concurrency of - // 100 at a time, ignoring missing paths - mergeMap( - (path) => - stat$(path).pipe( - map((stat) => [path, stat.mtimeMs] as const), - catchError((error: any) => (error?.code === 'ENOENT' ? Rx.EMPTY : Rx.throwError(error))) - ), - 100 - ), - toArray(), - map((entries) => new Map(entries)) + return new Map( + await allValuesFrom( + Rx.from(paths).pipe( + // map paths to [path, mtimeMs] entries with concurrency of + // 100 at a time, ignoring missing paths + mergeMap( + (path) => + stat$(path).pipe( + map((stat) => [path, stat.mtimeMs] as const), + catchError((error: any) => + error?.code === 'ENOENT' ? Rx.EMPTY : Rx.throwError(error) + ) + ), + 100 + ) + ) ) - .toPromise(); + ); } diff --git a/packages/kbn-optimizer/src/optimizer/get_output_stats.ts b/packages/kbn-optimizer/src/optimizer/get_output_stats.ts new file mode 100644 index 0000000000000..cc4cd05f42c3f --- /dev/null +++ b/packages/kbn-optimizer/src/optimizer/get_output_stats.ts @@ -0,0 +1,124 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Fs from 'fs'; +import Path from 'path'; + +import { ToolingLog, CiStatsMetrics } from '@kbn/dev-utils'; +import { OptimizerConfig } from './optimizer_config'; + +const flatten = (arr: Array): T[] => + arr.reduce((acc: T[], item) => acc.concat(item), []); + +interface Entry { + relPath: string; + stats: Fs.Stats; +} + +const IGNORED_EXTNAME = ['.map', '.br', '.gz']; + +const getFiles = (dir: string, parent?: string) => + flatten( + Fs.readdirSync(dir).map((name): Entry | Entry[] => { + const absPath = Path.join(dir, name); + const relPath = parent ? Path.join(parent, name) : name; + const stats = Fs.statSync(absPath); + + if (stats.isDirectory()) { + return getFiles(absPath, relPath); + } + + return { + relPath, + stats, + }; + }) + ).filter((file) => { + const filename = Path.basename(file.relPath); + if (filename.startsWith('.')) { + return false; + } + + const ext = Path.extname(filename); + if (IGNORED_EXTNAME.includes(ext)) { + return false; + } + + return true; + }); + +export function getMetrics(log: ToolingLog, config: OptimizerConfig) { + return flatten( + config.bundles.map((bundle) => { + // make the cache read from the cache file since it was likely updated by the worker + bundle.cache.refresh(); + + const outputFiles = getFiles(bundle.outputDir); + const entryName = `${bundle.id}.${bundle.type}.js`; + const entry = outputFiles.find((f) => f.relPath === entryName); + if (!entry) { + throw new Error( + `Unable to find bundle entry named [${entryName}] in [${bundle.outputDir}]` + ); + } + + const chunkPrefix = `${bundle.id}.chunk.`; + const asyncChunks = outputFiles.filter((f) => f.relPath.startsWith(chunkPrefix)); + const miscFiles = outputFiles.filter((f) => f !== entry && !asyncChunks.includes(f)); + + if (asyncChunks.length) { + log.verbose(bundle.id, 'async chunks', asyncChunks); + } + if (miscFiles.length) { + log.verbose(bundle.id, 'misc files', asyncChunks); + } + + const sumSize = (files: Entry[]) => files.reduce((acc: number, f) => acc + f.stats!.size, 0); + + const bundleMetrics: CiStatsMetrics = [ + { + group: `@kbn/optimizer bundle module count`, + id: bundle.id, + value: bundle.cache.getModuleCount() || 0, + }, + { + group: `page load bundle size`, + id: bundle.id, + value: entry.stats!.size, + limit: config.limits.pageLoadAssetSize?.[bundle.id], + limitConfigPath: `packages/kbn-optimizer/limits.yml`, + }, + { + group: `async chunks size`, + id: bundle.id, + value: sumSize(asyncChunks), + }, + { + group: `miscellaneous assets size`, + id: bundle.id, + value: sumSize(miscFiles), + }, + ]; + + log.debug(bundle.id, 'metrics', bundleMetrics); + + return bundleMetrics; + }) + ); +} diff --git a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts index 6edcde56e26de..b92eee0a51fd5 100644 --- a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts +++ b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts @@ -20,12 +20,11 @@ import * as Rx from 'rxjs'; import { REPO_ROOT } from '@kbn/utils'; -import { Update } from '../common'; +import { Update, allValuesFrom } from '../common'; import { OptimizerState } from './optimizer_state'; import { OptimizerConfig } from './optimizer_config'; import { handleOptimizerCompletion } from './handle_optimizer_completion'; -import { toArray } from 'rxjs/operators'; const createUpdate$ = (phase: OptimizerState['phase']) => Rx.of>({ @@ -44,13 +43,12 @@ const config = (watch?: boolean) => repoRoot: REPO_ROOT, watch, }); -const collect = (stream: Rx.Observable): Promise => stream.pipe(toArray()).toPromise(); it('errors if the optimizer completes when in watch mode', async () => { const update$ = createUpdate$('success'); await expect( - collect(update$.pipe(handleOptimizerCompletion(config(true)))) + allValuesFrom(update$.pipe(handleOptimizerCompletion(config(true)))) ).rejects.toThrowErrorMatchingInlineSnapshot( `"optimizer unexpectedly completed when in watch mode"` ); @@ -60,7 +58,7 @@ it('errors if the optimizer completes in phase "issue"', async () => { const update$ = createUpdate$('issue'); await expect( - collect(update$.pipe(handleOptimizerCompletion(config()))) + allValuesFrom(update$.pipe(handleOptimizerCompletion(config()))) ).rejects.toThrowErrorMatchingInlineSnapshot(`"webpack issue"`); }); @@ -68,7 +66,7 @@ it('errors if the optimizer completes in phase "initializing"', async () => { const update$ = createUpdate$('initializing'); await expect( - collect(update$.pipe(handleOptimizerCompletion(config()))) + allValuesFrom(update$.pipe(handleOptimizerCompletion(config()))) ).rejects.toThrowErrorMatchingInlineSnapshot( `"optimizer unexpectedly exit in phase \\"initializing\\""` ); @@ -78,7 +76,7 @@ it('errors if the optimizer completes in phase "reallocating"', async () => { const update$ = createUpdate$('reallocating'); await expect( - collect(update$.pipe(handleOptimizerCompletion(config()))) + allValuesFrom(update$.pipe(handleOptimizerCompletion(config()))) ).rejects.toThrowErrorMatchingInlineSnapshot( `"optimizer unexpectedly exit in phase \\"reallocating\\""` ); @@ -88,7 +86,7 @@ it('errors if the optimizer completes in phase "running"', async () => { const update$ = createUpdate$('running'); await expect( - collect(update$.pipe(handleOptimizerCompletion(config()))) + allValuesFrom(update$.pipe(handleOptimizerCompletion(config()))) ).rejects.toThrowErrorMatchingInlineSnapshot( `"optimizer unexpectedly exit in phase \\"running\\""` ); @@ -98,7 +96,7 @@ it('passes through errors on the source stream', async () => { const error = new Error('foo'); const update$ = Rx.throwError(error); - await expect(collect(update$.pipe(handleOptimizerCompletion(config())))).rejects.toThrowError( - error - ); + await expect( + allValuesFrom(update$.pipe(handleOptimizerCompletion(config()))) + ).rejects.toThrowError(error); }); diff --git a/packages/kbn-optimizer/src/optimizer/index.ts b/packages/kbn-optimizer/src/optimizer/index.ts index 84fd395e98976..77df112b44351 100644 --- a/packages/kbn-optimizer/src/optimizer/index.ts +++ b/packages/kbn-optimizer/src/optimizer/index.ts @@ -25,3 +25,4 @@ export * from './watch_bundles_for_changes'; export * from './run_workers'; export * from './bundle_cache'; export * from './handle_optimizer_completion'; +export * from './get_output_stats'; diff --git a/packages/kbn-optimizer/src/optimizer/observe_stdio.test.ts b/packages/kbn-optimizer/src/optimizer/observe_stdio.test.ts index 9bf8f9db1fe45..a7c07358fa6d6 100644 --- a/packages/kbn-optimizer/src/optimizer/observe_stdio.test.ts +++ b/packages/kbn-optimizer/src/optimizer/observe_stdio.test.ts @@ -19,7 +19,7 @@ import { Readable } from 'stream'; -import { toArray } from 'rxjs/operators'; +import { allValuesFrom } from '../common'; import { observeStdio$ } from './observe_stdio'; @@ -27,18 +27,18 @@ it('notifies on every line, uncluding partial content at the end without a newli const chunks = [`foo\nba`, `r\nb`, `az`]; await expect( - observeStdio$( - new Readable({ - read() { - this.push(chunks.shift()!); - if (!chunks.length) { - this.push(null); - } - }, - }) + allValuesFrom( + observeStdio$( + new Readable({ + read() { + this.push(chunks.shift()!); + if (!chunks.length) { + this.push(null); + } + }, + }) + ) ) - .pipe(toArray()) - .toPromise() ).resolves.toMatchInlineSnapshot(` Array [ "foo", diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts index fd887e8c2c012..948ba520931e5 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts @@ -22,6 +22,7 @@ jest.mock('./kibana_platform_plugins.ts'); jest.mock('./get_plugin_bundles.ts'); jest.mock('../common/theme_tags.ts'); jest.mock('./filter_by_id.ts'); +jest.mock('../limits.ts'); jest.mock('os', () => { const realOs = jest.requireActual('os'); @@ -385,6 +386,7 @@ describe('OptimizerConfig::create()', () => { .findKibanaPlatformPlugins; const getPluginBundles: jest.Mock = jest.requireMock('./get_plugin_bundles.ts').getPluginBundles; const filterById: jest.Mock = jest.requireMock('./filter_by_id.ts').filterById; + const readLimits: jest.Mock = jest.requireMock('../limits.ts').readLimits; beforeEach(() => { if ('mock' in OptimizerConfig.parseOptions) { @@ -398,6 +400,7 @@ describe('OptimizerConfig::create()', () => { findKibanaPlatformPlugins.mockReturnValue(Symbol('new platform plugins')); getPluginBundles.mockReturnValue([Symbol('bundle1'), Symbol('bundle2')]); filterById.mockReturnValue(Symbol('filtered bundles')); + readLimits.mockReturnValue(Symbol('limits')); jest.spyOn(OptimizerConfig, 'parseOptions').mockImplementation((): { [key in keyof ParsedOptions]: any; @@ -429,6 +432,7 @@ describe('OptimizerConfig::create()', () => { "cache": Symbol(parsed cache), "dist": Symbol(parsed dist), "inspectWorkers": Symbol(parsed inspect workers), + "limits": Symbol(limits), "maxWorkerCount": Symbol(parsed max worker count), "plugins": Symbol(new platform plugins), "profileWebpack": Symbol(parsed profile webpack), diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index b1ab1ebfe49f2..b685d6ea01591 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -32,6 +32,13 @@ import { import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins'; import { getPluginBundles } from './get_plugin_bundles'; import { filterById } from './filter_by_id'; +import { readLimits } from '../limits'; + +export interface Limits { + pageLoadAssetSize?: { + [id: string]: number | undefined; + }; +} function pickMaxWorkerCount(dist: boolean) { // don't break if cpus() returns nothing, or an empty array @@ -238,7 +245,8 @@ export class OptimizerConfig { options.maxWorkerCount, options.dist, options.profileWebpack, - options.themeTags + options.themeTags, + readLimits() ); } @@ -252,7 +260,8 @@ export class OptimizerConfig { public readonly maxWorkerCount: number, public readonly dist: boolean, public readonly profileWebpack: boolean, - public readonly themeTags: ThemeTags + public readonly themeTags: ThemeTags, + public readonly limits: Limits ) {} getWorkerConfig(optimizerCacheKey: unknown): WorkerConfig { diff --git a/packages/kbn-optimizer/src/report_optimizer_stats.ts b/packages/kbn-optimizer/src/report_optimizer_stats.ts index eff2bce0b827e..a0f59a3505e30 100644 --- a/packages/kbn-optimizer/src/report_optimizer_stats.ts +++ b/packages/kbn-optimizer/src/report_optimizer_stats.ts @@ -17,136 +17,41 @@ * under the License. */ -import Fs from 'fs'; -import Path from 'path'; - import { materialize, mergeMap, dematerialize } from 'rxjs/operators'; -import { CiStatsReporter, CiStatsMetrics, ToolingLog } from '@kbn/dev-utils'; +import { CiStatsReporter, ToolingLog } from '@kbn/dev-utils'; import { OptimizerUpdate$ } from './run_optimizer'; -import { OptimizerState, OptimizerConfig } from './optimizer'; +import { OptimizerConfig, getMetrics } from './optimizer'; import { pipeClosure } from './common'; -const flatten = (arr: Array): T[] => - arr.reduce((acc: T[], item) => acc.concat(item), []); - -interface Entry { - relPath: string; - stats: Fs.Stats; -} - -const IGNORED_EXTNAME = ['.map', '.br', '.gz']; - -const getFiles = (dir: string, parent?: string) => - flatten( - Fs.readdirSync(dir).map((name): Entry | Entry[] => { - const absPath = Path.join(dir, name); - const relPath = parent ? Path.join(parent, name) : name; - const stats = Fs.statSync(absPath); - - if (stats.isDirectory()) { - return getFiles(absPath, relPath); - } - - return { - relPath, - stats, - }; - }) - ).filter((file) => { - const filename = Path.basename(file.relPath); - if (filename.startsWith('.')) { - return false; - } - - const ext = Path.extname(filename); - if (IGNORED_EXTNAME.includes(ext)) { - return false; - } - - return true; - }); - export function reportOptimizerStats( reporter: CiStatsReporter, config: OptimizerConfig, log: ToolingLog ) { - return pipeClosure((update$: OptimizerUpdate$) => { - let lastState: OptimizerState | undefined; - return update$.pipe( + return pipeClosure((update$: OptimizerUpdate$) => + update$.pipe( materialize(), mergeMap(async (n) => { - if (n.kind === 'N' && n.value?.state) { - lastState = n.value?.state; - } - - if (n.kind === 'C' && lastState) { - await reporter.metrics( - flatten( - config.bundles.map((bundle) => { - // make the cache read from the cache file since it was likely updated by the worker - bundle.cache.refresh(); - - const outputFiles = getFiles(bundle.outputDir); - const entryName = `${bundle.id}.${bundle.type}.js`; - const entry = outputFiles.find((f) => f.relPath === entryName); - if (!entry) { - throw new Error( - `Unable to find bundle entry named [${entryName}] in [${bundle.outputDir}]` - ); - } - - const chunkPrefix = `${bundle.id}.chunk.`; - const asyncChunks = outputFiles.filter((f) => f.relPath.startsWith(chunkPrefix)); - const miscFiles = outputFiles.filter( - (f) => f !== entry && !asyncChunks.includes(f) - ); - - if (asyncChunks.length) { - log.verbose(bundle.id, 'async chunks', asyncChunks); - } - if (miscFiles.length) { - log.verbose(bundle.id, 'misc files', asyncChunks); - } - - const sumSize = (files: Entry[]) => - files.reduce((acc: number, f) => acc + f.stats!.size, 0); - - const metrics: CiStatsMetrics = [ - { - group: `@kbn/optimizer bundle module count`, - id: bundle.id, - value: bundle.cache.getModuleCount() || 0, - }, - { - group: `page load bundle size`, - id: bundle.id, - value: entry.stats!.size, - }, - { - group: `async chunks size`, - id: bundle.id, - value: sumSize(asyncChunks), - }, - { - group: `miscellaneous assets size`, - id: bundle.id, - value: sumSize(miscFiles), - }, - ]; - - log.info(bundle.id, 'metrics', metrics); - - return metrics; - }) - ) - ); + if (n.kind === 'C') { + const metrics = getMetrics(log, config); + + await reporter.metrics(metrics); + + for (const metric of metrics) { + if (metric.limit != null && metric.value > metric.limit) { + const value = metric.value.toLocaleString(); + const limit = metric.limit.toLocaleString(); + log.warning( + `Metric [${metric.group}] for [${metric.id}] of [${value}] over the limit of [${limit}]` + ); + } + } } return n; }), dematerialize() - ); - }); + ) + ); } diff --git a/packages/kbn-optimizer/src/worker/run_compilers.ts b/packages/kbn-optimizer/src/worker/run_compilers.ts index d78eb8214f607..0024d2801d173 100644 --- a/packages/kbn-optimizer/src/worker/run_compilers.ts +++ b/packages/kbn-optimizer/src/worker/run_compilers.ts @@ -182,7 +182,7 @@ const observeCompiler = ( ); bundle.cache.set({ - bundleRefExportIds, + bundleRefExportIds: bundleRefExportIds.sort(ascending((p) => p)), optimizerCacheKey: workerConfig.optimizerCacheKey, cacheKey: bundle.createCacheKey(files, mtimes), moduleCount, diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 48dba22505232..2e50f4214beb4 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -196,6 +196,7 @@ function help() { --oss Do not include the x-pack when running command. --skip-kibana-plugins Filter all plugins in ./plugins and ../kibana-extra when running command. --no-cache Disable the bootstrap cache + --no-validate Disable the bootstrap yarn.lock validation --verbose Set log level to verbose --debug Set log level to debug --quiet Set log level to error @@ -222,9 +223,10 @@ async function run(argv) { i: 'include' }, default: { - cache: true + cache: true, + validate: true }, - boolean: ['prefer-offline', 'frozen-lockfile', 'cache'] + boolean: ['prefer-offline', 'frozen-lockfile', 'cache', 'validate'] }); const args = options._; @@ -8998,7 +9000,11 @@ const BootstrapCommand = { } const yarnLock = await Object(_utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__["readYarnLock"])(kbn); - await Object(_utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__["validateYarnLock"])(kbn, yarnLock); + + if (options.validate) { + await Object(_utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__["validateYarnLock"])(kbn, yarnLock); + } + await Object(_utils_link_project_executables__WEBPACK_IMPORTED_MODULE_0__["linkProjectExecutables"])(projects, projectGraph); /** * At the end of the bootstrapping process we call all `kbn:bootstrap` scripts diff --git a/packages/kbn-pm/src/cli.ts b/packages/kbn-pm/src/cli.ts index 816e84c13bbe9..92ddf3d957cd5 100644 --- a/packages/kbn-pm/src/cli.ts +++ b/packages/kbn-pm/src/cli.ts @@ -47,6 +47,7 @@ function help() { --oss Do not include the x-pack when running command. --skip-kibana-plugins Filter all plugins in ./plugins and ../kibana-extra when running command. --no-cache Disable the bootstrap cache + --no-validate Disable the bootstrap yarn.lock validation --verbose Set log level to verbose --debug Set log level to debug --quiet Set log level to error @@ -80,8 +81,9 @@ export async function run(argv: string[]) { }, default: { cache: true, + validate: true, }, - boolean: ['prefer-offline', 'frozen-lockfile', 'cache'], + boolean: ['prefer-offline', 'frozen-lockfile', 'cache', 'validate'], }); const args = options._; diff --git a/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap b/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap deleted file mode 100644 index be146d710c87a..0000000000000 --- a/packages/kbn-pm/src/commands/__snapshots__/bootstrap.test.ts.snap +++ /dev/null @@ -1,159 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`calls "kbn:bootstrap" scripts and links executables after installing deps: link bins 1`] = ` -Array [ - Array [ - Map { - "kibana" => Project { - "allDependencies": Object { - "bar": "1.0.0", - }, - "devDependencies": Object {}, - "isWorkspaceProject": false, - "isWorkspaceRoot": true, - "json": Object { - "dependencies": Object { - "bar": "1.0.0", - }, - "name": "kibana", - "version": "1.0.0", - "workspaces": Object { - "packages": Array [ - "packages/*", - ], - }, - }, - "nodeModulesLocation": "/packages/kbn-pm/src/commands/node_modules", - "packageJsonLocation": "/packages/kbn-pm/src/commands/package.json", - "path": "/packages/kbn-pm/src/commands", - "productionDependencies": Object { - "bar": "1.0.0", - }, - "scripts": Object {}, - "targetLocation": "/packages/kbn-pm/src/commands/target", - "version": "1.0.0", - }, - "bar" => Project { - "allDependencies": Object {}, - "devDependencies": Object {}, - "isWorkspaceProject": false, - "isWorkspaceRoot": false, - "json": Object { - "name": "bar", - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "version": "1.0.0", - }, - "nodeModulesLocation": "/packages/kbn-pm/src/commands/packages/bar/node_modules", - "packageJsonLocation": "/packages/kbn-pm/src/commands/packages/bar/package.json", - "path": "/packages/kbn-pm/src/commands/packages/bar", - "productionDependencies": Object {}, - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "targetLocation": "/packages/kbn-pm/src/commands/packages/bar/target", - "version": "1.0.0", - }, - }, - Map { - "kibana" => Array [ - Project { - "allDependencies": Object {}, - "devDependencies": Object {}, - "isWorkspaceProject": false, - "isWorkspaceRoot": false, - "json": Object { - "name": "bar", - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "version": "1.0.0", - }, - "nodeModulesLocation": "/packages/kbn-pm/src/commands/packages/bar/node_modules", - "packageJsonLocation": "/packages/kbn-pm/src/commands/packages/bar/package.json", - "path": "/packages/kbn-pm/src/commands/packages/bar", - "productionDependencies": Object {}, - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "targetLocation": "/packages/kbn-pm/src/commands/packages/bar/target", - "version": "1.0.0", - }, - ], - "bar" => Array [], - }, - ], -] -`; - -exports[`calls "kbn:bootstrap" scripts and links executables after installing deps: script 1`] = ` -Array [ - Array [ - Object { - "args": Array [], - "debug": undefined, - "pkg": Project { - "allDependencies": Object {}, - "devDependencies": Object {}, - "isWorkspaceProject": false, - "isWorkspaceRoot": false, - "json": Object { - "name": "bar", - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "version": "1.0.0", - }, - "nodeModulesLocation": "/packages/kbn-pm/src/commands/packages/bar/node_modules", - "packageJsonLocation": "/packages/kbn-pm/src/commands/packages/bar/package.json", - "path": "/packages/kbn-pm/src/commands/packages/bar", - "productionDependencies": Object {}, - "scripts": Object { - "kbn:bootstrap": "node ./bar.js", - }, - "targetLocation": "/packages/kbn-pm/src/commands/packages/bar/target", - "version": "1.0.0", - }, - "script": "kbn:bootstrap", - }, - ], -] -`; - -exports[`does not run installer if no deps in package: install in dir 1`] = ` -Array [ - Array [ - "/packages/kbn-pm/src/commands", - Array [], - ], -] -`; - -exports[`handles "frozen-lockfile": install in dir 1`] = ` -Array [ - Array [ - "/packages/kbn-pm/src/commands", - Array [ - "--frozen-lockfile", - ], - ], -] -`; - -exports[`handles dependencies of dependencies: install in dir 1`] = ` -Array [ - Array [ - "/packages/kbn-pm/src/commands", - Array [], - ], - Array [ - "/packages/kbn-pm/src/commands/packages/bar", - Array [], - ], - Array [ - "/packages/kbn-pm/src/commands/packages/foo", - Array [], - ], -] -`; diff --git a/packages/kbn-pm/src/commands/bootstrap.test.ts b/packages/kbn-pm/src/commands/bootstrap.test.ts deleted file mode 100644 index 956c4e72933ba..0000000000000 --- a/packages/kbn-pm/src/commands/bootstrap.test.ts +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock('../utils/scripts'); -jest.mock('../utils/link_project_executables'); -jest.mock('../utils/validate_yarn_lock'); - -import { resolve } from 'path'; - -import { ToolingLogCollectingWriter } from '@kbn/dev-utils/tooling_log'; - -import { absolutePathSnapshotSerializer, stripAnsiSnapshotSerializer } from '../test_helpers'; -import { linkProjectExecutables } from '../utils/link_project_executables'; -import { IPackageJson } from '../utils/package_json'; -import { Project } from '../utils/project'; -import { buildProjectGraph } from '../utils/projects'; -import { installInDir, runScriptInPackageStreaming, yarnWorkspacesInfo } from '../utils/scripts'; -import { BootstrapCommand } from './bootstrap'; -import { Kibana } from '../utils/kibana'; -import { log } from '../utils/log'; - -const mockInstallInDir = installInDir as jest.Mock; -const mockRunScriptInPackageStreaming = runScriptInPackageStreaming as jest.Mock; -const mockLinkProjectExecutables = linkProjectExecutables as jest.Mock; -const mockYarnWorkspacesInfo = yarnWorkspacesInfo as jest.Mock; - -const logWriter = new ToolingLogCollectingWriter('debug'); -log.setLogLevel('silent'); -log.setWriters([logWriter]); -beforeEach(() => { - logWriter.messages.length = 0; -}); - -const createProject = (packageJson: IPackageJson, path = '.') => { - const project = new Project( - { - name: 'kibana', - version: '1.0.0', - ...packageJson, - }, - resolve(__dirname, path) - ); - - if (packageJson.workspaces) { - project.isWorkspaceRoot = true; - } - - return project; -}; -expect.addSnapshotSerializer(absolutePathSnapshotSerializer); -expect.addSnapshotSerializer(stripAnsiSnapshotSerializer); - -beforeEach(() => { - mockYarnWorkspacesInfo.mockResolvedValue({}); -}); - -afterEach(() => { - jest.resetAllMocks(); - jest.restoreAllMocks(); -}); - -test('handles dependencies of dependencies', async () => { - const kibana = createProject({ - dependencies: { - bar: '1.0.0', - }, - workspaces: { - packages: ['packages/*'], - }, - }); - const foo = createProject( - { - dependencies: { - bar: 'link:../bar', - }, - name: 'foo', - }, - 'packages/foo' - ); - const bar = createProject( - { - dependencies: { - baz: 'link:../baz', - }, - name: 'bar', - }, - 'packages/bar' - ); - const baz = createProject( - { - name: 'baz', - }, - 'packages/baz' - ); - - const projects = new Map([ - ['kibana', kibana], - ['foo', foo], - ['bar', bar], - ['baz', baz], - ]); - const kbn = new Kibana(projects); - const projectGraph = buildProjectGraph(projects); - - await BootstrapCommand.run(projects, projectGraph, { - extraArgs: [], - options: {}, - rootPath: '', - kbn, - }); - - expect(mockInstallInDir.mock.calls).toMatchSnapshot('install in dir'); - expect(logWriter.messages).toMatchInlineSnapshot(` - Array [ - info [kibana] running yarn, - "", - "", - info [bar] running yarn, - "", - "", - info [foo] running yarn, - "", - "", - ] - `); -}); - -test('does not run installer if no deps in package', async () => { - const kibana = createProject({ - dependencies: { - bar: '1.0.0', - }, - workspaces: { - packages: ['packages/*'], - }, - }); - // bar has no dependencies - const bar = createProject( - { - name: 'bar', - }, - 'packages/bar' - ); - - const projects = new Map([ - ['kibana', kibana], - ['bar', bar], - ]); - const kbn = new Kibana(projects); - const projectGraph = buildProjectGraph(projects); - - await BootstrapCommand.run(projects, projectGraph, { - extraArgs: [], - options: {}, - rootPath: '', - kbn, - }); - - expect(mockInstallInDir.mock.calls).toMatchSnapshot('install in dir'); - expect(logWriter.messages).toMatchInlineSnapshot(` - Array [ - info [kibana] running yarn, - "", - "", - ] - `); -}); - -test('handles "frozen-lockfile"', async () => { - const kibana = createProject({ - dependencies: { - foo: '2.2.0', - }, - workspaces: { - packages: ['packages/*'], - }, - }); - - const projects = new Map([['kibana', kibana]]); - const kbn = new Kibana(projects); - const projectGraph = buildProjectGraph(projects); - - await BootstrapCommand.run(projects, projectGraph, { - extraArgs: [], - options: { - 'frozen-lockfile': true, - }, - rootPath: '', - kbn, - }); - - expect(mockInstallInDir.mock.calls).toMatchSnapshot('install in dir'); -}); - -test('calls "kbn:bootstrap" scripts and links executables after installing deps', async () => { - const kibana = createProject({ - dependencies: { - bar: '1.0.0', - }, - workspaces: { - packages: ['packages/*'], - }, - }); - const bar = createProject( - { - name: 'bar', - scripts: { - 'kbn:bootstrap': 'node ./bar.js', - }, - }, - 'packages/bar' - ); - - const projects = new Map([ - ['kibana', kibana], - ['bar', bar], - ]); - const kbn = new Kibana(projects); - const projectGraph = buildProjectGraph(projects); - - await BootstrapCommand.run(projects, projectGraph, { - extraArgs: [], - options: {}, - rootPath: '', - kbn, - }); - - expect(mockLinkProjectExecutables.mock.calls).toMatchSnapshot('link bins'); - expect(mockRunScriptInPackageStreaming.mock.calls).toMatchSnapshot('script'); -}); diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index 7cf89c5f08f96..0fa3f355ae9d6 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -58,7 +58,9 @@ export const BootstrapCommand: ICommand = { const yarnLock = await readYarnLock(kbn); - await validateYarnLock(kbn, yarnLock); + if (options.validate) { + await validateYarnLock(kbn, yarnLock); + } await linkProjectExecutables(projects, projectGraph); diff --git a/packages/kbn-release-notes/src/lib/get_note_from_description.test.ts b/packages/kbn-release-notes/src/lib/get_note_from_description.test.ts index 23dcb302f090d..22b9713b78332 100644 --- a/packages/kbn-release-notes/src/lib/get_note_from_description.test.ts +++ b/packages/kbn-release-notes/src/lib/get_note_from_description.test.ts @@ -35,7 +35,8 @@ it('extracts expected components from html', () => { ## Release Note: Checkout this feature - `) + `), + 'release note' ) ).toMatchInlineSnapshot(`"Checkout this feature"`); @@ -46,10 +47,11 @@ it('extracts expected components from html', () => { Fixes: #1234 - #### Release Note: + #### Dev docs: We fixed an issue - `) + `), + 'dev docs' ) ).toMatchInlineSnapshot(`"We fixed an issue"`); @@ -60,8 +62,9 @@ it('extracts expected components from html', () => { Fixes: #1234 - Release note: Checkout feature foo - `) + OTHER TITLE: Checkout feature foo + `), + 'other title' ) ).toMatchInlineSnapshot(`"Checkout feature foo"`); @@ -73,7 +76,8 @@ it('extracts expected components from html', () => { My PR description release note : bar - `) + `), + 'release note' ) ).toMatchInlineSnapshot(`"bar"`); }); diff --git a/packages/kbn-release-notes/src/lib/get_note_from_description.ts b/packages/kbn-release-notes/src/lib/get_note_from_description.ts index 57df203470a5a..0d9135c431e36 100644 --- a/packages/kbn-release-notes/src/lib/get_note_from_description.ts +++ b/packages/kbn-release-notes/src/lib/get_note_from_description.ts @@ -19,11 +19,12 @@ import cheerio from 'cheerio'; -export function getNoteFromDescription(descriptionHtml: string) { +export function getNoteFromDescription(descriptionHtml: string, header: string) { + const re = new RegExp(`^(\\s*${header.toLowerCase()}(?:s)?\\s*:?\\s*)`, 'i'); const $ = cheerio.load(descriptionHtml); for (const el of $('p,h1,h2,h3,h4,h5').toArray()) { const text = $(el).text(); - const match = text.match(/^(\s*release note(?:s)?\s*:?\s*)/i); + const match = text.match(re); if (!match) { continue; diff --git a/packages/kbn-release-notes/src/lib/pr_api.ts b/packages/kbn-release-notes/src/lib/pr_api.ts index 1f26aa7ad86c3..5fa3dfdba10ef 100644 --- a/packages/kbn-release-notes/src/lib/pr_api.ts +++ b/packages/kbn-release-notes/src/lib/pr_api.ts @@ -178,7 +178,9 @@ export class PrApi { versions: labels .map((l) => Version.fromLabel(l)) .filter((v): v is Version => v instanceof Version), - note: getNoteFromDescription(node.bodyHTML), + note: + getNoteFromDescription(node.bodyHTML, 'release note') || + getNoteFromDescription(node.bodyHTML, 'dev docs'), }; } diff --git a/packages/kbn-std/src/index.ts b/packages/kbn-std/src/index.ts index d9d3ec4b0d52b..c111428017539 100644 --- a/packages/kbn-std/src/index.ts +++ b/packages/kbn-std/src/index.ts @@ -27,3 +27,4 @@ export { withTimeout } from './promise'; export { isRelativeUrl, modifyUrl, getUrlOrigin, URLMeaningfulParts } from './url'; export { unset } from './unset'; export { getFlattenedObject } from './get_flattened_object'; +export * from './rxjs_7'; diff --git a/packages/kbn-std/src/rxjs_7.test.ts b/packages/kbn-std/src/rxjs_7.test.ts new file mode 100644 index 0000000000000..dcc73602613fa --- /dev/null +++ b/packages/kbn-std/src/rxjs_7.test.ts @@ -0,0 +1,93 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as Rx from 'rxjs'; + +import { firstValueFrom, lastValueFrom } from './rxjs_7'; + +// create an empty observable that completes with no notifications +// after a delay to ensure helpers aren't checking for the EMPTY constant +function empty() { + return new Rx.Observable((subscriber) => { + setTimeout(() => { + subscriber.complete(); + }, 0); + }); +} + +describe('firstValueFrom()', () => { + it('resolves to the first value from the observable', async () => { + await expect(firstValueFrom(Rx.of(1, 2, 3))).resolves.toBe(1); + }); + + it('rejects if the observable is empty', async () => { + await expect(firstValueFrom(empty())).rejects.toThrowErrorMatchingInlineSnapshot( + `"no elements in sequence"` + ); + }); + + it('does not unsubscribe from the source observable that emits synchronously', async () => { + const values = [1, 2, 3, 4]; + let unsubscribed = false; + const source = new Rx.Observable((subscriber) => { + while (!subscriber.closed && values.length) { + subscriber.next(values.shift()!); + } + unsubscribed = subscriber.closed; + subscriber.complete(); + }); + + await expect(firstValueFrom(source)).resolves.toMatchInlineSnapshot(`1`); + if (unsubscribed) { + throw new Error('expected source to not be unsubscribed'); + } + expect(values).toEqual([]); + }); + + it('unsubscribes from the source observable after first async notification', async () => { + const values = [1, 2, 3, 4]; + let unsubscribed = false; + const source = new Rx.Observable((subscriber) => { + setTimeout(() => { + while (!subscriber.closed) { + subscriber.next(values.shift()!); + } + unsubscribed = subscriber.closed; + }); + }); + + await expect(firstValueFrom(source)).resolves.toMatchInlineSnapshot(`1`); + if (!unsubscribed) { + throw new Error('expected source to be unsubscribed'); + } + expect(values).toEqual([2, 3, 4]); + }); +}); + +describe('lastValueFrom()', () => { + it('resolves to the last value from the observable', async () => { + await expect(lastValueFrom(Rx.of(1, 2, 3))).resolves.toBe(3); + }); + + it('rejects if the observable is empty', async () => { + await expect(lastValueFrom(empty())).rejects.toThrowErrorMatchingInlineSnapshot( + `"no elements in sequence"` + ); + }); +}); diff --git a/packages/kbn-std/src/rxjs_7.ts b/packages/kbn-std/src/rxjs_7.ts new file mode 100644 index 0000000000000..f0a1be9125cc8 --- /dev/null +++ b/packages/kbn-std/src/rxjs_7.ts @@ -0,0 +1,251 @@ +/* eslint-disable @kbn/eslint/require-license-header */ + +/** + * @notice + * + * We include the `firstValueFrom()` and `lastValueFrom()` helpers + * extracted from the v7-beta.7 version of the RxJS library. + * + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Observable, Subscription, EmptyError } from 'rxjs'; + +export function firstValueFrom(source: Observable) { + return new Promise((resolve, reject) => { + const subs = new Subscription(); + subs.add( + source.subscribe({ + next: (value) => { + resolve(value); + subs.unsubscribe(); + }, + error: reject, + complete: () => { + reject(new EmptyError()); + }, + }) + ); + }); +} + +export function lastValueFrom(source: Observable) { + return new Promise((resolve, reject) => { + let _hasValue = false; + let _value: T; + source.subscribe({ + next: (value) => { + _value = value; + _hasValue = true; + }, + error: reject, + complete: () => { + if (_hasValue) { + resolve(_value); + } else { + reject(new EmptyError()); + } + }, + }); + }); +} diff --git a/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts b/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts index 87ba68c1bcb27..9f7ff5ac80c9f 100644 --- a/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts +++ b/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts @@ -88,7 +88,7 @@ export function runTelemetryCheck() { task: (context) => new Listr(checkCompatibleTypesTask(context), { exitOnError: true }), }, { - enabled: (_) => !!ignoreStoredJson, + enabled: (_) => fix || !ignoreStoredJson, title: 'Checking Matching collector.schema against stored json files', task: (context) => new Listr(checkMatchingSchemasTask(context, !fix), { exitOnError: true }), @@ -96,7 +96,10 @@ export function runTelemetryCheck() { { enabled: (_) => fix, skip: ({ roots }: TaskContext) => { - return roots.every(({ esMappingDiffs }) => !esMappingDiffs || !esMappingDiffs.length); + const noDiffs = roots.every( + ({ esMappingDiffs }) => !esMappingDiffs || !esMappingDiffs.length + ); + return noDiffs && 'No changes needed.'; }, title: 'Generating new telemetry mappings', task: (context) => new Listr(generateSchemasTask(context), { exitOnError: true }), @@ -104,7 +107,10 @@ export function runTelemetryCheck() { { enabled: (_) => fix, skip: ({ roots }: TaskContext) => { - return roots.every(({ esMappingDiffs }) => !esMappingDiffs || !esMappingDiffs.length); + const noDiffs = roots.every( + ({ esMappingDiffs }) => !esMappingDiffs || !esMappingDiffs.length + ); + return noDiffs && 'No changes needed.'; }, title: 'Updating telemetry mapping files', task: (context) => new Listr(writeToFileTask(context), { exitOnError: true }), diff --git a/packages/kbn-telemetry-tools/src/tools/__snapshots__/ts_parser.test.ts.snap b/packages/kbn-telemetry-tools/src/tools/__snapshots__/ts_parser.test.ts.snap index 5b1b3d9d35299..829b45496925c 100644 --- a/packages/kbn-telemetry-tools/src/tools/__snapshots__/ts_parser.test.ts.snap +++ b/packages/kbn-telemetry-tools/src/tools/__snapshots__/ts_parser.test.ts.snap @@ -1,5 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`parseUsageCollection throws when \`makeUsageCollector\` argument is a function call 1`] = ` +"Error extracting collector in src/fixtures/telemetry_collectors/externally_defined_usage_collector/index.ts +Error: makeUsageCollector argument must be an object." +`; + exports[`parseUsageCollection throws when mapping fields is not defined 1`] = ` "Error extracting collector in src/fixtures/telemetry_collectors/unmapped_collector.ts Error: usageCollector.schema must be defined." diff --git a/packages/kbn-telemetry-tools/src/tools/config.test.ts b/packages/kbn-telemetry-tools/src/tools/config.test.ts index 51ca0493cbb5a..ad2d1bce885ea 100644 --- a/packages/kbn-telemetry-tools/src/tools/config.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/config.test.ts @@ -33,7 +33,10 @@ describe('parseTelemetryRC', () => { { root: configRoot, output: configRoot, - exclude: [path.resolve(configRoot, './unmapped_collector.ts')], + exclude: [ + path.resolve(configRoot, './unmapped_collector.ts'), + path.resolve(configRoot, './externally_defined_usage_collector/index.ts'), + ], }, ]); }); diff --git a/packages/kbn-telemetry-tools/src/tools/ts_parser.test.ts b/packages/kbn-telemetry-tools/src/tools/ts_parser.test.ts index d036b93a7bbf9..64d7f1fc35f47 100644 --- a/packages/kbn-telemetry-tools/src/tools/ts_parser.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/ts_parser.test.ts @@ -33,7 +33,7 @@ export function loadFixtureProgram(fixtureName: string) { 'src', 'fixtures', 'telemetry_collectors', - `${fixtureName}.ts` + `${fixtureName}` ); const tsConfig = ts.findConfigFile('./', ts.sys.fileExists, 'tsconfig.json'); if (!tsConfig) { @@ -52,49 +52,56 @@ describe('parseUsageCollection', () => { it.todo('throws when a function is returned from fetch'); it.todo('throws when an object is not returned from fetch'); + it('throws when `makeUsageCollector` argument is a function call', () => { + const { program, sourceFile } = loadFixtureProgram( + 'externally_defined_usage_collector/index.ts' + ); + expect(() => [...parseUsageCollection(sourceFile, program)]).toThrowErrorMatchingSnapshot(); + }); + it('throws when mapping fields is not defined', () => { - const { program, sourceFile } = loadFixtureProgram('unmapped_collector'); + const { program, sourceFile } = loadFixtureProgram('unmapped_collector.ts'); expect(() => [...parseUsageCollection(sourceFile, program)]).toThrowErrorMatchingSnapshot(); }); it('parses root level defined collector', () => { - const { program, sourceFile } = loadFixtureProgram('working_collector'); + const { program, sourceFile } = loadFixtureProgram('working_collector.ts'); const result = [...parseUsageCollection(sourceFile, program)]; expect(result).toEqual([parsedWorkingCollector]); }); it('parses collector with schema defined as union of spreads', () => { - const { program, sourceFile } = loadFixtureProgram('schema_defined_with_spreads_collector'); + const { program, sourceFile } = loadFixtureProgram('schema_defined_with_spreads_collector.ts'); const result = [...parseUsageCollection(sourceFile, program)]; expect(result).toEqual([parsedSchemaDefinedWithSpreadsCollector]); }); it('parses nested collectors', () => { - const { program, sourceFile } = loadFixtureProgram('nested_collector'); + const { program, sourceFile } = loadFixtureProgram('nested_collector.ts'); const result = [...parseUsageCollection(sourceFile, program)]; expect(result).toEqual([parsedNestedCollector]); }); it('parses imported schema property', () => { - const { program, sourceFile } = loadFixtureProgram('imported_schema'); + const { program, sourceFile } = loadFixtureProgram('imported_schema.ts'); const result = [...parseUsageCollection(sourceFile, program)]; expect(result).toEqual(parsedImportedSchemaCollector); }); it('parses externally defined collectors', () => { - const { program, sourceFile } = loadFixtureProgram('externally_defined_collector'); + const { program, sourceFile } = loadFixtureProgram('externally_defined_collector.ts'); const result = [...parseUsageCollection(sourceFile, program)]; expect(result).toEqual(parsedExternallyDefinedCollector); }); it('parses imported Usage interface', () => { - const { program, sourceFile } = loadFixtureProgram('imported_usage_interface'); + const { program, sourceFile } = loadFixtureProgram('imported_usage_interface.ts'); const result = [...parseUsageCollection(sourceFile, program)]; expect(result).toEqual(parsedImportedUsageInterface); }); it('skips files that do not define a collector', () => { - const { program, sourceFile } = loadFixtureProgram('file_with_no_collector'); + const { program, sourceFile } = loadFixtureProgram('file_with_no_collector.ts'); const result = [...parseUsageCollection(sourceFile, program)]; expect(result).toEqual([]); }); diff --git a/packages/kbn-telemetry-tools/src/tools/ts_parser.ts b/packages/kbn-telemetry-tools/src/tools/ts_parser.ts index 6af8450f5a2e8..3af6b8b29768d 100644 --- a/packages/kbn-telemetry-tools/src/tools/ts_parser.ts +++ b/packages/kbn-telemetry-tools/src/tools/ts_parser.ts @@ -178,13 +178,11 @@ export function sourceHasUsageCollector(sourceFile: ts.SourceFile) { } const identifiers = (sourceFile as any).identifiers; - if ( - (!identifiers.get('makeUsageCollector') && !identifiers.get('type')) || - !identifiers.get('fetch') - ) { - return false; + if (identifiers.get('makeUsageCollector')) { + return true; } + return false; return true; } diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 4e86ec4bd72e0..8422c34c9ed08 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -11,6 +11,7 @@ }, "devDependencies": { "@babel/cli": "^7.10.5", + "@jest/types": "^26.5.2", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/utils": "1.0.0", @@ -22,14 +23,18 @@ "diff": "^4.0.1" }, "dependencies": { + "@jest/reporters": "^26.5.2", "chalk": "^4.1.0", "dedent": "^0.7.0", "del": "^5.1.0", + "execa": "^4.0.2", "exit-hook": "^2.2.0", "getopts": "^2.2.5", "glob": "^7.1.2", + "globby": "^8.0.1", "joi": "^13.5.2", "lodash": "^4.17.20", + "mustache": "^2.3.2", "parse-link-header": "^1.0.1", "rxjs": "^6.5.5", "strip-ansi": "^6.0.0", diff --git a/packages/kbn-test/src/index.ts b/packages/kbn-test/src/index.ts index 50ef521a2d811..a57b92fbdde25 100644 --- a/packages/kbn-test/src/index.ts +++ b/packages/kbn-test/src/index.ts @@ -55,8 +55,8 @@ export { export { runFailedTestsReporterCli } from './failed_tests_reporter'; -export { makeJunitReportPath } from './junit_report_path'; - export { CI_PARALLEL_PROCESS_PREFIX } from './ci_parallel_process_prefix'; export * from './functional_test_runner'; + +export * from './jest'; diff --git a/packages/kbn-test/src/jest/index.ts b/packages/kbn-test/src/jest/index.ts new file mode 100644 index 0000000000000..c6d680863d9c4 --- /dev/null +++ b/packages/kbn-test/src/jest/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './junit_reporter'; + +export * from './report_path'; diff --git a/packages/kbn-test/src/jest/integration_tests/__fixtures__/jest.config.js b/packages/kbn-test/src/jest/integration_tests/__fixtures__/jest.config.js new file mode 100644 index 0000000000000..50016c883d378 --- /dev/null +++ b/packages/kbn-test/src/jest/integration_tests/__fixtures__/jest.config.js @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const { resolve } = require('path'); +const { REPO_ROOT } = require('@kbn/utils'); + +module.exports = { + reporters: [ + 'default', + [ + `${REPO_ROOT}/packages/kbn-test/target/jest/junit_reporter`, + { + reportName: 'JUnit Reporter Integration Test', + rootDirectory: resolve( + REPO_ROOT, + 'packages/kbn-test/src/jest/integration_tests/__fixtures__' + ), + }, + ], + ], +}; diff --git a/src/dev/jest/integration_tests/__fixtures__/test.js b/packages/kbn-test/src/jest/integration_tests/__fixtures__/test.js similarity index 89% rename from src/dev/jest/integration_tests/__fixtures__/test.js rename to packages/kbn-test/src/jest/integration_tests/__fixtures__/test.js index fe0c65fd45b74..161012fb3a91c 100644 --- a/src/dev/jest/integration_tests/__fixtures__/test.js +++ b/packages/kbn-test/src/jest/integration_tests/__fixtures__/test.js @@ -17,6 +17,8 @@ * under the License. */ -it('fails', () => { - throw new Error('failure'); +describe('JUnit Reporter', () => { + it('fails', () => { + throw new Error('failure'); + }); }); diff --git a/src/dev/jest/integration_tests/junit_reporter.test.js b/packages/kbn-test/src/jest/integration_tests/junit_reporter.test.ts similarity index 82% rename from src/dev/jest/integration_tests/junit_reporter.test.js rename to packages/kbn-test/src/jest/integration_tests/junit_reporter.test.ts index 3280482a54203..1390c44d84a07 100644 --- a/src/dev/jest/integration_tests/junit_reporter.test.js +++ b/packages/kbn-test/src/jest/integration_tests/junit_reporter.test.ts @@ -25,27 +25,26 @@ import del from 'del'; import execa from 'execa'; import xml2js from 'xml2js'; import { makeJunitReportPath } from '@kbn/test'; +import { REPO_ROOT } from '@kbn/utils'; const MINUTE = 1000 * 60; -const ROOT_DIR = resolve(__dirname, '../../../../'); const FIXTURE_DIR = resolve(__dirname, '__fixtures__'); const TARGET_DIR = resolve(FIXTURE_DIR, 'target'); -const XML_PATH = makeJunitReportPath(FIXTURE_DIR, 'Jest Tests'); +const XML_PATH = makeJunitReportPath(FIXTURE_DIR, 'JUnit Reporter Integration Test'); afterAll(async () => { await del(TARGET_DIR); }); const parseXml = promisify(xml2js.parseString); - it( 'produces a valid junit report for failures', async () => { const result = await execa( - process.execPath, - ['-r', require.resolve('../../../setup_node_env'), require.resolve('jest/bin/jest')], + './node_modules/.bin/jest', + ['--config', 'packages/kbn-test/src/jest/integration_tests/__fixtures__/jest.config.js'], { - cwd: FIXTURE_DIR, + cwd: REPO_ROOT, env: { CI: 'true', }, @@ -57,6 +56,7 @@ it( await expect(parseXml(readFileSync(XML_PATH, 'utf8'))).resolves.toEqual({ testsuites: { $: { + failures: '1', name: 'jest', skipped: '0', tests: '1', @@ -67,7 +67,7 @@ it( { $: { failures: '1', - file: resolve(ROOT_DIR, 'src/dev/jest/integration_tests/__fixtures__/test.js'), + file: resolve(FIXTURE_DIR, './test.js'), name: 'test.js', skipped: '0', tests: '1', @@ -77,8 +77,8 @@ it( testcase: [ { $: { - classname: 'Jest Tests.·', - name: 'fails', + classname: 'JUnit Reporter Integration Test.·', + name: 'JUnit Reporter fails', time: expect.anything(), }, failure: [expect.stringMatching(/Error: failure\s+at /m)], diff --git a/src/dev/jest/junit_reporter.js b/packages/kbn-test/src/jest/junit_reporter.ts similarity index 74% rename from src/dev/jest/junit_reporter.js rename to packages/kbn-test/src/jest/junit_reporter.ts index f33358c081a06..8deb6751bc268 100644 --- a/src/dev/jest/junit_reporter.js +++ b/packages/kbn-test/src/jest/junit_reporter.ts @@ -22,20 +22,31 @@ import { writeFileSync, mkdirSync } from 'fs'; import xmlBuilder from 'xmlbuilder'; -import { escapeCdata, makeJunitReportPath } from '@kbn/test'; +import type { Config } from '@jest/types'; +import { AggregatedResult, Test, BaseReporter } from '@jest/reporters'; -const ROOT_DIR = dirname(require.resolve('../../../package.json')); +import { escapeCdata } from '../mocha/xml'; +import { makeJunitReportPath } from './report_path'; + +interface ReporterOptions { + reportName?: string; + rootDirectory?: string; +} /** * Jest reporter that produces JUnit report when running on CI * @class JestJUnitReporter */ -export default class JestJUnitReporter { - constructor(globalConfig, options = {}) { - const { reportName = 'Jest Tests', rootDirectory = ROOT_DIR } = options; - this._reportName = reportName; - this._rootDirectory = resolve(rootDirectory); +// eslint-disable-next-line import/no-default-export +export default class JestJUnitReporter extends BaseReporter { + private _reportName: string; + private _rootDirectory: string; + + constructor(globalConfig: Config.GlobalConfig, { rootDirectory, reportName }: ReporterOptions) { + super(); + this._reportName = reportName || 'Jest Tests'; + this._rootDirectory = rootDirectory ? resolve(rootDirectory) : resolve(__dirname, '../..'); } /** @@ -44,7 +55,7 @@ export default class JestJUnitReporter { * @param {JestResults} results see https://facebook.github.io/jest/docs/en/configuration.html#testresultsprocessor-string * @return {undefined} */ - onRunComplete(contexts, results) { + onRunComplete(contexts: Set, results: AggregatedResult): void { if (!process.env.CI || process.env.DISABLE_JUNIT_REPORTER || !results.testResults.length) { return; } @@ -55,18 +66,19 @@ export default class JestJUnitReporter { 'testsuites', { encoding: 'utf-8' }, {}, - { skipNullAttributes: true } + { keepNullAttributes: false } ); - const msToIso = (ms) => (ms ? new Date(ms).toISOString().slice(0, -5) : undefined); - const msToSec = (ms) => (ms ? (ms / 1000).toFixed(3) : undefined); + const msToIso = (ms: number | null | undefined) => + ms ? new Date(ms).toISOString().slice(0, -5) : undefined; + const msToSec = (ms: number | null | undefined) => (ms ? (ms / 1000).toFixed(3) : undefined); root.att({ name: 'jest', timestamp: msToIso(results.startTime), time: msToSec(Date.now() - results.startTime), tests: results.numTotalTests, - failures: results.numFailingTests, + failures: results.numFailedTests, skipped: results.numPendingTests, }); diff --git a/packages/kbn-test/src/junit_report_path.ts b/packages/kbn-test/src/jest/report_path.ts similarity index 93% rename from packages/kbn-test/src/junit_report_path.ts rename to packages/kbn-test/src/jest/report_path.ts index 90405d7a89c02..fe122c349c193 100644 --- a/packages/kbn-test/src/junit_report_path.ts +++ b/packages/kbn-test/src/jest/report_path.ts @@ -18,7 +18,7 @@ */ import { resolve } from 'path'; -import { CI_PARALLEL_PROCESS_PREFIX } from './ci_parallel_process_prefix'; +import { CI_PARALLEL_PROCESS_PREFIX } from '../ci_parallel_process_prefix'; export function makeJunitReportPath(rootDirectory: string, reportName: string) { return resolve( diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/entry.js index 966fb65406ac6..d74b45f973eb1 100644 --- a/packages/kbn-ui-shared-deps/entry.js +++ b/packages/kbn-ui-shared-deps/entry.js @@ -53,5 +53,8 @@ export const ElasticEuiChartsTheme = require('@elastic/eui/dist/eui_charts_theme export const Lodash = require('lodash'); export const LodashFp = require('lodash/fp'); +// runtime deps which don't need to be copied across all bundles +export const TsLib = require('tslib'); + import * as Theme from './theme.ts'; export { Theme }; diff --git a/packages/kbn-ui-shared-deps/index.js b/packages/kbn-ui-shared-deps/index.js index a403ae63a8f70..8f931fae4f337 100644 --- a/packages/kbn-ui-shared-deps/index.js +++ b/packages/kbn-ui-shared-deps/index.js @@ -63,5 +63,10 @@ exports.externals = { '@elastic/eui/dist/eui_theme_dark.json': '__kbnSharedDeps__.Theme.euiDarkVars', lodash: '__kbnSharedDeps__.Lodash', 'lodash/fp': '__kbnSharedDeps__.LodashFp', + + /** + * runtime deps which don't need to be copied across all bundles + */ + tslib: '__kbnSharedDeps__.TsLib', }; exports.publicPathLoader = require.resolve('./public_path_loader'); diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index e5ebb874e58aa..0a154c537fec1 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -33,6 +33,7 @@ "rxjs": "^6.5.5", "styled-components": "^5.1.0", "symbol-observable": "^1.2.0", + "tslib": "^2.0.0", "whatwg-fetch": "^3.0.0" }, "devDependencies": { diff --git a/src/core/public/overlays/banners/banners_list.test.tsx b/src/core/public/overlays/banners/banners_list.test.tsx index dbee20790fa94..3850a88699d90 100644 --- a/src/core/public/overlays/banners/banners_list.test.tsx +++ b/src/core/public/overlays/banners/banners_list.test.tsx @@ -43,7 +43,7 @@ describe('BannersList', () => { ]); expect(mount().html()).toMatchInlineSnapshot( - `"

Hello!

"` + `"

Hello!

"` ); }); @@ -85,7 +85,7 @@ describe('BannersList', () => { // Two new banners should be rendered expect(component.html()).toMatchInlineSnapshot( - `"

First Banner!

Second banner!

"` + `"

First Banner!

Second banner!

"` ); // Original banner should be unmounted expect(unmount).toHaveBeenCalled(); diff --git a/src/core/public/overlays/banners/banners_list.tsx b/src/core/public/overlays/banners/banners_list.tsx index 6503af985f9c8..30acf6abff70b 100644 --- a/src/core/public/overlays/banners/banners_list.tsx +++ b/src/core/public/overlays/banners/banners_list.tsx @@ -59,6 +59,11 @@ const BannerItem: React.FunctionComponent<{ banner: OverlayBanner }> = ({ banner useEffect(() => banner.mount(element.current!), [banner]); // Only unmount / remount if banner object changed. return ( -
+
- - - + - - } - labelType="label" - > - - - - } - labelType="label" + + Label template + + +
+
+
+
+ +
+
+
+ +
+
+ +
- - - - +
+ +
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+
+
+ + Input + +
+
+
+ + Output + +
+
+
+ Input +
+
+ john +
+
+
+ Output +
+
+
+ converted url for john +
+
+
+
+ Input +
+
+ /some/pathname/asset.png +
+
+
+ Output +
+
+
+ converted url for /some/pathname/asset.png +
+
+
+
+ Input +
+
+ 1234 +
+
+
+ Output +
+
+
+ converted url for 1234 +
+
+
+
+
+
+
+
`; diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap index 14e5012e9a554..83e815dd72661 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap @@ -4,6 +4,7 @@ exports[`UrlTemplateFlyout should not render if not visible 1`] = `""`; exports[`UrlTemplateFlyout should render normally 1`] = ` diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.tsx index d04ee58f26b0a..4dd3fb8f1b695 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.tsx @@ -63,7 +63,7 @@ const items: LabelTemplateExampleItem[] = [ export const LabelTemplateFlyout = ({ isVisible = false, onClose = () => {} }) => { return isVisible ? ( - +

diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.test.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.test.tsx index a1a1655949432..eb5cac111928f 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.test.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.test.tsx @@ -18,13 +18,22 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; import { FieldFormat } from 'src/plugins/data/public'; - +import { IntlProvider } from 'react-intl'; import { UrlFormatEditor } from './url'; +import { coreMock } from '../../../../../../../../../core/public/mocks'; +import { createKibanaReactContext } from '../../../../../../../../kibana_react/public'; +import { render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +jest.mock('@elastic/eui/lib/services/accessibility', () => { + return { + htmlIdGenerator: () => () => `generated-id`, + }; +}); const fieldType = 'string'; -const format = { +const format = ({ getConverterFor: jest .fn() .mockImplementation(() => (input: string) => `converted url for ${input}`), @@ -35,78 +44,115 @@ const format = { { kind: 'audio', text: 'Audio' }, ], }, -}; +} as unknown) as FieldFormat; const formatParams = { openLinkInCurrentTab: true, urlTemplate: '', labelTemplate: '', width: '', height: '', + type: 'a', }; + const onChange = jest.fn(); const onError = jest.fn(); +const renderWithContext = (Element: React.ReactElement) => + render( + + {Element} + + ); + +const MY_BASE_PATH = 'my-base-path'; +const KibanaReactContext = createKibanaReactContext( + coreMock.createStart({ basePath: 'my-base-path' }) +); + describe('UrlFormatEditor', () => { it('should have a formatId', () => { expect(UrlFormatEditor.formatId).toEqual('url'); }); it('should render normally', async () => { - const component = shallow( + const { container } = renderWithContext( ); - - expect(component).toMatchSnapshot(); + expect(container).toMatchSnapshot(); }); it('should render url template help', async () => { - const component = shallow( + const { getByText, getByTestId } = renderWithContext( ); - (component.instance() as UrlFormatEditor).showUrlTemplateHelp(); - component.update(); - expect(component).toMatchSnapshot(); + getByText('URL template help'); + userEvent.click(getByText('URL template help')); + expect(getByTestId('urlTemplateFlyoutTestSubj')).toBeVisible(); }); it('should render label template help', async () => { - const component = shallow( + const { getByText, getByTestId } = renderWithContext( ); - (component.instance() as UrlFormatEditor).showLabelTemplateHelp(); - component.update(); - expect(component).toMatchSnapshot(); + getByText('Label template help'); + userEvent.click(getByText('Label template help')); + expect(getByTestId('labelTemplateFlyoutTestSubj')).toBeVisible(); }); it('should render width and height fields if image', async () => { - const component = shallow( + const { getByLabelText } = renderWithContext( ); - expect(component).toMatchSnapshot(); + expect(getByLabelText('Width')).toBeInTheDocument(); + expect(getByLabelText('Height')).toBeInTheDocument(); + }); + + it('should append base path to preview images', async () => { + let sampleImageUrlTemplate = ''; + const { getByLabelText } = renderWithContext( + { + sampleImageUrlTemplate = urlTemplate; + }} + onError={onError} + /> + ); + + // TODO: sample image url emitted only during change event + // So can't just path `type: img` and check rendered value + userEvent.selectOptions(getByLabelText('Type'), 'img'); + expect(sampleImageUrlTemplate).toContain(MY_BASE_PATH); + expect(sampleImageUrlTemplate).toMatchInlineSnapshot( + `"my-base-path/plugins/indexPatternManagement/assets/icons/{{value}}.png"` + ); }); }); diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.tsx index 30acf09526f85..95b5fc3955280 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.tsx @@ -36,6 +36,8 @@ import { FormatEditorSamples } from '../../samples'; import { LabelTemplateFlyout } from './label_template_flyout'; import { UrlTemplateFlyout } from './url_template_flyout'; +import type { IndexPatternManagmentContextValue } from '../../../../../../types'; +import { context as contextType } from '../../../../../../../../kibana_react/public'; interface OnChangeParam { type: string; @@ -66,14 +68,21 @@ export class UrlFormatEditor extends DefaultFormatEditor< UrlFormatEditorFormatParams, UrlFormatEditorFormatState > { + static contextType = contextType; static formatId = 'url'; - iconPattern: string; + // TODO: @kbn/optimizer can't compile this + // declare context: IndexPatternManagmentContextValue; + context: IndexPatternManagmentContextValue | undefined; + private get sampleIconPath() { + const sampleIconPath = `/plugins/indexPatternManagement/assets/icons/{{value}}.png`; + return this.context?.services.http + ? this.context.services.http.basePath.prepend(sampleIconPath) + : sampleIconPath; + } constructor(props: FormatEditorProps) { super(props); - this.iconPattern = `/plugins/indexPatternManagement/assets/icons/{{value}}.png`; - this.state = { ...this.state, sampleInputsByType: { @@ -104,9 +113,9 @@ export class UrlFormatEditor extends DefaultFormatEditor< params.width = width; params.height = height; if (!urlTemplate) { - params.urlTemplate = this.iconPattern; + params.urlTemplate = this.sampleIconPath; } - } else if (newType !== 'img' && urlTemplate === this.iconPattern) { + } else if (newType !== 'img' && urlTemplate === this.sampleIconPath) { params.urlTemplate = undefined; } this.onChange(params); diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.tsx index c1b144b0d9eac..b1c66874d69cf 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.tsx @@ -26,7 +26,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; export const UrlTemplateFlyout = ({ isVisible = false, onClose = () => {} }) => { return isVisible ? ( - +

diff --git a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx index add45a07e0c5f..d3f97e7f8aeb7 100644 --- a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx +++ b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx @@ -102,6 +102,7 @@ export async function mountManagementSection( ); return () => { + chrome.docTitle.reset(); ReactDOM.unmountComponentAtNode(params.element); }; } diff --git a/src/plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.tsx b/src/plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.tsx index 6da5be450beb0..f424b25526b16 100644 --- a/src/plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.tsx +++ b/src/plugins/input_control_vis/public/components/editor/index_pattern_select_form_row.tsx @@ -49,8 +49,6 @@ function IndexPatternSelectFormRowUi(props: IndexPatternSelectFormRowUiProps) { indexPatternId={indexPatternId} onChange={onChange} data-test-subj={selectId} - // TODO: supply actual savedObjectsClient here - savedObjectsClient={{} as any} /> ); diff --git a/src/plugins/input_control_vis/public/control/control.ts b/src/plugins/input_control_vis/public/control/control.ts index da2dc7bab7cf7..303b340e5905b 100644 --- a/src/plugins/input_control_vis/public/control/control.ts +++ b/src/plugins/input_control_vis/public/control/control.ts @@ -83,7 +83,7 @@ export abstract class Control { format = (value: any) => { const indexPattern = this.filterManager.getIndexPattern(); const field = this.filterManager.getField(); - if (field) { + if (field && indexPattern) { return indexPattern.getFormatterForField(field).convert(value); } diff --git a/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts b/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts index e0eeddf93f67e..141f879f248a9 100644 --- a/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts +++ b/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.test.ts @@ -21,7 +21,11 @@ import expect from '@kbn/expect'; import { FilterManager } from './filter_manager'; import { coreMock } from '../../../../../core/public/mocks'; -import { Filter, IndexPattern, FilterManager as QueryFilterManager } from '../../../../data/public'; +import { + Filter, + FilterManager as QueryFilterManager, + IndexPatternsContract, +} from '../../../../data/public'; const setupMock = coreMock.createSetup(); @@ -39,7 +43,6 @@ describe('FilterManager', function () { const controlId = 'control1'; describe('findFilters', function () { - const indexPatternMock = {} as IndexPattern; let kbnFilters: Filter[]; const queryFilterMock = new QueryFilterManager(setupMock.uiSettings); queryFilterMock.getAppFilters = () => kbnFilters; @@ -48,7 +51,13 @@ describe('FilterManager', function () { let filterManager: FilterManagerTest; beforeEach(() => { kbnFilters = []; - filterManager = new FilterManagerTest(controlId, 'field1', indexPatternMock, queryFilterMock); + filterManager = new FilterManagerTest( + controlId, + 'field1', + '1', + {} as IndexPatternsContract, + queryFilterMock + ); }); test('should not find filters that are not controlled by any visualization', function () { diff --git a/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.ts b/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.ts index ece3f7a88ba37..de948ccda8663 100644 --- a/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.ts +++ b/src/plugins/input_control_vis/public/control/filter_manager/filter_manager.ts @@ -19,14 +19,22 @@ import _ from 'lodash'; -import { FilterManager as QueryFilterManager, IndexPattern, Filter } from '../../../../data/public'; +import { + FilterManager as QueryFilterManager, + IndexPattern, + Filter, + IndexPatternsContract, +} from '../../../../data/public'; export abstract class FilterManager { + protected indexPattern: IndexPattern | undefined; + constructor( public controlId: string, public fieldName: string, - public indexPattern: IndexPattern, - public queryFilter: QueryFilterManager + private indexPatternId: string, + private indexPatternsService: IndexPatternsContract, + protected queryFilter: QueryFilterManager ) {} /** @@ -41,12 +49,22 @@ export abstract class FilterManager { abstract getValueFromFilterBar(): any; - getIndexPattern(): IndexPattern { + async init() { + try { + if (!this.indexPattern) { + this.indexPattern = await this.indexPatternsService.get(this.indexPatternId); + } + } catch (e) { + // invalid index pattern id + } + } + + getIndexPattern(): IndexPattern | undefined { return this.indexPattern; } getField() { - return this.indexPattern.fields.getByName(this.fieldName); + return this.indexPattern?.fields.getByName(this.fieldName); } findFilters(): Filter[] { @@ -54,7 +72,7 @@ export abstract class FilterManager { this.queryFilter.getAppFilters(), this.queryFilter.getGlobalFilters(), ]); - return kbnFilters.filter((kbnFilter) => { + return kbnFilters.filter((kbnFilter: Filter) => { return _.get(kbnFilter, 'meta.controlledBy') === this.controlId; }); } diff --git a/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts b/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts index ec1f9d42aa67b..066d87230f909 100644 --- a/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts +++ b/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.test.ts @@ -19,7 +19,12 @@ import expect from '@kbn/expect'; -import { Filter, IndexPattern, FilterManager as QueryFilterManager } from '../../../../data/public'; +import { + Filter, + IndexPattern, + FilterManager as QueryFilterManager, + IndexPatternsContract, +} from '../../../../data/public'; import { PhraseFilterManager } from './phrase_filter_manager'; describe('PhraseFilterManager', function () { @@ -42,15 +47,20 @@ describe('PhraseFilterManager', function () { }, }, } as IndexPattern; + const indexPatternsServiceMock = ({ + get: jest.fn().mockReturnValue(Promise.resolve(indexPatternMock)), + } as unknown) as jest.Mocked; const queryFilterMock: QueryFilterManager = {} as QueryFilterManager; let filterManager: PhraseFilterManager; - beforeEach(() => { + beforeEach(async () => { filterManager = new PhraseFilterManager( controlId, 'field1', - indexPatternMock, + '1', + indexPatternsServiceMock, queryFilterMock ); + await filterManager.init(); }); test('should create match phrase filter from single value', function () { @@ -89,10 +99,11 @@ describe('PhraseFilterManager', function () { constructor( id: string, fieldName: string, - indexPattern: IndexPattern, + indexPatternId: string, + indexPatternsService: IndexPatternsContract, queryFilter: QueryFilterManager ) { - super(id, fieldName, indexPattern, queryFilter); + super(id, fieldName, indexPatternId, indexPatternsService, queryFilter); this.mockFilters = []; } @@ -105,14 +116,15 @@ describe('PhraseFilterManager', function () { } } - const indexPatternMock: IndexPattern = {} as IndexPattern; + const indexPatternsServiceMock = {} as IndexPatternsContract; const queryFilterMock: QueryFilterManager = {} as QueryFilterManager; let filterManager: MockFindFiltersPhraseFilterManager; beforeEach(() => { filterManager = new MockFindFiltersPhraseFilterManager( controlId, 'field1', - indexPatternMock, + '1', + indexPatternsServiceMock, queryFilterMock ); }); diff --git a/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts b/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts index 03ed6c5520dec..f3a2b4085b923 100644 --- a/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts +++ b/src/plugins/input_control_vis/public/control/filter_manager/phrase_filter_manager.ts @@ -23,7 +23,7 @@ import { FilterManager } from './filter_manager'; import { PhraseFilter, esFilters, - IndexPattern, + IndexPatternsContract, FilterManager as QueryFilterManager, } from '../../../../data/public'; @@ -31,24 +31,26 @@ export class PhraseFilterManager extends FilterManager { constructor( controlId: string, fieldName: string, - indexPattern: IndexPattern, + indexPatternId: string, + indexPatternsService: IndexPatternsContract, queryFilter: QueryFilterManager ) { - super(controlId, fieldName, indexPattern, queryFilter); + super(controlId, fieldName, indexPatternId, indexPatternsService, queryFilter); } createFilter(phrases: any): PhraseFilter { + const indexPattern = this.getIndexPattern()!; let newFilter: PhraseFilter; - const value = this.indexPattern.fields.getByName(this.fieldName); + const value = indexPattern.fields.getByName(this.fieldName); if (!value) { throw new Error(`Unable to find field with name: ${this.fieldName} on indexPattern`); } if (phrases.length === 1) { - newFilter = esFilters.buildPhraseFilter(value, phrases[0], this.indexPattern); + newFilter = esFilters.buildPhraseFilter(value, phrases[0], indexPattern); } else { - newFilter = esFilters.buildPhrasesFilter(value, phrases, this.indexPattern); + newFilter = esFilters.buildPhrasesFilter(value, phrases, indexPattern); } newFilter.meta.key = this.fieldName; diff --git a/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts b/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts index 8556312d10f4a..2a66567006049 100644 --- a/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts +++ b/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.test.ts @@ -25,6 +25,7 @@ import { RangeFilterMeta, IndexPattern, FilterManager as QueryFilterManager, + IndexPatternsContract, } from '../../../../data/public'; describe('RangeFilterManager', function () { @@ -46,15 +47,20 @@ describe('RangeFilterManager', function () { }, }, } as IndexPattern; + const indexPatternsServiceMock = ({ + get: jest.fn().mockReturnValue(Promise.resolve(indexPatternMock)), + } as unknown) as jest.Mocked; const queryFilterMock: QueryFilterManager = {} as QueryFilterManager; let filterManager: RangeFilterManager; - beforeEach(() => { + beforeEach(async () => { filterManager = new RangeFilterManager( controlId, 'field1', - indexPatternMock, + '1', + indexPatternsServiceMock, queryFilterMock ); + await filterManager.init(); }); test('should create range filter from slider value', function () { @@ -75,10 +81,11 @@ describe('RangeFilterManager', function () { constructor( id: string, fieldName: string, - indexPattern: IndexPattern, + indexPatternId: string, + indexPatternsService: IndexPatternsContract, queryFilter: QueryFilterManager ) { - super(id, fieldName, indexPattern, queryFilter); + super(id, fieldName, indexPatternId, indexPatternsService, queryFilter); this.mockFilters = []; } @@ -91,14 +98,15 @@ describe('RangeFilterManager', function () { } } - const indexPatternMock: IndexPattern = {} as IndexPattern; + const indexPatternsServiceMock = {} as IndexPatternsContract; const queryFilterMock: QueryFilterManager = {} as QueryFilterManager; let filterManager: MockFindFiltersRangeFilterManager; beforeEach(() => { filterManager = new MockFindFiltersRangeFilterManager( controlId, 'field1', - indexPatternMock, + '1', + indexPatternsServiceMock, queryFilterMock ); }); diff --git a/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts b/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts index 1a884cf267c41..39e4bfa19e5dc 100644 --- a/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts +++ b/src/plugins/input_control_vis/public/control/filter_manager/range_filter_manager.ts @@ -61,11 +61,12 @@ export class RangeFilterManager extends FilterManager { * @return {object} range filter */ createFilter(value: SliderValue): RangeFilter { + const indexPattern = this.getIndexPattern()!; const newFilter = esFilters.buildRangeFilter( // TODO: Fix type to be required - this.indexPattern.fields.getByName(this.fieldName) as IFieldType, + indexPattern.fields.getByName(this.fieldName) as IFieldType, toRange(value), - this.indexPattern + indexPattern ); newFilter.meta.key = this.fieldName; newFilter.meta.controlledBy = this.controlId; diff --git a/src/plugins/input_control_vis/public/control/list_control_factory.ts b/src/plugins/input_control_vis/public/control/list_control_factory.ts index 325eb471d510b..74ee09117887e 100644 --- a/src/plugins/input_control_vis/public/control/list_control_factory.ts +++ b/src/plugins/input_control_vis/public/control/list_control_factory.ts @@ -142,6 +142,17 @@ export class ListControl extends Control { timeout: `${settings.autocompleteTimeout}ms`, terminate_after: Number(settings.autocompleteTerminateAfter), }; + + // dynamic options are only allowed on String fields but the setting defaults to true so it could + // be enabled for non-string fields (since UI input is hidden for non-string fields). + // If field is not string, then disable dynamic options. + const field = indexPattern?.fields + .getAll() + .find(({ name }) => name === this.controlParams.fieldName); + if (field && field.type !== 'string') { + this.options.dynamicOptions = false; + } + const aggs = termsAgg({ field: indexPattern.fields.getByName(fieldName), size: this.options.dynamicOptions ? null : _.get(this.options, 'size', 5), @@ -213,27 +224,20 @@ export async function listControlFactory( deps: InputControlVisDependencies ) { const [, { data: dataPluginStart }] = await deps.core.getStartServices(); - const indexPattern = await dataPluginStart.indexPatterns.get(controlParams.indexPattern); - - // dynamic options are only allowed on String fields but the setting defaults to true so it could - // be enabled for non-string fields (since UI input is hidden for non-string fields). - // If field is not string, then disable dynamic options. - const field = indexPattern.fields.getAll().find(({ name }) => name === controlParams.fieldName); - if (field && field.type !== 'string') { - controlParams.options.dynamicOptions = false; - } const listControl = new ListControl( controlParams, new PhraseFilterManager( controlParams.id, controlParams.fieldName, - indexPattern, + controlParams.indexPattern, + dataPluginStart.indexPatterns, deps.data.query.filterManager ), useTimeFilter, dataPluginStart.search.searchSource, deps ); + await listControl.filterManager.init(); return listControl; } diff --git a/src/plugins/input_control_vis/public/control/range_control_factory.ts b/src/plugins/input_control_vis/public/control/range_control_factory.ts index eac79ca5fcca8..9cb1a0e25a8db 100644 --- a/src/plugins/input_control_vis/public/control/range_control_factory.ts +++ b/src/plugins/input_control_vis/public/control/range_control_factory.ts @@ -134,18 +134,20 @@ export async function rangeControlFactory( deps: InputControlVisDependencies ): Promise { const [, { data: dataPluginStart }] = await deps.core.getStartServices(); - const indexPattern = await dataPluginStart.indexPatterns.get(controlParams.indexPattern); - return new RangeControl( + const rangeControl = new RangeControl( controlParams, new RangeFilterManager( controlParams.id, controlParams.fieldName, - indexPattern, + controlParams.indexPattern, + dataPluginStart.indexPatterns, deps.data.query.filterManager ), useTimeFilter, dataPluginStart.search.searchSource, deps ); + await rangeControl.filterManager.init(); + return rangeControl; } diff --git a/src/plugins/legacy_export/server/routes/import.ts b/src/plugins/legacy_export/server/routes/import.ts index 8d33983ad7e3c..135d79d782fd7 100644 --- a/src/plugins/legacy_export/server/routes/import.ts +++ b/src/plugins/legacy_export/server/routes/import.ts @@ -28,7 +28,7 @@ export const registerImportRoute = (router: IRouter, maxImportPayloadBytes: numb validate: { body: schema.object({ objects: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), - version: schema.string(), + version: schema.maybe(schema.string()), }), query: schema.object({ force: schema.boolean({ defaultValue: false }), diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 1f474dcbb8ff4..160e99a40790c 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -1290,6 +1290,235 @@ } } }, + "core": { + "properties": { + "config": { + "properties": { + "elasticsearch": { + "properties": { + "sniffOnStart": { + "type": "boolean" + }, + "sniffIntervalMs": { + "type": "long" + }, + "sniffOnConnectionFault": { + "type": "boolean" + }, + "numberOfHostsConfigured": { + "type": "long" + }, + "requestHeadersWhitelistConfigured": { + "type": "boolean" + }, + "customHeadersConfigured": { + "type": "boolean" + }, + "shardTimeoutMs": { + "type": "long" + }, + "requestTimeoutMs": { + "type": "long" + }, + "pingTimeoutMs": { + "type": "long" + }, + "logQueries": { + "type": "boolean" + }, + "ssl": { + "properties": { + "verificationMode": { + "type": "keyword" + }, + "certificateAuthoritiesConfigured": { + "type": "boolean" + }, + "certificateConfigured": { + "type": "boolean" + }, + "keyConfigured": { + "type": "boolean" + }, + "keystoreConfigured": { + "type": "boolean" + }, + "truststoreConfigured": { + "type": "boolean" + }, + "alwaysPresentCertificate": { + "type": "boolean" + } + } + }, + "apiVersion": { + "type": "keyword" + }, + "healthCheckDelayMs": { + "type": "long" + } + } + }, + "http": { + "properties": { + "basePathConfigured": { + "type": "boolean" + }, + "maxPayloadInBytes": { + "type": "long" + }, + "rewriteBasePath": { + "type": "boolean" + }, + "keepaliveTimeout": { + "type": "long" + }, + "socketTimeout": { + "type": "long" + }, + "compression": { + "properties": { + "enabled": { + "type": "boolean" + }, + "referrerWhitelistConfigured": { + "type": "boolean" + } + } + }, + "xsrf": { + "properties": { + "disableProtection": { + "type": "boolean" + }, + "whitelistConfigured": { + "type": "boolean" + } + } + }, + "requestId": { + "properties": { + "allowFromAnyIp": { + "type": "boolean" + }, + "ipAllowlistConfigured": { + "type": "boolean" + } + } + }, + "ssl": { + "properties": { + "certificateAuthoritiesConfigured": { + "type": "boolean" + }, + "certificateConfigured": { + "type": "boolean" + }, + "cipherSuites": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "keyConfigured": { + "type": "boolean" + }, + "keystoreConfigured": { + "type": "boolean" + }, + "truststoreConfigured": { + "type": "boolean" + }, + "redirectHttpFromPortConfigured": { + "type": "boolean" + }, + "supportedProtocols": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "clientAuthentication": { + "type": "keyword" + } + } + } + } + }, + "logging": { + "properties": { + "appendersTypesUsed": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "loggersConfiguredCount": { + "type": "long" + } + } + }, + "savedObjects": { + "properties": { + "maxImportPayloadBytes": { + "type": "long" + }, + "maxImportExportSizeBytes": { + "type": "long" + } + } + } + } + }, + "environment": { + "properties": { + "memory": { + "properties": { + "heapSizeLimit": { + "type": "long" + }, + "heapTotalBytes": { + "type": "long" + }, + "heapUsedBytes": { + "type": "long" + } + } + } + } + }, + "services": { + "properties": { + "savedObjects": { + "properties": { + "indices": { + "type": "array", + "items": { + "properties": { + "docsCount": { + "type": "long" + }, + "docsDeleted": { + "type": "long" + }, + "alias": { + "type": "text" + }, + "primaryStoreSizeBytes": { + "type": "long" + }, + "storeSizeBytes": { + "type": "long" + } + } + } + } + } + } + } + } + } + }, "csp": { "properties": { "strict": { @@ -2502,6 +2731,48 @@ "type": "long" } } + }, + "vis_type_vega": { + "properties": { + "vega_lib_specs_total": { + "type": "long" + }, + "vega_lite_lib_specs_total": { + "type": "long" + }, + "vega_use_map_total": { + "type": "long" + } + } + }, + "visualization_types": { + "properties": { + "DYNAMIC_KEY": { + "properties": { + "total": { + "type": "long" + }, + "spaces_min": { + "type": "long" + }, + "spaces_max": { + "type": "long" + }, + "spaces_avg": { + "type": "long" + }, + "saved_7_days_total": { + "type": "long" + }, + "saved_30_days_total": { + "type": "long" + }, + "saved_90_days_total": { + "type": "long" + } + } + } + } } } } diff --git a/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap b/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap index dc6571de969f0..a32609c2e3d34 100644 --- a/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap +++ b/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap @@ -2,12 +2,9 @@ exports[`interpreter/functions#table returns an object with the correct structure 1`] = ` Object { - "as": "visualization", + "as": "table_vis", "type": "render", "value": Object { - "params": Object { - "listenOnChange": true, - }, "visConfig": Object { "dimensions": Object { "buckets": Array [], diff --git a/src/plugins/vis_type_table/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_table/public/__snapshots__/to_ast.test.ts.snap new file mode 100644 index 0000000000000..d2298e6fb3eb5 --- /dev/null +++ b/src/plugins/vis_type_table/public/__snapshots__/to_ast.test.ts.snap @@ -0,0 +1,74 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`table vis toExpressionAst function should match snapshot based on params & dimensions 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "aggConfigs": Array [ + "[]", + ], + "includeFormatHints": Array [ + false, + ], + "index": Array [ + "123", + ], + "metricsAtAllLevels": Array [ + false, + ], + "partialRows": Array [ + true, + ], + }, + "function": "esaggs", + "type": "function", + }, + Object { + "arguments": Object { + "visConfig": Array [ + "{\\"perPage\\":20,\\"percentageCol\\":\\"Count\\",\\"showMetricsAtAllLevels\\":true,\\"showPartialRows\\":true,\\"showTotal\\":true,\\"sort\\":{\\"columnIndex\\":null,\\"direction\\":null},\\"totalFunc\\":\\"sum\\",\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"number\\"},\\"params\\":{},\\"label\\":\\"Count\\",\\"aggType\\":\\"count\\"}],\\"buckets\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"YYYY-MM-DD HH:mm\\"}},\\"params\\":{},\\"label\\":\\"order_date per 3 hours\\",\\"aggType\\":\\"date_histogram\\"}]}}", + ], + }, + "function": "kibana_table", + "type": "function", + }, + ], + "type": "expression", +} +`; + +exports[`table vis toExpressionAst function should match snapshot without params 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "aggConfigs": Array [ + "[]", + ], + "includeFormatHints": Array [ + false, + ], + "index": Array [ + "123", + ], + "metricsAtAllLevels": Array [ + false, + ], + }, + "function": "esaggs", + "type": "function", + }, + Object { + "arguments": Object { + "visConfig": Array [ + "{\\"showLabel\\":false,\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"number\\"},\\"params\\":{},\\"label\\":\\"Count\\",\\"aggType\\":\\"count\\"}],\\"buckets\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"YYYY-MM-DD HH:mm\\"}},\\"params\\":{},\\"label\\":\\"order_date per 3 hours\\",\\"aggType\\":\\"date_histogram\\"}]}}", + ], + }, + "function": "kibana_table", + "type": "function", + }, + ], + "type": "expression", +} +`; diff --git a/src/plugins/vis_type_table/public/index.ts b/src/plugins/vis_type_table/public/index.ts index 5621fdb094772..6493c967165db 100644 --- a/src/plugins/vis_type_table/public/index.ts +++ b/src/plugins/vis_type_table/public/index.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import './index.scss'; import { PluginInitializerContext } from 'kibana/public'; import { TableVisPlugin as Plugin } from './plugin'; diff --git a/src/plugins/vis_type_table/public/_table_vis.scss b/src/plugins/vis_type_table/public/legacy/_table_vis.scss similarity index 91% rename from src/plugins/vis_type_table/public/_table_vis.scss rename to src/plugins/vis_type_table/public/legacy/_table_vis.scss index 8a36b9818c2a3..fa12ef9a1cf39 100644 --- a/src/plugins/vis_type_table/public/_table_vis.scss +++ b/src/plugins/vis_type_table/public/legacy/_table_vis.scss @@ -4,8 +4,10 @@ .table-vis { display: flex; flex-direction: column; - flex: 1 0 100%; + flex: 1 1 0; overflow: auto; + + @include euiScrollBar; } .table-vis-container { diff --git a/src/plugins/vis_type_table/public/agg_table/_agg_table.scss b/src/plugins/vis_type_table/public/legacy/agg_table/_agg_table.scss similarity index 100% rename from src/plugins/vis_type_table/public/agg_table/_agg_table.scss rename to src/plugins/vis_type_table/public/legacy/agg_table/_agg_table.scss diff --git a/src/plugins/vis_type_table/public/agg_table/_index.scss b/src/plugins/vis_type_table/public/legacy/agg_table/_index.scss similarity index 100% rename from src/plugins/vis_type_table/public/agg_table/_index.scss rename to src/plugins/vis_type_table/public/legacy/agg_table/_index.scss diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table.html b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.html similarity index 100% rename from src/plugins/vis_type_table/public/agg_table/agg_table.html rename to src/plugins/vis_type_table/public/legacy/agg_table/agg_table.html diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table.js b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.js similarity index 99% rename from src/plugins/vis_type_table/public/agg_table/agg_table.js rename to src/plugins/vis_type_table/public/legacy/agg_table/agg_table.js index 1e98a06c2a6a9..a9ec431e9d940 100644 --- a/src/plugins/vis_type_table/public/agg_table/agg_table.js +++ b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.js @@ -17,9 +17,9 @@ * under the License. */ import _ from 'lodash'; -import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../share/public'; +import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../../share/public'; import aggTableTemplate from './agg_table.html'; -import { getFormatService } from '../services'; +import { getFormatService } from '../../services'; import { i18n } from '@kbn/i18n'; export function KbnAggTable(config, RecursionHelper) { diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table.test.js b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js similarity index 97% rename from src/plugins/vis_type_table/public/agg_table/agg_table.test.js rename to src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js index 29a10151a9418..c93fb4f8bd568 100644 --- a/src/plugins/vis_type_table/public/agg_table/agg_table.test.js +++ b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js @@ -24,14 +24,14 @@ import 'angular-mocks'; import sinon from 'sinon'; import { round } from 'lodash'; -import { getFieldFormatsRegistry } from '../../../data/public/test_utils'; -import { coreMock } from '../../../../core/public/mocks'; -import { initAngularBootstrap } from '../../../kibana_legacy/public'; -import { setUiSettings } from '../../../data/public/services'; -import { UI_SETTINGS } from '../../../data/public/'; -import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../share/public'; - -import { setFormatService } from '../services'; +import { getFieldFormatsRegistry } from '../../../../data/public/test_utils'; +import { coreMock } from '../../../../../core/public/mocks'; +import { initAngularBootstrap } from '../../../../kibana_legacy/public'; +import { setUiSettings } from '../../../../data/public/services'; +import { UI_SETTINGS } from '../../../../data/public/'; +import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../../share/public'; + +import { setFormatService } from '../../services'; import { getInnerAngular } from '../get_inner_angular'; import { initTableVisLegacyModule } from '../table_vis_legacy_module'; import { tabifiedData } from './tabified_data'; diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table_group.html b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.html similarity index 100% rename from src/plugins/vis_type_table/public/agg_table/agg_table_group.html rename to src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.html diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table_group.js b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.js similarity index 100% rename from src/plugins/vis_type_table/public/agg_table/agg_table_group.js rename to src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.js diff --git a/src/plugins/vis_type_table/public/agg_table/agg_table_group.test.js b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js similarity index 92% rename from src/plugins/vis_type_table/public/agg_table/agg_table_group.test.js rename to src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js index 04cf624331d81..833f51b446ac1 100644 --- a/src/plugins/vis_type_table/public/agg_table/agg_table_group.test.js +++ b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table_group.test.js @@ -22,11 +22,11 @@ import angular from 'angular'; import 'angular-mocks'; import expect from '@kbn/expect'; -import { getFieldFormatsRegistry } from '../../../data/public/test_utils'; -import { coreMock } from '../../../../core/public/mocks'; -import { initAngularBootstrap } from '../../../kibana_legacy/public'; -import { setUiSettings } from '../../../data/public/services'; -import { setFormatService } from '../services'; +import { getFieldFormatsRegistry } from '../../../../data/public/test_utils'; +import { coreMock } from '../../../../../core/public/mocks'; +import { initAngularBootstrap } from '../../../../kibana_legacy/public'; +import { setUiSettings } from '../../../../data/public/services'; +import { setFormatService } from '../../services'; import { getInnerAngular } from '../get_inner_angular'; import { initTableVisLegacyModule } from '../table_vis_legacy_module'; import { tabifiedData } from './tabified_data'; diff --git a/src/plugins/vis_type_table/public/agg_table/tabified_data.js b/src/plugins/vis_type_table/public/legacy/agg_table/tabified_data.js similarity index 100% rename from src/plugins/vis_type_table/public/agg_table/tabified_data.js rename to src/plugins/vis_type_table/public/legacy/agg_table/tabified_data.js diff --git a/src/plugins/vis_type_table/public/get_inner_angular.ts b/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts similarity index 98% rename from src/plugins/vis_type_table/public/get_inner_angular.ts rename to src/plugins/vis_type_table/public/legacy/get_inner_angular.ts index 4e4269a1f44f4..f3d88a2a217b3 100644 --- a/src/plugins/vis_type_table/public/get_inner_angular.ts +++ b/src/plugins/vis_type_table/public/legacy/get_inner_angular.ts @@ -33,7 +33,7 @@ import { PrivateProvider, watchMultiDecorator, KbnAccessibleClickProvider, -} from '../../kibana_legacy/public'; +} from '../../../kibana_legacy/public'; initAngularBootstrap(); diff --git a/src/plugins/vis_type_table/public/index.scss b/src/plugins/vis_type_table/public/legacy/index.scss similarity index 100% rename from src/plugins/vis_type_table/public/index.scss rename to src/plugins/vis_type_table/public/legacy/index.scss diff --git a/src/plugins/vis_type_table/public/paginated_table/_index.scss b/src/plugins/vis_type_table/public/legacy/paginated_table/_index.scss similarity index 100% rename from src/plugins/vis_type_table/public/paginated_table/_index.scss rename to src/plugins/vis_type_table/public/legacy/paginated_table/_index.scss diff --git a/src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss b/src/plugins/vis_type_table/public/legacy/paginated_table/_table_cell_filter.scss similarity index 100% rename from src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss rename to src/plugins/vis_type_table/public/legacy/paginated_table/_table_cell_filter.scss diff --git a/src/plugins/vis_type_table/public/paginated_table/paginated_table.html b/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.html similarity index 100% rename from src/plugins/vis_type_table/public/paginated_table/paginated_table.html rename to src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.html diff --git a/src/plugins/vis_type_table/public/paginated_table/paginated_table.js b/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.js similarity index 100% rename from src/plugins/vis_type_table/public/paginated_table/paginated_table.js rename to src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.js diff --git a/src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts b/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts similarity index 98% rename from src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts rename to src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts index de253f26ff9e7..3bc5f79557041 100644 --- a/src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts +++ b/src/plugins/vis_type_table/public/legacy/paginated_table/paginated_table.test.ts @@ -25,11 +25,7 @@ import 'angular-mocks'; import { getAngularModule } from '../get_inner_angular'; import { initTableVisLegacyModule } from '../table_vis_legacy_module'; -import { coreMock } from '../../../../core/public/mocks'; - -jest.mock('../../../kibana_legacy/public/angular/angular_config', () => ({ - configureAppAngularModule: () => {}, -})); +import { coreMock } from '../../../../../core/public/mocks'; interface Sort { columnIndex: number; diff --git a/src/plugins/vis_type_table/public/paginated_table/rows.js b/src/plugins/vis_type_table/public/legacy/paginated_table/rows.js similarity index 91% rename from src/plugins/vis_type_table/public/paginated_table/rows.js rename to src/plugins/vis_type_table/public/legacy/paginated_table/rows.js index d8f01a10c63fa..54e754adcc170 100644 --- a/src/plugins/vis_type_table/public/paginated_table/rows.js +++ b/src/plugins/vis_type_table/public/legacy/paginated_table/rows.js @@ -44,15 +44,18 @@ export function KbnRows($compile) { } $scope.filter({ - data: [ - { - table: $scope.table, - row: $scope.rows.findIndex((r) => r === row), - column: $scope.table.columns.findIndex((c) => c.id === column.id), - value, - }, - ], - negate, + name: 'filterBucket', + data: { + data: [ + { + table: $scope.table, + row: $scope.rows.findIndex((r) => r === row), + column: $scope.table.columns.findIndex((c) => c.id === column.id), + value, + }, + ], + negate, + }, }); }; diff --git a/src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html b/src/plugins/vis_type_table/public/legacy/paginated_table/table_cell_filter.html similarity index 100% rename from src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html rename to src/plugins/vis_type_table/public/legacy/paginated_table/table_cell_filter.html diff --git a/src/plugins/vis_type_table/public/table_vis.html b/src/plugins/vis_type_table/public/legacy/table_vis.html similarity index 96% rename from src/plugins/vis_type_table/public/table_vis.html rename to src/plugins/vis_type_table/public/legacy/table_vis.html index f721b670400d6..c469cd250755c 100644 --- a/src/plugins/vis_type_table/public/table_vis.html +++ b/src/plugins/vis_type_table/public/legacy/table_vis.html @@ -15,7 +15,7 @@
({ - configureAppAngularModule: () => {}, -})); - interface TableVisScope extends IScope { [key: string]: any; } @@ -112,10 +107,6 @@ describe('Table Vis - Controller', () => { coreMock.createSetup() ); }); - const tableVisTypeDefinition = getTableVisTypeDefinition( - coreMock.createSetup(), - coreMock.createPluginInitializerContext() - ); function getRangeVis(params?: object) { return ({ diff --git a/src/plugins/vis_type_table/public/table_vis_legacy_module.ts b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_module.ts similarity index 100% rename from src/plugins/vis_type_table/public/table_vis_legacy_module.ts rename to src/plugins/vis_type_table/public/legacy/table_vis_legacy_module.ts diff --git a/src/plugins/vis_type_table/public/legacy/table_vis_legacy_renderer.tsx b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_renderer.tsx new file mode 100644 index 0000000000000..ab633bd5137ba --- /dev/null +++ b/src/plugins/vis_type_table/public/legacy/table_vis_legacy_renderer.tsx @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreSetup, PluginInitializerContext } from 'kibana/public'; +import { ExpressionRenderDefinition } from 'src/plugins/expressions'; +import { TablePluginStartDependencies } from '../plugin'; +import { TableVisRenderValue } from '../table_vis_fn'; +import { TableVisLegacyController } from './vis_controller'; + +const tableVisRegistry = new Map(); + +export const getTableVisLegacyRenderer: ( + core: CoreSetup, + context: PluginInitializerContext +) => ExpressionRenderDefinition = (core, context) => ({ + name: 'table_vis', + reuseDomNode: true, + render: async (domNode, config, handlers) => { + let registeredController = tableVisRegistry.get(domNode); + + if (!registeredController) { + const { getTableVisualizationControllerClass } = await import('./vis_controller'); + + const Controller = getTableVisualizationControllerClass(core, context); + registeredController = new Controller(domNode); + tableVisRegistry.set(domNode, registeredController); + + handlers.onDestroy(() => { + registeredController?.destroy(); + tableVisRegistry.delete(domNode); + }); + } + + await registeredController.render(config.visData, config.visConfig, handlers); + handlers.done(); + }, +}); diff --git a/src/plugins/vis_type_table/public/vis_controller.ts b/src/plugins/vis_type_table/public/legacy/vis_controller.ts similarity index 68% rename from src/plugins/vis_type_table/public/vis_controller.ts rename to src/plugins/vis_type_table/public/legacy/vis_controller.ts index 1781808660260..eff8e34f3e778 100644 --- a/src/plugins/vis_type_table/public/vis_controller.ts +++ b/src/plugins/vis_type_table/public/legacy/vis_controller.ts @@ -20,35 +20,43 @@ import { CoreSetup, PluginInitializerContext } from 'kibana/public'; import angular, { IModule, auto, IRootScopeService, IScope, ICompileService } from 'angular'; import $ from 'jquery'; -import { VisParams, ExprVis } from '../../visualizations/public'; +import './index.scss'; + +import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { getAngularModule } from './get_inner_angular'; -import { getKibanaLegacy } from './services'; import { initTableVisLegacyModule } from './table_vis_legacy_module'; +// @ts-ignore +import tableVisTemplate from './table_vis.html'; +import { TablePluginStartDependencies } from '../plugin'; +import { TableVisConfig } from '../types'; +import { TableContext } from '../table_vis_response_handler'; const innerAngularName = 'kibana/table_vis'; +export type TableVisLegacyController = InstanceType< + ReturnType +>; + export function getTableVisualizationControllerClass( - core: CoreSetup, + core: CoreSetup, context: PluginInitializerContext ) { return class TableVisualizationController { private tableVisModule: IModule | undefined; private injector: auto.IInjectorService | undefined; el: JQuery; - vis: ExprVis; $rootScope: IRootScopeService | null = null; $scope: (IScope & { [key: string]: any }) | undefined; $compile: ICompileService | undefined; - constructor(domeElement: Element, vis: ExprVis) { + constructor(domeElement: Element) { this.el = $(domeElement); - this.vis = vis; } getInjector() { if (!this.injector) { const mountpoint = document.createElement('div'); - mountpoint.setAttribute('style', 'height: 100%; width: 100%;'); + mountpoint.className = 'visualization'; this.injector = angular.bootstrap(mountpoint, [innerAngularName]); this.el.append(mountpoint); } @@ -58,14 +66,18 @@ export function getTableVisualizationControllerClass( async initLocalAngular() { if (!this.tableVisModule) { - const [coreStart] = await core.getStartServices(); + const [coreStart, { kibanaLegacy }] = await core.getStartServices(); this.tableVisModule = getAngularModule(innerAngularName, coreStart, context); initTableVisLegacyModule(this.tableVisModule); + kibanaLegacy.loadFontAwesome(); } } - async render(esResponse: object, visParams: VisParams): Promise { - getKibanaLegacy().loadFontAwesome(); + async render( + esResponse: TableContext, + visParams: TableVisConfig, + handlers: IInterpreterRenderHandlers + ): Promise { await this.initLocalAngular(); return new Promise(async (resolve, reject) => { @@ -79,16 +91,6 @@ export function getTableVisualizationControllerClass( return; } - // How things get into this $scope? - // To inject variables into this $scope there's the following pipeline of stuff to check: - // - visualize_embeddable => that's what the editor creates to wrap this Angular component - // - build_pipeline => it serialize all the params into an Angular template compiled on the fly - // - table_vis_fn => unserialize the params and prepare them for the final React/Angular bridge - // - visualization_renderer => creates the wrapper component for this controller and passes the params - // - // In case some prop is missing check into the top of the chain if they are available and check - // the list above that it is passing through - this.$scope.vis = this.vis; this.$scope.visState = { params: visParams, title: visParams.title }; this.$scope.esResponse = esResponse; @@ -101,11 +103,10 @@ export function getTableVisualizationControllerClass( if (!this.$scope && this.$compile) { this.$scope = this.$rootScope.$new(); - this.$scope.uiState = this.vis.getUiState(); + this.$scope.uiState = handlers.uiState; + this.$scope.filter = handlers.event; updateScope(); - this.el - .find('div') - .append(this.$compile(this.vis.type.visConfig?.template ?? '')(this.$scope)); + this.el.find('div').append(this.$compile(tableVisTemplate)(this.$scope)); this.$scope.$apply(); } else { updateScope(); diff --git a/src/plugins/vis_type_table/public/plugin.ts b/src/plugins/vis_type_table/public/plugin.ts index 28f823df79d91..35ef5fc831cb7 100644 --- a/src/plugins/vis_type_table/public/plugin.ts +++ b/src/plugins/vis_type_table/public/plugin.ts @@ -21,10 +21,11 @@ import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; import { VisualizationsSetup } from '../../visualizations/public'; import { createTableVisFn } from './table_vis_fn'; -import { getTableVisTypeDefinition } from './table_vis_type'; +import { tableVisTypeDefinition } from './table_vis_type'; import { DataPublicPluginStart } from '../../data/public'; -import { setFormatService, setKibanaLegacy } from './services'; +import { setFormatService } from './services'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; +import { getTableVisLegacyRenderer } from './legacy/table_vis_legacy_renderer'; /** @internal */ export interface TablePluginSetupDependencies { @@ -39,7 +40,9 @@ export interface TablePluginStartDependencies { } /** @internal */ -export class TableVisPlugin implements Plugin, void> { +export class TableVisPlugin + implements + Plugin, void, TablePluginSetupDependencies, TablePluginStartDependencies> { initializerContext: PluginInitializerContext; createBaseVisualization: any; @@ -48,17 +51,15 @@ export class TableVisPlugin implements Plugin, void> { } public async setup( - core: CoreSetup, + core: CoreSetup, { expressions, visualizations }: TablePluginSetupDependencies ) { expressions.registerFunction(createTableVisFn); - visualizations.createBaseVisualization( - getTableVisTypeDefinition(core, this.initializerContext) - ); + expressions.registerRenderer(getTableVisLegacyRenderer(core, this.initializerContext)); + visualizations.createBaseVisualization(tableVisTypeDefinition); } - public start(core: CoreStart, { data, kibanaLegacy }: TablePluginStartDependencies) { + public start(core: CoreStart, { data }: TablePluginStartDependencies) { setFormatService(data.fieldFormats); - setKibanaLegacy(kibanaLegacy); } } diff --git a/src/plugins/vis_type_table/public/services.ts b/src/plugins/vis_type_table/public/services.ts index b4f996f078f6b..3aaffe75e27f1 100644 --- a/src/plugins/vis_type_table/public/services.ts +++ b/src/plugins/vis_type_table/public/services.ts @@ -19,12 +19,7 @@ import { createGetterSetter } from '../../kibana_utils/public'; import { DataPublicPluginStart } from '../../data/public'; -import { KibanaLegacyStart } from '../../kibana_legacy/public'; export const [getFormatService, setFormatService] = createGetterSetter< DataPublicPluginStart['fieldFormats'] >('table data.fieldFormats'); - -export const [getKibanaLegacy, setKibanaLegacy] = createGetterSetter( - 'table kibanaLegacy' -); diff --git a/src/plugins/vis_type_table/public/table_vis_fn.ts b/src/plugins/vis_type_table/public/table_vis_fn.ts index 9739a7a284e6c..2e446ba4e4fcf 100644 --- a/src/plugins/vis_type_table/public/table_vis_fn.ts +++ b/src/plugins/vis_type_table/public/table_vis_fn.ts @@ -20,6 +20,7 @@ import { i18n } from '@kbn/i18n'; import { tableVisResponseHandler, TableContext } from './table_vis_response_handler'; import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; +import { TableVisConfig } from './types'; export type Input = KibanaDatatable; @@ -27,23 +28,20 @@ interface Arguments { visConfig: string | null; } -type VisParams = Required; - -interface RenderValue { +export interface TableVisRenderValue { visData: TableContext; visType: 'table'; - visConfig: VisParams; - params: { - listenOnChange: boolean; - }; + visConfig: TableVisConfig; } -export const createTableVisFn = (): ExpressionFunctionDefinition< +export type TableExpressionFunctionDefinition = ExpressionFunctionDefinition< 'kibana_table', Input, Arguments, - Render -> => ({ + Render +>; + +export const createTableVisFn = (): TableExpressionFunctionDefinition => ({ name: 'kibana_table', type: 'render', inputTypes: ['kibana_datatable'], @@ -63,14 +61,11 @@ export const createTableVisFn = (): ExpressionFunctionDefinition< return { type: 'render', - as: 'visualization', + as: 'table_vis', value: { visData: convertedData, visType: 'table', visConfig, - params: { - listenOnChange: true, - }, }, }; }, diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts index 95f4f06ee6111..bfc7abac02895 100644 --- a/src/plugins/vis_type_table/public/table_vis_type.ts +++ b/src/plugins/vis_type_table/public/table_vis_type.ts @@ -16,91 +16,82 @@ * specific language governing permissions and limitations * under the License. */ -import { CoreSetup, PluginInitializerContext } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { AggGroupNames } from '../../data/public'; import { Schemas } from '../../vis_default_editor/public'; import { BaseVisTypeOptions } from '../../visualizations/public'; -import { tableVisResponseHandler } from './table_vis_response_handler'; -// @ts-ignore -import tableVisTemplate from './table_vis.html'; + import { TableOptions } from './components/table_vis_options_lazy'; -import { getTableVisualizationControllerClass } from './vis_controller'; import { VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public'; +import { toExpressionAst } from './to_ast'; +import { TableVisParams } from './types'; -export function getTableVisTypeDefinition( - core: CoreSetup, - context: PluginInitializerContext -): BaseVisTypeOptions { - return { - name: 'table', - title: i18n.translate('visTypeTable.tableVisTitle', { - defaultMessage: 'Data Table', - }), - icon: 'visTable', - description: i18n.translate('visTypeTable.tableVisDescription', { - defaultMessage: 'Display values in a table', - }), - visualization: getTableVisualizationControllerClass(core, context), - getSupportedTriggers: () => { - return [VIS_EVENT_TO_TRIGGER.filter]; - }, - visConfig: { - defaults: { - perPage: 10, - showPartialRows: false, - showMetricsAtAllLevels: false, - sort: { - columnIndex: null, - direction: null, - }, - showTotal: false, - totalFunc: 'sum', - percentageCol: '', +export const tableVisTypeDefinition: BaseVisTypeOptions = { + name: 'table', + title: i18n.translate('visTypeTable.tableVisTitle', { + defaultMessage: 'Data Table', + }), + icon: 'visTable', + description: i18n.translate('visTypeTable.tableVisDescription', { + defaultMessage: 'Display values in a table', + }), + getSupportedTriggers: () => { + return [VIS_EVENT_TO_TRIGGER.filter]; + }, + visConfig: { + defaults: { + perPage: 10, + showPartialRows: false, + showMetricsAtAllLevels: false, + sort: { + columnIndex: null, + direction: null, }, - template: tableVisTemplate, + showTotal: false, + totalFunc: 'sum', + percentageCol: '', }, - editorConfig: { - optionsTemplate: TableOptions, - schemas: new Schemas([ - { - group: AggGroupNames.Metrics, - name: 'metric', - title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.metricTitle', { - defaultMessage: 'Metric', - }), - aggFilter: ['!geo_centroid', '!geo_bounds'], - aggSettings: { - top_hits: { - allowStrings: true, - }, + }, + editorConfig: { + optionsTemplate: TableOptions, + schemas: new Schemas([ + { + group: AggGroupNames.Metrics, + name: 'metric', + title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.metricTitle', { + defaultMessage: 'Metric', + }), + aggFilter: ['!geo_centroid', '!geo_bounds'], + aggSettings: { + top_hits: { + allowStrings: true, }, - min: 1, - defaults: [{ type: 'count', schema: 'metric' }], - }, - { - group: AggGroupNames.Buckets, - name: 'bucket', - title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.bucketTitle', { - defaultMessage: 'Split rows', - }), - aggFilter: ['!filter'], - }, - { - group: AggGroupNames.Buckets, - name: 'split', - title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.splitTitle', { - defaultMessage: 'Split table', - }), - min: 0, - max: 1, - aggFilter: ['!filter'], }, - ]), - }, - responseHandler: tableVisResponseHandler, - hierarchicalData: (vis) => { - return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels); - }, - }; -} + min: 1, + defaults: [{ type: 'count', schema: 'metric' }], + }, + { + group: AggGroupNames.Buckets, + name: 'bucket', + title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.bucketTitle', { + defaultMessage: 'Split rows', + }), + aggFilter: ['!filter'], + }, + { + group: AggGroupNames.Buckets, + name: 'split', + title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.splitTitle', { + defaultMessage: 'Split table', + }), + min: 0, + max: 1, + aggFilter: ['!filter'], + }, + ]), + }, + toExpressionAst, + hierarchicalData: (vis) => { + return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels); + }, +}; diff --git a/src/plugins/vis_type_table/public/to_ast.test.ts b/src/plugins/vis_type_table/public/to_ast.test.ts new file mode 100644 index 0000000000000..045b5814944b0 --- /dev/null +++ b/src/plugins/vis_type_table/public/to_ast.test.ts @@ -0,0 +1,79 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Vis } from 'src/plugins/visualizations/public'; +import { toExpressionAst } from './to_ast'; +import { AggTypes, TableVisParams } from './types'; + +const mockSchemas = { + metric: [{ accessor: 1, format: { id: 'number' }, params: {}, label: 'Count', aggType: 'count' }], + bucket: [ + { + accessor: 0, + format: { id: 'date', params: { pattern: 'YYYY-MM-DD HH:mm' } }, + params: {}, + label: 'order_date per 3 hours', + aggType: 'date_histogram', + }, + ], +}; + +jest.mock('../../visualizations/public', () => ({ + getVisSchemas: () => mockSchemas, +})); + +describe('table vis toExpressionAst function', () => { + let vis: Vis; + + beforeEach(() => { + vis = { + isHierarchical: () => false, + type: {}, + params: { + showLabel: false, + }, + data: { + indexPattern: { id: '123' }, + aggs: { + getResponseAggs: () => [], + aggs: [], + }, + }, + } as any; + }); + + it('should match snapshot without params', () => { + const actual = toExpressionAst(vis, {} as any); + expect(actual).toMatchSnapshot(); + }); + + it('should match snapshot based on params & dimensions', () => { + vis.params = { + perPage: 20, + percentageCol: 'Count', + showMetricsAtAllLevels: true, + showPartialRows: true, + showTotal: true, + sort: { columnIndex: null, direction: null }, + totalFunc: AggTypes.SUM, + }; + const actual = toExpressionAst(vis, {} as any); + expect(actual).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/vis_type_table/public/to_ast.ts b/src/plugins/vis_type_table/public/to_ast.ts new file mode 100644 index 0000000000000..449e2dde7f7c9 --- /dev/null +++ b/src/plugins/vis_type_table/public/to_ast.ts @@ -0,0 +1,74 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { EsaggsExpressionFunctionDefinition } from '../../data/common/search/expressions'; +import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { getVisSchemas, Vis, BuildPipelineParams } from '../../visualizations/public'; +import { TableExpressionFunctionDefinition } from './table_vis_fn'; +import { TableVisConfig, TableVisParams } from './types'; + +const buildTableVisConfig = ( + schemas: ReturnType, + visParams: TableVisParams +) => { + const visConfig = {} as any; + const metrics = schemas.metric; + const buckets = schemas.bucket || []; + visConfig.dimensions = { + metrics, + buckets, + splitRow: schemas.split_row, + splitColumn: schemas.split_column, + }; + + if (visParams.showPartialRows && !visParams.showMetricsAtAllLevels) { + // Handle case where user wants to see partial rows but not metrics at all levels. + // This requires calculating how many metrics will come back in the tabified response, + // and removing all metrics from the dimensions except the last set. + const metricsPerBucket = metrics.length / buckets.length; + visConfig.dimensions.metrics.splice(0, metricsPerBucket * buckets.length - metricsPerBucket); + } + return visConfig; +}; + +export const toExpressionAst = (vis: Vis, params: BuildPipelineParams) => { + const esaggs = buildExpressionFunction('esaggs', { + index: vis.data.indexPattern!.id!, + metricsAtAllLevels: vis.isHierarchical(), + partialRows: vis.params.showPartialRows, + aggConfigs: JSON.stringify(vis.data.aggs!.aggs), + includeFormatHints: false, + }); + + const schemas = getVisSchemas(vis, params); + + const visConfig: TableVisConfig = { + ...vis.params, + ...buildTableVisConfig(schemas, vis.params), + title: vis.title, + }; + + const table = buildExpressionFunction('kibana_table', { + visConfig: JSON.stringify(visConfig), + }); + + const ast = buildExpression([esaggs, table]); + + return ast.toAst(); +}; diff --git a/src/plugins/vis_type_table/public/types.ts b/src/plugins/vis_type_table/public/types.ts index 39023d1305cb6..c0a995ad5da69 100644 --- a/src/plugins/vis_type_table/public/types.ts +++ b/src/plugins/vis_type_table/public/types.ts @@ -33,7 +33,6 @@ export interface Dimensions { } export interface TableVisParams { - type: 'table'; perPage: number | ''; showPartialRows: boolean; showMetricsAtAllLevels: boolean; @@ -44,5 +43,9 @@ export interface TableVisParams { showTotal: boolean; totalFunc: AggTypes; percentageCol: string; +} + +export interface TableVisConfig extends TableVisParams { + title: string; dimensions: Dimensions; } diff --git a/src/plugins/vis_type_timeseries/common/vis_schema.ts b/src/plugins/vis_type_timeseries/common/vis_schema.ts index b33215934c5df..40f776050617e 100644 --- a/src/plugins/vis_type_timeseries/common/vis_schema.ts +++ b/src/plugins/vis_type_timeseries/common/vis_schema.ts @@ -104,6 +104,7 @@ export const metricsItems = schema.object({ }) ) ), + numberOfSignificantValueDigits: numberOptional, percentiles: schema.maybe( schema.arrayOf( schema.object({ @@ -164,6 +165,7 @@ export const seriesItems = schema.object({ hide_in_legend: numberIntegerOptional, hidden: schema.maybe(schema.boolean()), id: stringRequired, + ignore_global_filter: numberOptional, label: stringOptionalNullable, line_width: numberOptionalOrEmptyString, metrics: schema.arrayOf(metricsItems), diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js index f12c0c8f6f465..a34b74d106492 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js @@ -24,10 +24,11 @@ import { FieldSelect } from './field_select'; import { AggRow } from './agg_row'; import { createChangeHandler } from '../lib/create_change_handler'; import { createSelectHandler } from '../lib/create_select_handler'; +import { createNumberHandler } from '../lib/create_number_handler'; import { htmlIdGenerator, EuiSpacer, - EuiFlexGroup, + EuiFlexGrid, EuiFlexItem, EuiFormLabel, EuiFormRow, @@ -35,6 +36,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; import { Percentiles, newPercentile } from './percentile_ui'; +import { PercentileHdr } from './percentile_hdr'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.HISTOGRAM]; @@ -46,6 +48,8 @@ export function PercentileAgg(props) { const handleChange = createChangeHandler(props.onChange, model); const handleSelectChange = createSelectHandler(handleChange); + const handleNumberChange = createNumberHandler(handleChange); + const indexPattern = (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; @@ -66,7 +70,7 @@ export function PercentileAgg(props) { siblings={props.siblings} dragHandleProps={props.dragHandleProps} > - + - - - - - + + + } + > + + + + + + + ); } diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx new file mode 100644 index 0000000000000..c0ba7d3c6765b --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { EuiFieldNumber, EuiFormRow, EuiIconTip } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export interface PercentileHdrProps { + value: number | undefined; + onChange: () => void; +} + +export const PercentileHdr = ({ value, onChange }: PercentileHdrProps) => ( + + {' '} + + } + type="questionInCircle" + /> + + } + > + + +); diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx index ef8876a19b1a6..444e11d0d563d 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx @@ -18,15 +18,8 @@ */ import React, { ChangeEvent } from 'react'; import { get } from 'lodash'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - htmlIdGenerator, - EuiFieldNumber, - EuiFlexGroup, - EuiFlexItem, - EuiFormLabel, - EuiSpacer, -} from '@elastic/eui'; + +import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { AddDeleteButtons } from '../../add_delete_buttons'; @@ -50,8 +43,6 @@ export const MultiValueRow = ({ disableAdd, disableDelete, }: MultiValueRowProps) => { - const htmlId = htmlIdGenerator(); - const onFieldNumberChange = (event: ChangeEvent) => onChange({ ...model, @@ -59,17 +50,9 @@ export const MultiValueRow = ({ }); return ( -
- - - - - - - + + + onDelete(model)} disableDelete={disableDelete} disableAdd={disableAdd} + responsive={false} /> - -
+ ); }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx index d02a16ade2bba..f78df9b1ddef4 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx @@ -20,11 +20,11 @@ import React from 'react'; import { htmlIdGenerator, - EuiFlexGroup, EuiFlexItem, EuiFormLabel, EuiFormRow, EuiSpacer, + EuiFlexGrid, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { AggSelect } from '../agg_select'; @@ -34,12 +34,16 @@ import { FieldSelect } from '../field_select'; import { createChangeHandler } from '../../lib/create_change_handler'; // @ts-ignore import { createSelectHandler } from '../../lib/create_select_handler'; +// @ts-ignore +import { createNumberHandler } from '../../lib/create_number_handler'; + import { AggRow } from '../agg_row'; import { PercentileRankValues } from './percentile_rank_values'; import { IFieldType, KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { MetricsItemsSchema, PanelSchema, SeriesItemsSchema } from '../../../../../common/types'; import { DragHandleProps } from '../../../../types'; +import { PercentileHdr } from '../percentile_hdr'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.HISTOGRAM]; @@ -67,6 +71,7 @@ export const PercentileRankAgg = (props: PercentileRankAggProps) => { const isTablePanel = panel.type === 'table'; const handleChange = createChangeHandler(props.onChange, model); const handleSelectChange = createSelectHandler(handleChange); + const handleNumberChange = createNumberHandler(handleChange); const handlePercentileRankValuesChange = (values: MetricsItemsSchema['values']) => { handleChange({ @@ -84,7 +89,7 @@ export const PercentileRankAgg = (props: PercentileRankAggProps) => { siblings={props.siblings} dragHandleProps={props.dragHandleProps} > - + { /> - - - {model.values && ( - - )} + + + + } + > + + + + + + + ); }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx index b66d79d67f427..92ca4dfb706ac 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { last } from 'lodash'; -import { EuiFlexGroup } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { MultiValueRow } from './multi_value_row'; interface PercentileRankValuesProps { @@ -52,19 +52,20 @@ export const PercentileRankValues = (props: PercentileRankValuesProps) => { disableDeleteRow: boolean; disableAddRow: boolean; }) => ( - + + + ); return ( - + {showOnlyLastRow && renderRow({ rowModel: { diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js index bd421248a3607..fb556a053df3f 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js @@ -19,6 +19,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; import _ from 'lodash'; import { collectionActions } from '../lib/collection_actions'; import { AddDeleteButtons } from '../add_delete_buttons'; @@ -27,17 +28,19 @@ import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, - EuiFormLabel, EuiComboBox, EuiFieldNumber, + EuiFormRow, + EuiFlexGrid, + EuiPanel, } from '@elastic/eui'; -import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage } from '@kbn/i18n/react'; export const newPercentile = (opts) => { return _.assign({ id: uuid.v1(), mode: 'line', shade: 0.2 }, opts); }; -class PercentilesUi extends Component { +export class Percentiles extends Component { handleTextChange(item, name) { return (e) => { const handleChange = collectionActions.handleChange.bind(null, this.props); @@ -50,22 +53,31 @@ class PercentilesUi extends Component { renderRow = (row, i, items) => { const defaults = { value: '', percentile: '', shade: '' }; const model = { ...defaults, ...row }; - const { intl, panel } = this.props; + const { panel } = this.props; + const flexItemStyle = { minWidth: 100 }; const percentileFieldNumber = ( - - + + + } + > + + ); @@ -77,99 +89,103 @@ class PercentilesUi extends Component { const handleDelete = collectionActions.handleDelete.bind(null, this.props, model); const modeOptions = [ { - label: intl.formatMessage({ - id: 'visTypeTimeseries.percentile.modeOptions.lineLabel', + label: i18n.translate('visTypeTimeseries.percentile.modeOptions.lineLabel', { defaultMessage: 'Line', }), value: 'line', }, { - label: intl.formatMessage({ - id: 'visTypeTimeseries.percentile.modeOptions.bandLabel', + label: i18n.translate('visTypeTimeseries.percentile.modeOptions.bandLabel', { defaultMessage: 'Band', }), value: 'band', }, ]; - const optionsStyle = {}; + const optionsStyle = { + ...flexItemStyle, + }; if (model.mode === 'line') { optionsStyle.display = 'none'; } - const labelStyle = { marginBottom: 0 }; + const htmlId = htmlIdGenerator(model.id); const selectedModeOption = modeOptions.find((option) => { return model.mode === option.value; }); return ( - - {percentileFieldNumber} - - - - - - - - - - - - - + + + + {percentileFieldNumber} + + + } + > + + + + + + } + > + + + + + + } + > + + + + + + + - - - - - - - - - - - - - - - - - - - + + + ); }; @@ -192,15 +208,13 @@ class PercentilesUi extends Component { } } -PercentilesUi.defaultProps = { +Percentiles.defaultProps = { name: 'percentile', }; -PercentilesUi.propTypes = { +Percentiles.propTypes = { name: PropTypes.string, model: PropTypes.object, panel: PropTypes.object, onChange: PropTypes.func, }; - -export const Percentiles = injectI18n(PercentilesUi); 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 c3e18ec202c0f..a219d7e8ee174 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 @@ -36,8 +36,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getDefaultQueryLanguage } from './lib/get_default_query_language'; -import { QueryBarWrapper } from './query_bar_wrapper'; +import { SeriesConfigQueryBarWithIgnoreGlobalFilter } from './series_config_query_bar_with_ignore_global_filter'; export const SeriesConfig = (props) => { const defaults = { offset_time: '', value_template: '' }; @@ -56,28 +55,12 @@ export const SeriesConfig = (props) => { - - } - fullWidth - > - props.onChange({ filter })} - indexPatterns={[seriesIndexPattern]} - /> - + @@ -162,6 +145,7 @@ export const SeriesConfig = (props) => { SeriesConfig.propTypes = { fields: PropTypes.object, + panel: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, indexPatternForQuery: PropTypes.string, diff --git a/src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js b/src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js new file mode 100644 index 0000000000000..b1275335522e6 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/series_config_query_bar_with_ignore_global_filter.js @@ -0,0 +1,104 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import PropTypes from 'prop-types'; +import React from 'react'; +import { htmlIdGenerator, EuiFlexItem, EuiFormRow, EuiToolTip, EuiFlexGroup } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { YesNo } from './yes_no'; +import { getDefaultQueryLanguage } from './lib/get_default_query_language'; +import { QueryBarWrapper } from './query_bar_wrapper'; + +export function SeriesConfigQueryBarWithIgnoreGlobalFilter({ + panel, + model, + onChange, + indexPatternForQuery, +}) { + const htmlId = htmlIdGenerator(); + const yesNoOption = ( + + ); + return ( + + + + } + fullWidth + > + onChange({ filter })} + indexPatterns={[indexPatternForQuery]} + /> + + + + + } + fullWidth + > + {panel.ignore_global_filter ? ( + + } + > + {yesNoOption} + + ) : ( + yesNoOption + )} + + + + ); +} + +SeriesConfigQueryBarWithIgnoreGlobalFilter.propTypes = { + onChange: PropTypes.func, + model: PropTypes.object, + panel: PropTypes.object, + indexPatternForQuery: PropTypes.string, +}; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.js index 851e900b5a622..7550f48386378 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/gauge/series.js @@ -90,6 +90,7 @@ function GaugeSeriesUi(props) { seriesBody = ( - - - } - fullWidth - > - props.onChange({ filter })} - indexPatterns={[seriesIndexPattern]} - /> - - + {type} @@ -584,6 +565,7 @@ export const TimeseriesConfig = injectI18n(function (props) { TimeseriesConfig.propTypes = { fields: PropTypes.object, model: PropTypes.object, + panel: PropTypes.object, onChange: PropTypes.func, indexPatternForQuery: PropTypes.string, seriesQuantity: PropTypes.object, diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/series.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/series.js index 06ad7e14bf358..680c1c5e78ad4 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/series.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/series.js @@ -97,6 +97,7 @@ const TimeseriesSeriesUI = injectI18n(function (props) { seriesBody = ( { const chartRef = useRef(); - const updateCursor = (_, cursor) => { - if (chartRef.current) { - chartRef.current.dispatchExternalPointerEvent(cursor); - } - }; useEffect(() => { + const updateCursor = (_, cursor) => { + if (chartRef.current) { + chartRef.current.dispatchExternalPointerEvent(cursor); + } + }; + eventBus.on(ACTIVE_CURSOR, updateCursor); return () => { eventBus.off(ACTIVE_CURSOR, undefined, updateCursor); }; - }, []); // eslint-disable-line + }, []); const tooltipFormatter = decorateFormatter(xAxisFormatter); const uiSettings = getUISettings(); @@ -139,6 +140,7 @@ export const TimeSeries = ({ type: tooltipMode === 'show_focused' ? TooltipType.Follow : TooltipType.VerticalCursor, headerFormatter: tooltipFormatter, }} + externalPointerEvents={{ tooltip: { visible: false } }} /> {annotations.map(({ id, data, icon, color }) => { diff --git a/src/plugins/vis_type_timeseries/server/lib/get_fields.ts b/src/plugins/vis_type_timeseries/server/lib/get_fields.ts index 26a1792e3ec70..682d0a071e50d 100644 --- a/src/plugins/vis_type_timeseries/server/lib/get_fields.ts +++ b/src/plugins/vis_type_timeseries/server/lib/get_fields.ts @@ -62,8 +62,11 @@ export async function getFields( let indexPatternString = indexPattern; if (!indexPatternString) { - const [, { data }] = await framework.core.getStartServices(); - const indexPatternsService = await data.indexPatterns.indexPatternsServiceFactory(request); + const [{ savedObjects }, { data }] = await framework.core.getStartServices(); + const savedObjectsClient = savedObjects.getScopedClient(request); + const indexPatternsService = await data.indexPatterns.indexPatternsServiceFactory( + savedObjectsClient + ); const defaultIndexPattern = await indexPatternsService.getDefault(); indexPatternString = get(defaultIndexPattern, 'title', ''); } diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js index 1eace13c2e336..232efc7514a5a 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js @@ -43,7 +43,6 @@ export async function getSeriesData(req, panel) { (acc, items) => acc.concat(items), [] ); - const data = await searchStrategy.search(req, searches); const handleResponseBodyFn = handleResponseBody(panel); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js index f033a43806312..dc2936072165e 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js @@ -64,6 +64,16 @@ function extendStatsBucket(bucket, metrics) { return body; } +function getPercentileHdrParam(bucket) { + return bucket.numberOfSignificantValueDigits + ? { + hdr: { + number_of_significant_value_digits: bucket.numberOfSignificantValueDigits, + }, + } + : undefined; +} + export const bucketTransform = { count: () => { return { @@ -139,13 +149,14 @@ export const bucketTransform = { bucket.percentiles.filter((p) => p.percentile).map((p) => p.percentile) ); } - const agg = { + + return { percentiles: { field: bucket.field, percents, + ...getPercentileHdrParam(bucket), }, }; - return agg; }, percentile_rank: (bucket) => { @@ -155,6 +166,7 @@ export const bucketTransform = { percentile_ranks: { field: bucket.field, values: (bucket.values || []).map((value) => (isEmpty(value) ? 0 : value)), + ...getPercentileHdrParam(bucket), }, }; }, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js index 906b3e71c7cb9..e0a4f90b1bb35 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js @@ -27,8 +27,9 @@ export function query(req, panel, series, esQueryConfig, indexPatternObject) { const { from, to } = offsetTime(req, series.offset_time); doc.size = 0; - const queries = !panel.ignore_global_filter ? req.payload.query : []; - const filters = !panel.ignore_global_filter ? req.payload.filters : []; + const ignoreGlobalFilter = panel.ignore_global_filter || series.ignore_global_filter; + const queries = !ignoreGlobalFilter ? req.payload.query : []; + const filters = !ignoreGlobalFilter ? req.payload.filters : []; doc.query = esQuery.buildEsQuery(indexPatternObject, queries, filters, esQueryConfig); const timerange = { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js index daf89b08fab4b..feb81d98f6246 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js @@ -318,4 +318,60 @@ describe('query(req, panel, series)', () => { }, }); }); + + test('returns doc with panel filter (ignoring globals from series)', () => { + req.payload.filters = [ + { + bool: { + must: [ + { + term: { + host: 'example', + }, + }, + ], + }, + }, + ]; + panel.filter = { query: 'host:web-server', language: 'lucene' }; + series.ignore_global_filter = true; + const next = (doc) => doc; + const doc = query(req, panel, series, config)(next)({}); + expect(doc).toEqual({ + size: 0, + query: { + bool: { + filter: [], + must: [ + { + range: { + timestamp: { + gte: '2017-01-01T00:00:00.000Z', + lte: '2017-01-01T01:00:00.000Z', + format: 'strict_date_optional_time', + }, + }, + }, + { + bool: { + filter: [], + must: [ + { + query_string: { + query: panel.filter.query, + analyze_wildcard: true, + }, + }, + ], + must_not: [], + should: [], + }, + }, + ], + must_not: [], + should: [], + }, + }, + }); + }); }); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js index a11324f73e611..c163605af7ac5 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js @@ -32,7 +32,7 @@ export function percentileRank(resp, panel, series, meta) { } getSplits(resp, panel, series, meta).forEach((split) => { - (metric.values || []).forEach((percentileRank) => { + (metric.values || []).forEach((percentileRank, index) => { const data = split.timeseries.buckets.map((bucket) => [ bucket.key, getAggValue(bucket, { @@ -43,7 +43,7 @@ export function percentileRank(resp, panel, series, meta) { results.push({ data, - id: `${split.id}:${percentileRank}`, + id: `${split.id}:${percentileRank}:${index}`, label: `${split.label} (${percentileRank || 0})`, color: split.color, ...getDefaultDecoration(series), diff --git a/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.mock.ts b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.mock.ts new file mode 100644 index 0000000000000..70be4c273b77f --- /dev/null +++ b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.mock.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const mockStats = { somestat: 1 }; +export const mockGetStats = jest.fn().mockResolvedValue(mockStats); +jest.doMock('./get_usage_collector', () => ({ + getStats: mockGetStats, +})); diff --git a/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.test.ts b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.test.ts index f2854896791c1..891ebf6582670 100644 --- a/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.test.ts +++ b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.test.ts @@ -17,10 +17,8 @@ * under the License. */ -import { of } from 'rxjs'; - import { LegacyAPICaller } from 'src/core/server'; -import { getUsageCollector } from './get_usage_collector'; +import { getStats } from './get_usage_collector'; import { HomeServerPluginSetup } from '../../../home/server'; const mockedSavedObjects = [ @@ -76,8 +74,8 @@ const getMockCallCluster = (hits?: unknown[]) => jest.fn().mockReturnValue(Promise.resolve({ hits: { hits } }) as unknown) as LegacyAPICaller; describe('Vega visualization usage collector', () => { - const configMock = of({ kibana: { index: '' } }); - const usageCollector = getUsageCollector(configMock, { + const mockIndex = 'mock_index'; + const mockDeps = { home: ({ sampleData: { getSampleDatasets: jest.fn().mockReturnValue([ @@ -100,44 +98,37 @@ describe('Vega visualization usage collector', () => { ]), }, } as unknown) as HomeServerPluginSetup, - }); - - test('Should fit the shape', () => { - expect(usageCollector.type).toBe('vis_type_vega'); - expect(usageCollector.isReady()).toBe(true); - expect(usageCollector.fetch).toEqual(expect.any(Function)); - }); + }; test('Returns undefined when no results found (undefined)', async () => { - const result = await usageCollector.fetch(getMockCallCluster()); + const result = await getStats(getMockCallCluster(), mockIndex, mockDeps); expect(result).toBeUndefined(); }); test('Returns undefined when no results found (0 results)', async () => { - const result = await usageCollector.fetch(getMockCallCluster([])); + const result = await getStats(getMockCallCluster([]), mockIndex, mockDeps); expect(result).toBeUndefined(); }); test('Returns undefined when no vega saved objects found', async () => { - const result = await usageCollector.fetch( - getMockCallCluster([ - { - _id: 'visualization:myvis-123', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "area"}' }, - }, + const mockCallCluster = getMockCallCluster([ + { + _id: 'visualization:myvis-123', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "area"}' }, }, - ]) - ); + }, + ]); + const result = await getStats(mockCallCluster, mockIndex, mockDeps); expect(result).toBeUndefined(); }); test('Should ingnore sample data visualizations', async () => { - const callCluster = getMockCallCluster([ + const mockCallCluster = getMockCallCluster([ { _id: 'visualization:sampledata-123', _source: { @@ -155,13 +146,14 @@ describe('Vega visualization usage collector', () => { }, ]); - const result = await usageCollector.fetch(callCluster); + const result = await getStats(mockCallCluster, mockIndex, mockDeps); expect(result).toBeUndefined(); }); test('Summarizes visualizations response data', async () => { - const result = await usageCollector.fetch(getMockCallCluster(mockedSavedObjects)); + const mockCallCluster = getMockCallCluster(mockedSavedObjects); + const result = await getStats(mockCallCluster, mockIndex, mockDeps); expect(result).toMatchObject({ vega_lib_specs_total: 2, diff --git a/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.ts b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.ts index d5e691ca48301..b0de8eb2f5140 100644 --- a/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/vis_type_vega/server/usage_collector/get_usage_collector.ts @@ -17,23 +17,16 @@ * under the License. */ import { parse } from 'hjson'; -import { first } from 'rxjs/operators'; import { SearchResponse } from 'elasticsearch'; import { LegacyAPICaller, SavedObject } from 'src/core/server'; -import { - ConfigObservable, - VegaSavedObjectAttributes, - VisTypeVegaPluginSetupDependencies, -} from '../types'; +import { VegaSavedObjectAttributes, VisTypeVegaPluginSetupDependencies } from '../types'; type UsageCollectorDependencies = Pick; type ESResponse = SearchResponse<{ visualization: { visState: string } }>; type VegaType = 'vega' | 'vega-lite'; -const VEGA_USAGE_TYPE = 'vis_type_vega'; - function isVegaType(attributes: any): attributes is VegaSavedObjectAttributes { return attributes && attributes.type === 'vega' && attributes.params?.spec; } @@ -64,11 +57,25 @@ const getDefaultVegaVisualizations = (home: UsageCollectorDependencies['home']) return titles; }; -const getStats = async ( +export interface VegaUsage { + vega_lib_specs_total: number; + vega_lite_lib_specs_total: number; + vega_use_map_total: number; +} + +export const getStats = async ( callCluster: LegacyAPICaller, index: string, { home }: UsageCollectorDependencies -) => { +): Promise => { + let shouldPublishTelemetry = false; + + const vegaUsage = { + vega_lib_specs_total: 0, + vega_lite_lib_specs_total: 0, + vega_use_map_total: 0, + }; + const searchParams = { size: 10000, index, @@ -82,9 +89,9 @@ const getStats = async ( }, }, }; + const esResponse: ESResponse = await callCluster('search', searchParams); const size = esResponse?.hits?.hits?.length ?? 0; - let shouldPublishTelemetry = false; if (!size) { return; @@ -93,55 +100,32 @@ const getStats = async ( // we want to exclude the Vega Sample Data visualizations from the stats // in order to have more accurate results const excludedFromStatsVisualizations = getDefaultVegaVisualizations(home); + for (const hit of esResponse.hits.hits) { + const visualization = hit._source?.visualization; + const visState = JSON.parse(visualization?.visState ?? '{}'); + + if (isVegaType(visState) && !excludedFromStatsVisualizations.includes(visState.title)) { + try { + const spec = parse(visState.params.spec, { legacyRoot: false }); + + if (spec) { + shouldPublishTelemetry = true; - const finalTelemetry = esResponse.hits.hits.reduce( - (telemetry, hit) => { - const visualization = hit._source?.visualization; - const visState = JSON.parse(visualization?.visState ?? '{}'); - - if (isVegaType(visState) && !excludedFromStatsVisualizations.includes(visState.title)) - try { - const spec = parse(visState.params.spec, { legacyRoot: false }); - - if (spec) { - shouldPublishTelemetry = true; - if (checkVegaSchemaType(spec.$schema, 'vega')) { - telemetry.vega_lib_specs_total++; - } - if (checkVegaSchemaType(spec.$schema, 'vega-lite')) { - telemetry.vega_lite_lib_specs_total++; - } - if (spec.config?.kibana?.type === 'map') { - telemetry.vega_use_map_total++; - } + if (checkVegaSchemaType(spec.$schema, 'vega')) { + vegaUsage.vega_lib_specs_total++; + } + if (checkVegaSchemaType(spec.$schema, 'vega-lite')) { + vegaUsage.vega_lite_lib_specs_total++; + } + if (spec.config?.kibana?.type === 'map') { + vegaUsage.vega_use_map_total++; } - } catch (e) { - // Let it go, the data is invalid and we'll don't need to handle it } - - return telemetry; - }, - { - vega_lib_specs_total: 0, - vega_lite_lib_specs_total: 0, - vega_use_map_total: 0, + } catch (e) { + // Let it go, the data is invalid and we'll don't need to handle it + } } - ); + } - return shouldPublishTelemetry ? finalTelemetry : undefined; + return shouldPublishTelemetry ? vegaUsage : undefined; }; - -export function getUsageCollector( - config: ConfigObservable, - dependencies: UsageCollectorDependencies -) { - return { - type: VEGA_USAGE_TYPE, - isReady: () => true, - fetch: async (callCluster: LegacyAPICaller) => { - const { index } = (await config.pipe(first()).toPromise()).kibana; - - return await getStats(callCluster, index, dependencies); - }, - }; -} diff --git a/src/plugins/vis_type_vega/server/usage_collector/index.ts b/src/plugins/vis_type_vega/server/usage_collector/index.ts index 4fc12a14054ec..decc3ef1ff1d9 100644 --- a/src/plugins/vis_type_vega/server/usage_collector/index.ts +++ b/src/plugins/vis_type_vega/server/usage_collector/index.ts @@ -17,16 +17,4 @@ * under the License. */ -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { getUsageCollector } from './get_usage_collector'; -import { ConfigObservable, VisTypeVegaPluginSetupDependencies } from '../types'; - -export function registerVegaUsageCollector( - collectorSet: UsageCollectionSetup, - config: ConfigObservable, - dependencies: Pick -) { - const collector = collectorSet.makeUsageCollector(getUsageCollector(config, dependencies)); - - collectorSet.registerCollector(collector); -} +export { registerVegaUsageCollector } from './register_vega_collector'; diff --git a/src/plugins/vis_type_vega/server/usage_collector/register_vega_collector.test.ts b/src/plugins/vis_type_vega/server/usage_collector/register_vega_collector.test.ts new file mode 100644 index 0000000000000..433b786ed46a2 --- /dev/null +++ b/src/plugins/vis_type_vega/server/usage_collector/register_vega_collector.test.ts @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { of } from 'rxjs'; +import { mockStats, mockGetStats } from './get_usage_collector.mock'; +import { createUsageCollectionSetupMock } from 'src/plugins/usage_collection/server/usage_collection.mock'; +import { HomeServerPluginSetup } from '../../../home/server'; +import { registerVegaUsageCollector } from './register_vega_collector'; + +describe('registerVegaUsageCollector', () => { + const mockIndex = 'mock_index'; + const mockDeps = { home: ({} as unknown) as HomeServerPluginSetup }; + const mockConfig = of({ kibana: { index: mockIndex } }); + + it('makes a usage collector and registers it`', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVegaUsageCollector(mockCollectorSet, mockConfig, mockDeps); + expect(mockCollectorSet.makeUsageCollector).toBeCalledTimes(1); + expect(mockCollectorSet.registerCollector).toBeCalledTimes(1); + }); + + it('makeUsageCollector configs fit the shape', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVegaUsageCollector(mockCollectorSet, mockConfig, mockDeps); + expect(mockCollectorSet.makeUsageCollector).toHaveBeenCalledWith({ + type: 'vis_type_vega', + isReady: expect.any(Function), + fetch: expect.any(Function), + schema: expect.any(Object), + }); + const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; + expect(usageCollectorConfig.isReady()).toBe(true); + }); + + it('makeUsageCollector config.isReady returns true', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVegaUsageCollector(mockCollectorSet, mockConfig, mockDeps); + const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; + expect(usageCollectorConfig.isReady()).toBe(true); + }); + + it('makeUsageCollector config.fetch calls getStats', async () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVegaUsageCollector(mockCollectorSet, mockConfig, mockDeps); + const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; + const mockCallCluster = jest.fn(); + const fetchResult = await usageCollectorConfig.fetch(mockCallCluster); + expect(mockGetStats).toBeCalledTimes(1); + expect(mockGetStats).toBeCalledWith(mockCallCluster, mockIndex, mockDeps); + expect(fetchResult).toBe(mockStats); + }); +}); diff --git a/src/plugins/vis_type_vega/server/usage_collector/register_vega_collector.ts b/src/plugins/vis_type_vega/server/usage_collector/register_vega_collector.ts new file mode 100644 index 0000000000000..af62821f7cdc0 --- /dev/null +++ b/src/plugins/vis_type_vega/server/usage_collector/register_vega_collector.ts @@ -0,0 +1,46 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { first } from 'rxjs/operators'; +import { getStats, VegaUsage } from './get_usage_collector'; +import { ConfigObservable, VisTypeVegaPluginSetupDependencies } from '../types'; + +export function registerVegaUsageCollector( + collectorSet: UsageCollectionSetup, + config: ConfigObservable, + dependencies: Pick +) { + const collector = collectorSet.makeUsageCollector({ + type: 'vis_type_vega', + isReady: () => true, + schema: { + vega_lib_specs_total: { type: 'long' }, + vega_lite_lib_specs_total: { type: 'long' }, + vega_use_map_total: { type: 'long' }, + }, + fetch: async (callCluster) => { + const { index } = (await config.pipe(first()).toPromise()).kibana; + + return await getStats(callCluster, index, dependencies); + }, + }); + + collectorSet.registerCollector(collector); +} diff --git a/src/plugins/visualizations/kibana.json b/src/plugins/visualizations/kibana.json index bf36bb35d0563..688987b1104a1 100644 --- a/src/plugins/visualizations/kibana.json +++ b/src/plugins/visualizations/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "inspector", "dashboard"], + "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "inspector" ], "optionalPlugins": ["usageCollection"], "requiredBundles": ["kibanaUtils", "discover", "savedObjects"] } diff --git a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts index b27d24d980e8d..36f3f7d6ed22e 100644 --- a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts +++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts @@ -25,7 +25,11 @@ import { VisualizeByReferenceInput, VisualizeSavedObjectAttributes, } from './visualize_embeddable'; -import { IContainer, ErrorEmbeddable } from '../../../../plugins/embeddable/public'; +import { + IContainer, + ErrorEmbeddable, + AttributeService, +} from '../../../../plugins/embeddable/public'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { getSavedVisualizationsLoader, @@ -37,7 +41,6 @@ import { import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants'; import { SavedVisualizationsLoader } from '../saved_visualizations'; -import { AttributeService } from '../../../dashboard/public'; export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDeps) => async ( vis: Vis, diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index fe8a9adff4052..a810b4b65528f 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -38,6 +38,7 @@ import { Adapters, SavedObjectEmbeddableInput, ReferenceOrValueEmbeddable, + AttributeService, } from '../../../../plugins/embeddable/public'; import { IExpressionLoaderParams, @@ -51,7 +52,6 @@ import { VIS_EVENT_TO_TRIGGER } from './events'; import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; import { TriggerId } from '../../../ui_actions/public'; import { SavedObjectAttributes } from '../../../../core/types'; -import { AttributeService } from '../../../dashboard/public'; import { SavedVisualizationsLoader } from '../saved_visualizations'; import { VisSavedObject } from '../types'; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 87f78f5639ff0..4b851da6be70e 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -26,6 +26,7 @@ import { EmbeddableOutput, ErrorEmbeddable, IContainer, + AttributeService, } from '../../../embeddable/public'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { @@ -50,7 +51,6 @@ import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_obje import { StartServicesGetter } from '../../../kibana_utils/public'; import { VisualizationsStartDeps } from '../plugin'; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants'; -import { AttributeService } from '../../../dashboard/public'; import { checkForDuplicateTitle } from '../../../saved_objects/public'; interface VisualizationAttributes extends SavedObjectAttributes { @@ -126,7 +126,7 @@ export class VisualizeEmbeddableFactory if (!this.attributeService) { this.attributeService = await this.deps .start() - .plugins.dashboard.getAttributeService< + .plugins.embeddable.getAttributeService< VisualizeSavedObjectAttributes, VisualizeByValueInput, VisualizeByReferenceInput diff --git a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap index c0c37e2262f9c..cbdecd4aac747 100644 --- a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap +++ b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap @@ -12,16 +12,6 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunct exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles region_map function without buckets 1`] = `"regionmap visConfig='{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with showPartialRows=true and showMetricsAtAllLevels=false 1`] = `"kibana_table visConfig='{\\"showMetricsAtAllLevels\\":false,\\"showPartialRows\\":true,\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":4,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":5,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[0,3]}}' "`; - -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with showPartialRows=true and showMetricsAtAllLevels=true 1`] = `"kibana_table visConfig='{\\"showMetricsAtAllLevels\\":true,\\"showPartialRows\\":true,\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":1,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":2,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":4,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":5,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[0,3]}}' "`; - -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with splits 1`] = `"kibana_table visConfig='{\\"foo\\":\\"bar\\",\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[],\\"splitRow\\":[1,2]}}' "`; - -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function with splits and buckets 1`] = `"kibana_table visConfig='{\\"foo\\":\\"bar\\",\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":1,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[3],\\"splitRow\\":[2,4]}}' "`; - -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles table function without splits or buckets 1`] = `"kibana_table visConfig='{\\"foo\\":\\"bar\\",\\"dimensions\\":{\\"metrics\\":[{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},{\\"accessor\\":1,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"}],\\"buckets\\":[]}}' "`; - exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tile_map function 1`] = `"tilemap visConfig='{\\"metric\\":{},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},\\"geohash\\":1,\\"geocentroid\\":3}}' "`; exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles vega function 1`] = `"vega spec='this is a test' "`; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts index a1fea45f51781..c744043ed155b 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts @@ -117,84 +117,6 @@ describe('visualize loader pipeline helpers: build pipeline', () => { expect(actual).toMatchSnapshot(); }); - describe('handles table function', () => { - it('without splits or buckets', () => { - const params = { foo: 'bar' }; - const schemas = { - ...schemasDef, - metric: [ - { ...schemaConfig, accessor: 0 }, - { ...schemaConfig, accessor: 1 }, - ], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - - it('with splits', () => { - const params = { foo: 'bar' }; - const schemas = { - ...schemasDef, - split_row: [1, 2], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - - it('with splits and buckets', () => { - const params = { foo: 'bar' }; - const schemas = { - ...schemasDef, - metric: [ - { ...schemaConfig, accessor: 0 }, - { ...schemaConfig, accessor: 1 }, - ], - split_row: [2, 4], - bucket: [3], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - - it('with showPartialRows=true and showMetricsAtAllLevels=true', () => { - const params = { - showMetricsAtAllLevels: true, - showPartialRows: true, - }; - const schemas = { - ...schemasDef, - metric: [ - { ...schemaConfig, accessor: 1 }, - { ...schemaConfig, accessor: 2 }, - { ...schemaConfig, accessor: 4 }, - { ...schemaConfig, accessor: 5 }, - ], - bucket: [0, 3], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - - it('with showPartialRows=true and showMetricsAtAllLevels=false', () => { - const params = { - showMetricsAtAllLevels: false, - showPartialRows: true, - }; - const schemas = { - ...schemasDef, - metric: [ - { ...schemaConfig, accessor: 1 }, - { ...schemaConfig, accessor: 2 }, - { ...schemaConfig, accessor: 4 }, - { ...schemaConfig, accessor: 5 }, - ], - bucket: [0, 3], - }; - const actual = buildPipelineVisFunction.table(params, schemas, uiState); - expect(actual).toMatchSnapshot(); - }); - }); - describe('handles region_map function', () => { it('without buckets', () => { const params = { metric: {} }; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index 9f6a4d5553292..eb431212166a3 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -39,14 +39,14 @@ export interface SchemaConfig { export interface Schemas { metric: SchemaConfig[]; - bucket?: any[]; + bucket?: SchemaConfig[]; geo_centroid?: any[]; group?: any[]; params?: any[]; radius?: any[]; segment?: any[]; - split_column?: any[]; - split_row?: any[]; + split_column?: SchemaConfig[]; + split_row?: SchemaConfig[]; width?: any[]; // catch all for schema name [key: string]: any[] | undefined; @@ -267,13 +267,6 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = { const paramsArray = [paramsJson, uiStateJson].filter((param) => Boolean(param)); return `tsvb ${paramsArray.join(' ')}`; }, - table: (params, schemas) => { - const visConfig = { - ...params, - ...buildVisConfig.table(schemas, params), - }; - return `kibana_table ${prepareJson('visConfig', visConfig)}`; - }, region_map: (params, schemas) => { const visConfig = { ...params, @@ -298,26 +291,6 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = { }; const buildVisConfig: BuildVisConfigFunction = { - table: (schemas, visParams = {}) => { - const visConfig = {} as any; - const metrics = schemas.metric; - const buckets = schemas.bucket || []; - visConfig.dimensions = { - metrics, - buckets, - splitRow: schemas.split_row, - splitColumn: schemas.split_column, - }; - - if (visParams.showMetricsAtAllLevels === false && visParams.showPartialRows === true) { - // Handle case where user wants to see partial rows but not metrics at all levels. - // This requires calculating how many metrics will come back in the tabified response, - // and removing all metrics from the dimensions except the last set. - const metricsPerBucket = metrics.length / buckets.length; - visConfig.dimensions.metrics.splice(0, metricsPerBucket * buckets.length - metricsPerBucket); - } - return visConfig; - }, region_map: (schemas) => { const visConfig = {} as any; visConfig.metric = schemas.metric[0]; diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 37a9972983421..be7629ef41145 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -112,7 +112,7 @@ export interface VisualizationsStartDeps { uiActions: UiActionsStart; application: ApplicationStart; dashboard: DashboardStart; - getAttributeService: DashboardStart['getAttributeService']; + getAttributeService: EmbeddableStart['getAttributeService']; savedObjectsClient: SavedObjectsClientContract; } diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts index ad7618a8640ba..5261b2cac7dcf 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization.ts @@ -45,13 +45,13 @@ export const visualizationSavedObjectType: SavedObjectsType = { properties: { description: { type: 'text' }, kibanaSavedObjectMeta: { - properties: { searchSourceJSON: { type: 'text', index: false, doc_values: false } }, + properties: { searchSourceJSON: { type: 'text', index: false } }, }, savedSearchRefName: { type: 'keyword', index: false, doc_values: false }, title: { type: 'text' }, - uiStateJSON: { type: 'text', index: false, doc_values: false }, + uiStateJSON: { type: 'text', index: false }, version: { type: 'integer' }, - visState: { type: 'text', index: false, doc_values: false }, + visState: { type: 'text', index: false }, }, }, migrations: visualizationSavedObjectTypeMigrations, diff --git a/src/plugins/visualizations/server/usage_collector/get_usage_collector.mock.ts b/src/plugins/visualizations/server/usage_collector/get_usage_collector.mock.ts new file mode 100644 index 0000000000000..70be4c273b77f --- /dev/null +++ b/src/plugins/visualizations/server/usage_collector/get_usage_collector.mock.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const mockStats = { somestat: 1 }; +export const mockGetStats = jest.fn().mockResolvedValue(mockStats); +jest.doMock('./get_usage_collector', () => ({ + getStats: mockGetStats, +})); diff --git a/src/plugins/visualizations/server/usage_collector/get_usage_collector.test.ts b/src/plugins/visualizations/server/usage_collector/get_usage_collector.test.ts index 108f270bc8eab..fd363b3505186 100644 --- a/src/plugins/visualizations/server/usage_collector/get_usage_collector.test.ts +++ b/src/plugins/visualizations/server/usage_collector/get_usage_collector.test.ts @@ -18,10 +18,8 @@ */ import moment from 'moment'; -import { of } from 'rxjs'; - import { LegacyAPICaller } from 'src/core/server'; -import { getUsageCollector } from './get_usage_collector'; +import { getStats } from './get_usage_collector'; const defaultMockSavedObjects = [ { @@ -121,29 +119,22 @@ const enlargedMockSavedObjects = [ ]; describe('Visualizations usage collector', () => { - const configMock = of({ kibana: { index: '' } }); - const usageCollector = getUsageCollector(configMock); + const mockIndex = ''; const getMockCallCluster = (hits: unknown[]) => (() => Promise.resolve({ hits: { hits } }) as unknown) as LegacyAPICaller; - test('Should fit the shape', () => { - expect(usageCollector.type).toBe('visualization_types'); - expect(usageCollector.isReady()).toBe(true); - expect(usageCollector.fetch).toEqual(expect.any(Function)); - }); - test('Returns undefined when no results found (undefined)', async () => { - const result = await usageCollector.fetch(getMockCallCluster(undefined as any)); - expect(result).toBe(undefined); + const result = await getStats(getMockCallCluster(undefined as any), mockIndex); + expect(result).toBeUndefined(); }); test('Returns undefined when no results found (0 results)', async () => { - const result = await usageCollector.fetch(getMockCallCluster([])); - expect(result).toBe(undefined); + const result = await getStats(getMockCallCluster([]), mockIndex); + expect(result).toBeUndefined(); }); test('Summarizes visualizations response data', async () => { - const result = await usageCollector.fetch(getMockCallCluster(defaultMockSavedObjects)); + const result = await getStats(getMockCallCluster(defaultMockSavedObjects), mockIndex); expect(result).toMatchObject({ shell_beads: { @@ -198,7 +189,7 @@ describe('Visualizations usage collector', () => { }, }; - const result = await usageCollector.fetch(getMockCallCluster(enlargedMockSavedObjects)); + const result = await getStats(getMockCallCluster(enlargedMockSavedObjects), mockIndex); expect(result).toMatchObject(expectedStats); }); diff --git a/src/plugins/visualizations/server/usage_collector/get_usage_collector.ts b/src/plugins/visualizations/server/usage_collector/get_usage_collector.ts index 9ba09a3303b5f..aed9a54dcf01a 100644 --- a/src/plugins/visualizations/server/usage_collector/get_usage_collector.ts +++ b/src/plugins/visualizations/server/usage_collector/get_usage_collector.ts @@ -17,16 +17,12 @@ * under the License. */ -import { Observable } from 'rxjs'; import { countBy, get, groupBy, mapValues, max, min, values } from 'lodash'; -import { first } from 'rxjs/operators'; import { SearchResponse } from 'elasticsearch'; import { LegacyAPICaller } from 'src/core/server'; import { getPastDays } from './get_past_days'; -const VIS_USAGE_TYPE = 'visualization_types'; - type ESResponse = SearchResponse<{ visualization: { visState: string } }>; interface VisSummary { @@ -35,10 +31,25 @@ interface VisSummary { past_days: number; } +export interface VisualizationUsage { + [x: string]: { + total: number; + spaces_min?: number; + spaces_max?: number; + spaces_avg: number; + saved_7_days_total: number; + saved_30_days_total: number; + saved_90_days_total: number; + }; +} + /* * Parse the response data into telemetry payload */ -async function getStats(callCluster: LegacyAPICaller, index: string) { +export async function getStats( + callCluster: LegacyAPICaller, + index: string +): Promise { const searchParams = { size: 10000, // elasticsearch index.max_result_window default value index, @@ -94,14 +105,3 @@ async function getStats(callCluster: LegacyAPICaller, index: string) { }; }); } - -export function getUsageCollector(config: Observable<{ kibana: { index: string } }>) { - return { - type: VIS_USAGE_TYPE, - isReady: () => true, - fetch: async (callCluster: LegacyAPICaller) => { - const index = (await config.pipe(first()).toPromise()).kibana.index; - return await getStats(callCluster, index); - }, - }; -} diff --git a/src/plugins/visualizations/server/usage_collector/index.ts b/src/plugins/visualizations/server/usage_collector/index.ts index ed890a6b5e48a..fb7e895d9d64a 100644 --- a/src/plugins/visualizations/server/usage_collector/index.ts +++ b/src/plugins/visualizations/server/usage_collector/index.ts @@ -17,15 +17,4 @@ * under the License. */ -import { Observable } from 'rxjs'; - -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { getUsageCollector } from './get_usage_collector'; - -export function registerVisualizationsCollector( - collectorSet: UsageCollectionSetup, - config: Observable<{ kibana: { index: string } }> -) { - const collector = collectorSet.makeUsageCollector(getUsageCollector(config)); - collectorSet.registerCollector(collector); -} +export { registerVisualizationsCollector } from './register_visualizations_collector'; diff --git a/src/plugins/visualizations/server/usage_collector/register_visualizations_collector.test.ts b/src/plugins/visualizations/server/usage_collector/register_visualizations_collector.test.ts new file mode 100644 index 0000000000000..38d88dd65001b --- /dev/null +++ b/src/plugins/visualizations/server/usage_collector/register_visualizations_collector.test.ts @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { of } from 'rxjs'; +import { mockStats, mockGetStats } from './get_usage_collector.mock'; +import { createUsageCollectionSetupMock } from 'src/plugins/usage_collection/server/usage_collection.mock'; + +import { registerVisualizationsCollector } from './register_visualizations_collector'; + +describe('registerVisualizationsCollector', () => { + const mockIndex = 'mock_index'; + const mockConfig = of({ kibana: { index: mockIndex } }); + + it('makes a usage collector and registers it`', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVisualizationsCollector(mockCollectorSet, mockConfig); + expect(mockCollectorSet.makeUsageCollector).toBeCalledTimes(1); + expect(mockCollectorSet.registerCollector).toBeCalledTimes(1); + }); + + it('makeUsageCollector configs fit the shape', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVisualizationsCollector(mockCollectorSet, mockConfig); + expect(mockCollectorSet.makeUsageCollector).toHaveBeenCalledWith({ + type: 'visualization_types', + isReady: expect.any(Function), + fetch: expect.any(Function), + schema: expect.any(Object), + }); + const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; + expect(usageCollectorConfig.isReady()).toBe(true); + }); + + it('makeUsageCollector config.isReady returns true', () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVisualizationsCollector(mockCollectorSet, mockConfig); + const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; + expect(usageCollectorConfig.isReady()).toBe(true); + }); + + it('makeUsageCollector config.fetch calls getStats', async () => { + const mockCollectorSet = createUsageCollectionSetupMock(); + registerVisualizationsCollector(mockCollectorSet, mockConfig); + const usageCollectorConfig = mockCollectorSet.makeUsageCollector.mock.calls[0][0]; + const mockCallCluster = jest.fn(); + const fetchResult = await usageCollectorConfig.fetch(mockCallCluster); + expect(mockGetStats).toBeCalledTimes(1); + expect(mockGetStats).toBeCalledWith(mockCallCluster, mockIndex); + expect(fetchResult).toBe(mockStats); + }); +}); diff --git a/src/plugins/visualizations/server/usage_collector/register_visualizations_collector.ts b/src/plugins/visualizations/server/usage_collector/register_visualizations_collector.ts new file mode 100644 index 0000000000000..5919b3d20642f --- /dev/null +++ b/src/plugins/visualizations/server/usage_collector/register_visualizations_collector.ts @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Observable } from 'rxjs'; +import { first } from 'rxjs/operators'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; + +import { getStats, VisualizationUsage } from './get_usage_collector'; + +export function registerVisualizationsCollector( + collectorSet: UsageCollectionSetup, + config: Observable<{ kibana: { index: string } }> +) { + const collector = collectorSet.makeUsageCollector({ + type: 'visualization_types', + isReady: () => true, + schema: { + DYNAMIC_KEY: { + total: { type: 'long' }, + spaces_min: { type: 'long' }, + spaces_max: { type: 'long' }, + spaces_avg: { type: 'long' }, + saved_7_days_total: { type: 'long' }, + saved_30_days_total: { type: 'long' }, + saved_90_days_total: { type: 'long' }, + }, + }, + fetch: async (callCluster) => { + const index = (await config.pipe(first()).toPromise()).kibana.index; + return await getStats(callCluster, index); + }, + }); + collectorSet.registerCollector(collector); +} diff --git a/test/accessibility/services/a11y/analyze_with_axe.js b/test/accessibility/services/a11y/analyze_with_axe.js index 6d4aa1491278e..aaecdbeb0e95d 100644 --- a/test/accessibility/services/a11y/analyze_with_axe.js +++ b/test/accessibility/services/a11y/analyze_with_axe.js @@ -33,6 +33,14 @@ export function analyzeWithAxe(context, options, callback) { id: 'aria-required-children', selector: '[data-skip-axe="aria-required-children"] > *', }, + { + id: 'label', + selector: '[data-test-subj="comboBoxSearchInput"] *', + }, + { + id: 'aria-roles', + selector: '[data-test-subj="comboBoxSearchInput"] *', + }, ], }); return window.axe.run(context, options); diff --git a/test/api_integration/apis/index_patterns/fields_for_wildcard_route/response.js b/test/api_integration/apis/index_patterns/fields_for_wildcard_route/response.js index 45a09be6c2deb..ae96f17fdceb9 100644 --- a/test/api_integration/apis/index_patterns/fields_for_wildcard_route/response.js +++ b/test/api_integration/apis/index_patterns/fields_for_wildcard_route/response.js @@ -28,12 +28,12 @@ export default function ({ getService }) { expect(resp.body.fields).to.eql(sortBy(resp.body.fields, 'name')); }; - describe('response', () => { + describe('fields_for_wildcard_route response', () => { before(() => esArchiver.load('index_patterns/basic_index')); after(() => esArchiver.unload('index_patterns/basic_index')); - it('returns a flattened version of the fields in es', () => - supertest + it('returns a flattened version of the fields in es', async () => { + await supertest .get('/api/index_patterns/_fields_for_wildcard') .query({ pattern: 'basic_index' }) .expect(200, { @@ -86,10 +86,12 @@ export default function ({ getService }) { }, ], }) - .then(ensureFieldsAreSorted)); + .then(ensureFieldsAreSorted); + }); - it('always returns a field for all passed meta fields', () => - supertest + // https://github.com/elastic/kibana/issues/79813 + it.skip('always returns a field for all passed meta fields', async () => { + await supertest .get('/api/index_patterns/_fields_for_wildcard') .query({ pattern: 'basic_index', @@ -168,14 +170,16 @@ export default function ({ getService }) { }, ], }) - .then(ensureFieldsAreSorted)); + .then(ensureFieldsAreSorted); + }); - it('returns 404 when the pattern does not exist', () => - supertest + it('returns 404 when the pattern does not exist', async () => { + await supertest .get('/api/index_patterns/_fields_for_wildcard') .query({ pattern: '[non-existing-pattern]its-invalid-*', }) - .expect(404)); + .expect(404); + }); }); } diff --git a/test/common/config.js b/test/common/config.js index dbbd75d1f9577..9d6d531ae4b37 100644 --- a/test/common/config.js +++ b/test/common/config.js @@ -48,6 +48,8 @@ export default function () { `--elasticsearch.username=${kibanaServerTestUser.username}`, `--elasticsearch.password=${kibanaServerTestUser.password}`, `--home.disableWelcomeScreen=true`, + // Needed for async search functional tests to introduce a delay + `--data.search.aggs.shardDelay.enabled=true`, `--security.showInsecureClusterWarning=false`, '--telemetry.banner=false', '--telemetry.optIn=false', diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js index f722e91dc0ae2..b62907dfe2c24 100644 --- a/test/functional/apps/dashboard/index.js +++ b/test/functional/apps/dashboard/index.js @@ -57,6 +57,7 @@ export default function ({ getService, loadTestFile }) { loadTestFile(require.resolve('./time_zones')); loadTestFile(require.resolve('./dashboard_options')); loadTestFile(require.resolve('./data_shared_attributes')); + loadTestFile(require.resolve('./share')); loadTestFile(require.resolve('./embed_mode')); loadTestFile(require.resolve('./dashboard_back_button')); loadTestFile(require.resolve('./dashboard_error_handling')); diff --git a/test/functional/apps/dashboard/share.ts b/test/functional/apps/dashboard/share.ts new file mode 100644 index 0000000000000..cc9c4786f0592 --- /dev/null +++ b/test/functional/apps/dashboard/share.ts @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['dashboard', 'common', 'share']); + + describe('share dashboard', () => { + before(async () => { + await esArchiver.load('dashboard/current/kibana'); + await kibanaServer.uiSettings.replace({ + defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', + }); + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.preserveCrossAppState(); + await PageObjects.dashboard.loadSavedDashboard('few panels'); + }); + + it('has "panels" state when sharing a snapshot', async () => { + await PageObjects.share.clickShareTopNavButton(); + const sharedUrl = await PageObjects.share.getSharedUrl(); + expect(sharedUrl).to.contain('panels'); + }); + }); +} diff --git a/test/functional/apps/discover/_field_data.js b/test/functional/apps/discover/_field_data.js index f0472fb5a3da5..d9cb09432b26f 100644 --- a/test/functional/apps/discover/_field_data.js +++ b/test/functional/apps/discover/_field_data.js @@ -27,7 +27,8 @@ export default function ({ getService, getPageObjects }) { const queryBar = getService('queryBar'); const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']); - describe('discover tab', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/78689 + describe.skip('discover tab', function describeIndexTests() { this.tags('includeFirefox'); before(async function () { await esArchiver.loadIfNeeded('logstash_functional'); diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 41667e1f26c8e..cc229ef0c2e08 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -505,6 +505,16 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo async scrollKibanaBodyTop() { await browser.setScrollToById('kibana-body', 0, 0); } + + /** + * Dismiss Banner if available. + */ + async dismissBanner() { + if (await testSubjects.exists('global-banner-item')) { + const button = await find.byButtonText('Dismiss'); + await button.click(); + } + } } return new CommonPage(); diff --git a/test/plugin_functional/plugins/data_search/server/plugin.ts b/test/plugin_functional/plugins/data_search/server/plugin.ts index 4252008dcd7ee..e016ef56802f3 100644 --- a/test/plugin_functional/plugins/data_search/server/plugin.ts +++ b/test/plugin_functional/plugins/data_search/server/plugin.ts @@ -58,12 +58,15 @@ export class DataSearchTestPlugin }, }, async (context, req, res) => { - const [, { data }] = await core.getStartServices(); + const [{ savedObjects }, { data }] = await core.getStartServices(); const service = await data.search.searchSource.asScoped(req); + const savedObjectsClient = savedObjects.getScopedClient(req); // Since the index pattern ID can change on each test run, we need // to look it up on the fly and insert it into the request. - const indexPatterns = await data.indexPatterns.indexPatternsServiceFactory(req); + const indexPatterns = await data.indexPatterns.indexPatternsServiceFactory( + savedObjectsClient + ); const ids = await indexPatterns.getIds(); // @ts-expect-error Force overwriting the request req.body.index = ids[0]; diff --git a/test/plugin_functional/plugins/index_patterns/server/plugin.ts b/test/plugin_functional/plugins/index_patterns/server/plugin.ts index ddf9acb259983..a54502b740211 100644 --- a/test/plugin_functional/plugins/index_patterns/server/plugin.ts +++ b/test/plugin_functional/plugins/index_patterns/server/plugin.ts @@ -39,8 +39,9 @@ export class IndexPatternsTestPlugin router.get( { path: '/api/index-patterns-plugin/get-all', validate: false }, async (context, req, res) => { - const [, { data }] = await core.getStartServices(); - const service = await data.indexPatterns.indexPatternsServiceFactory(req); + const [{ savedObjects }, { data }] = await core.getStartServices(); + const savedObjectsClient = savedObjects.getScopedClient(req); + const service = await data.indexPatterns.indexPatternsServiceFactory(savedObjectsClient); const ids = await service.getIds(); return res.ok({ body: ids }); } @@ -57,8 +58,9 @@ export class IndexPatternsTestPlugin }, async (context, req, res) => { const id = (req.params as Record).id; - const [, { data }] = await core.getStartServices(); - const service = await data.indexPatterns.indexPatternsServiceFactory(req); + const [{ savedObjects }, { data }] = await core.getStartServices(); + const savedObjectsClient = savedObjects.getScopedClient(req); + const service = await data.indexPatterns.indexPatternsServiceFactory(savedObjectsClient); const ip = await service.get(id); return res.ok({ body: ip.toSpec() }); } @@ -74,9 +76,10 @@ export class IndexPatternsTestPlugin }, }, async (context, req, res) => { - const [, { data }] = await core.getStartServices(); + const [{ savedObjects }, { data }] = await core.getStartServices(); const id = (req.params as Record).id; - const service = await data.indexPatterns.indexPatternsServiceFactory(req); + const savedObjectsClient = savedObjects.getScopedClient(req); + const service = await data.indexPatterns.indexPatternsServiceFactory(savedObjectsClient); const ip = await service.get(id); await service.updateSavedObject(ip); return res.ok(); @@ -93,9 +96,10 @@ export class IndexPatternsTestPlugin }, }, async (context, req, res) => { - const [, { data }] = await core.getStartServices(); + const [{ savedObjects }, { data }] = await core.getStartServices(); const id = (req.params as Record).id; - const service = await data.indexPatterns.indexPatternsServiceFactory(req); + const savedObjectsClient = savedObjects.getScopedClient(req); + const service = await data.indexPatterns.indexPatternsServiceFactory(savedObjectsClient); await service.delete(id); return res.ok(); } diff --git a/test/scripts/checks/bundle_limits.sh b/test/scripts/checks/bundle_limits.sh new file mode 100755 index 0000000000000..10d9d9343fda4 --- /dev/null +++ b/test/scripts/checks/bundle_limits.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +source src/dev/ci_setup/setup_env.sh + +node scripts/build_kibana_platform_plugins --validate-limits diff --git a/test/scripts/jenkins_test_setup_oss.sh b/test/scripts/jenkins_test_setup_oss.sh index b7eac33f35176..53626ce89462a 100755 --- a/test/scripts/jenkins_test_setup_oss.sh +++ b/test/scripts/jenkins_test_setup_oss.sh @@ -3,11 +3,7 @@ source test/scripts/jenkins_test_setup.sh if [[ -z "$CODE_COVERAGE" ]]; then - - destDir="build/kibana-build-oss" - if [[ ! "$TASK_QUEUE_PROCESS_ID" ]]; then - destDir="${destDir}-${CI_PARALLEL_PROCESS_NUMBER}" - fi + destDir="$WORKSPACE/kibana-build-oss-${TASK_QUEUE_PROCESS_ID:-$CI_PARALLEL_PROCESS_NUMBER}" if [[ ! -d $destDir ]]; then mkdir -p $destDir diff --git a/test/scripts/jenkins_test_setup_xpack.sh b/test/scripts/jenkins_test_setup_xpack.sh index 74a3de77e3a76..b9227fd8ff416 100755 --- a/test/scripts/jenkins_test_setup_xpack.sh +++ b/test/scripts/jenkins_test_setup_xpack.sh @@ -3,11 +3,7 @@ source test/scripts/jenkins_test_setup.sh if [[ -z "$CODE_COVERAGE" ]]; then - - destDir="build/kibana-build-xpack" - if [[ ! "$TASK_QUEUE_PROCESS_ID" ]]; then - destDir="${destDir}-${CI_PARALLEL_PROCESS_NUMBER}" - fi + destDir="$WORKSPACE/kibana-build-xpack-${TASK_QUEUE_PROCESS_ID:-$CI_PARALLEL_PROCESS_NUMBER}" if [[ ! -d $destDir ]]; then mkdir -p $destDir diff --git a/test/scripts/jenkins_xpack_build_plugins.sh b/test/scripts/jenkins_xpack_build_plugins.sh index 3fd3d02de1304..289e64f66c89b 100755 --- a/test/scripts/jenkins_xpack_build_plugins.sh +++ b/test/scripts/jenkins_xpack_build_plugins.sh @@ -10,5 +10,6 @@ node scripts/build_kibana_platform_plugins \ --scan-dir "$XPACK_DIR/test/alerting_api_integration/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \ + --scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \ --workers 12 \ --verbose diff --git a/vars/githubPr.groovy b/vars/githubPr.groovy index ec3dbd919fed6..fd5412c905683 100644 --- a/vars/githubPr.groovy +++ b/vars/githubPr.groovy @@ -87,15 +87,6 @@ def getLatestBuildInfo(comment) { return comment ? getBuildInfoFromComment(comment.body) : null } -def createBuildInfo() { - return [ - status: buildUtils.getBuildStatus(), - url: env.BUILD_URL, - number: env.BUILD_NUMBER, - commit: getCommitHash() - ] -} - def getHistoryText(builds) { if (!builds || builds.size() < 1) { return "" @@ -155,6 +146,16 @@ def getTestFailuresMessage() { return messages.join("\n") } +def getBuildStatusIncludingMetrics() { + def status = buildUtils.getBuildStatus() + + if (status == 'SUCCESS' && !ciStats.getMetricsSuccess()) { + return 'FAILURE' + } + + return status +} + def getNextCommentMessage(previousCommentInfo = [:], isFinal = false) { def info = previousCommentInfo ?: [:] info.builds = previousCommentInfo.builds ?: [] @@ -163,7 +164,10 @@ def getNextCommentMessage(previousCommentInfo = [:], isFinal = false) { info.builds = info.builds.findAll { it.number != env.BUILD_NUMBER } def messages = [] - def status = buildUtils.getBuildStatus() + + def status = isFinal + ? getBuildStatusIncludingMetrics() + : buildUtils.getBuildStatus() if (!isFinal) { def failuresPart = status != 'SUCCESS' ? ', with failures' : '' @@ -228,7 +232,12 @@ def getNextCommentMessage(previousCommentInfo = [:], isFinal = false) { messages << "To update your PR or re-run it, just comment with:\n`@elasticmachine merge upstream`" - info.builds << createBuildInfo() + info.builds << [ + status: status, + url: env.BUILD_URL, + number: env.BUILD_NUMBER, + commit: getCommitHash() + ] messages << """

+
); }; diff --git a/src/core/server/saved_objects/routes/bulk_create.ts b/src/core/server/saved_objects/routes/bulk_create.ts index 0f925d61ead98..b048c5d8f99bf 100644 --- a/src/core/server/saved_objects/routes/bulk_create.ts +++ b/src/core/server/saved_objects/routes/bulk_create.ts @@ -44,7 +44,7 @@ export const registerBulkCreateRoute = (router: IRouter) => { }) ) ), - namespaces: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })), + initialNamespaces: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })), }) ), }, diff --git a/src/core/server/saved_objects/routes/create.ts b/src/core/server/saved_objects/routes/create.ts index 191dbfaa0dbf1..816315705a375 100644 --- a/src/core/server/saved_objects/routes/create.ts +++ b/src/core/server/saved_objects/routes/create.ts @@ -44,16 +44,16 @@ export const registerCreateRoute = (router: IRouter) => { }) ) ), - namespaces: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })), + initialNamespaces: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })), }), }, }, router.handleLegacyErrors(async (context, req, res) => { const { type, id } = req.params; const { overwrite } = req.query; - const { attributes, migrationVersion, references, namespaces } = req.body; + const { attributes, migrationVersion, references, initialNamespaces } = req.body; - const options = { id, overwrite, migrationVersion, references, namespaces }; + const options = { id, overwrite, migrationVersion, references, initialNamespaces }; const result = await context.core.savedObjects.client.create(type, attributes, options); return res.ok({ body: result }); }) diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js index 10c7f143e52b9..e93bdb34ecc75 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -635,13 +635,13 @@ describe('SavedObjectsRepository', () => { await test(namespace); }); - it(`adds namespaces instead of namespace`, async () => { + it(`adds initialNamespaces instead of namespace`, async () => { const test = async (namespace) => { const ns2 = 'bar-namespace'; const ns3 = 'baz-namespace'; const objects = [ - { ...obj1, type: MULTI_NAMESPACE_TYPE, namespaces: [ns2] }, - { ...obj2, type: MULTI_NAMESPACE_TYPE, namespaces: [ns3] }, + { ...obj1, type: MULTI_NAMESPACE_TYPE, initialNamespaces: [ns2] }, + { ...obj2, type: MULTI_NAMESPACE_TYPE, initialNamespaces: [ns3] }, ]; await bulkCreateSuccess(objects, { namespace, overwrite: true }); const body = [ @@ -758,15 +758,15 @@ describe('SavedObjectsRepository', () => { ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); }); - it(`returns error when namespaces is used with a non-multi-namespace object`, async () => { + it(`returns error when initialNamespaces is used with a non-multi-namespace object`, async () => { const test = async (objType) => { - const obj = { ...obj3, type: objType, namespaces: [] }; + const obj = { ...obj3, type: objType, initialNamespaces: [] }; await bulkCreateError( obj, undefined, expectErrorResult( obj, - createBadRequestError('"namespaces" can only be used on multi-namespace types') + createBadRequestError('"initialNamespaces" can only be used on multi-namespace types') ) ); }; @@ -774,14 +774,14 @@ describe('SavedObjectsRepository', () => { await test(NAMESPACE_AGNOSTIC_TYPE); }); - it(`throws when options.namespaces is used with a multi-namespace type and is empty`, async () => { - const obj = { ...obj3, type: MULTI_NAMESPACE_TYPE, namespaces: [] }; + it(`throws when options.initialNamespaces is used with a multi-namespace type and is empty`, async () => { + const obj = { ...obj3, type: MULTI_NAMESPACE_TYPE, initialNamespaces: [] }; await bulkCreateError( obj, undefined, expectErrorResult( obj, - createBadRequestError('"namespaces" must be a non-empty array of strings') + createBadRequestError('"initialNamespaces" must be a non-empty array of strings') ) ); }); @@ -1993,13 +1993,13 @@ describe('SavedObjectsRepository', () => { ); }); - it(`adds namespaces instead of namespace`, async () => { - const options = { id, namespace, namespaces: ['bar-namespace', 'baz-namespace'] }; + it(`adds initialNamespaces instead of namespace`, async () => { + const options = { id, namespace, initialNamespaces: ['bar-namespace', 'baz-namespace'] }; await createSuccess(MULTI_NAMESPACE_TYPE, attributes, options); expect(client.create).toHaveBeenCalledWith( expect.objectContaining({ id: `${MULTI_NAMESPACE_TYPE}:${id}`, - body: expect.objectContaining({ namespaces: options.namespaces }), + body: expect.objectContaining({ namespaces: options.initialNamespaces }), }), expect.anything() ); @@ -2021,23 +2021,25 @@ describe('SavedObjectsRepository', () => { }); describe('errors', () => { - it(`throws when options.namespaces is used with a non-multi-namespace object`, async () => { + it(`throws when options.initialNamespaces is used with a non-multi-namespace object`, async () => { const test = async (objType) => { await expect( - savedObjectsRepository.create(objType, attributes, { namespaces: [namespace] }) + savedObjectsRepository.create(objType, attributes, { initialNamespaces: [namespace] }) ).rejects.toThrowError( - createBadRequestError('"options.namespaces" can only be used on multi-namespace types') + createBadRequestError( + '"options.initialNamespaces" can only be used on multi-namespace types' + ) ); }; await test('dashboard'); await test(NAMESPACE_AGNOSTIC_TYPE); }); - it(`throws when options.namespaces is used with a multi-namespace type and is empty`, async () => { + it(`throws when options.initialNamespaces is used with a multi-namespace type and is empty`, async () => { await expect( - savedObjectsRepository.create(MULTI_NAMESPACE_TYPE, attributes, { namespaces: [] }) + savedObjectsRepository.create(MULTI_NAMESPACE_TYPE, attributes, { initialNamespaces: [] }) ).rejects.toThrowError( - createBadRequestError('"options.namespaces" must be a non-empty array of strings') + createBadRequestError('"options.initialNamespaces" must be a non-empty array of strings') ); }); diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index bae96ceec2783..39aacd6b05b7b 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -230,19 +230,19 @@ export class SavedObjectsRepository { references = [], refresh = DEFAULT_REFRESH_SETTING, originId, - namespaces, + initialNamespaces, version, } = options; const namespace = normalizeNamespace(options.namespace); - if (namespaces) { + if (initialNamespaces) { if (!this._registry.isMultiNamespace(type)) { throw SavedObjectsErrorHelpers.createBadRequestError( - '"options.namespaces" can only be used on multi-namespace types' + '"options.initialNamespaces" can only be used on multi-namespace types' ); - } else if (!namespaces.length) { + } else if (!initialNamespaces.length) { throw SavedObjectsErrorHelpers.createBadRequestError( - '"options.namespaces" must be a non-empty array of strings' + '"options.initialNamespaces" must be a non-empty array of strings' ); } } @@ -262,9 +262,9 @@ export class SavedObjectsRepository { // we will overwrite a multi-namespace saved object if it exists; if that happens, ensure we preserve its included namespaces // note: this check throws an error if the object is found but does not exist in this namespace const existingNamespaces = await this.preflightGetNamespaces(type, id, namespace); - savedObjectNamespaces = namespaces || existingNamespaces; + savedObjectNamespaces = initialNamespaces || existingNamespaces; } else { - savedObjectNamespaces = namespaces || getSavedObjectNamespaces(namespace); + savedObjectNamespaces = initialNamespaces || getSavedObjectNamespaces(namespace); } } @@ -323,14 +323,14 @@ export class SavedObjectsRepository { let error: DecoratedError | undefined; if (!this._allowedTypes.includes(object.type)) { error = SavedObjectsErrorHelpers.createUnsupportedTypeError(object.type); - } else if (object.namespaces) { + } else if (object.initialNamespaces) { if (!this._registry.isMultiNamespace(object.type)) { error = SavedObjectsErrorHelpers.createBadRequestError( - '"namespaces" can only be used on multi-namespace types' + '"initialNamespaces" can only be used on multi-namespace types' ); - } else if (!object.namespaces.length) { + } else if (!object.initialNamespaces.length) { error = SavedObjectsErrorHelpers.createBadRequestError( - '"namespaces" must be a non-empty array of strings' + '"initialNamespaces" must be a non-empty array of strings' ); } } @@ -388,7 +388,7 @@ export class SavedObjectsRepository { let versionProperties; const { esRequestIndex, - object: { namespaces, version, ...object }, + object: { initialNamespaces, version, ...object }, method, } = expectedBulkGetResult.value; if (esRequestIndex !== undefined) { @@ -410,13 +410,13 @@ export class SavedObjectsRepository { }; } savedObjectNamespaces = - namespaces || getSavedObjectNamespaces(namespace, docFound && actualResult); + initialNamespaces || getSavedObjectNamespaces(namespace, docFound && actualResult); versionProperties = getExpectedVersionProperties(version, actualResult); } else { if (this._registry.isSingleNamespace(object.type)) { savedObjectNamespace = namespace; } else if (this._registry.isMultiNamespace(object.type)) { - savedObjectNamespaces = namespaces || getSavedObjectNamespaces(namespace); + savedObjectNamespaces = initialNamespaces || getSavedObjectNamespaces(namespace); } versionProperties = getExpectedVersionProperties(version); } diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts index d2b3b89b928c7..6782998d1bf1e 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.ts +++ b/src/core/server/saved_objects/service/saved_objects_client.ts @@ -56,7 +56,7 @@ export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions { * * Note: this can only be used for multi-namespace object types. */ - namespaces?: string[]; + initialNamespaces?: string[]; } /** @@ -79,7 +79,7 @@ export interface SavedObjectsBulkCreateObject { * * Note: this can only be used for multi-namespace object types. */ - namespaces?: string[]; + initialNamespaces?: string[]; } /** diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index a877700a48bcb..20bd102e6f507 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1936,8 +1936,8 @@ export interface SavedObjectsBulkCreateObject { attributes: T; // (undocumented) id?: string; + initialNamespaces?: string[]; migrationVersion?: SavedObjectsMigrationVersion; - namespaces?: string[]; originId?: string; // (undocumented) references?: SavedObjectReference[]; @@ -2094,8 +2094,8 @@ export interface SavedObjectsCoreFieldMapping { // @public (undocumented) export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions { id?: string; + initialNamespaces?: string[]; migrationVersion?: SavedObjectsMigrationVersion; - namespaces?: string[]; originId?: string; overwrite?: boolean; // (undocumented) diff --git a/src/dev/jest/config.integration.js b/src/dev/jest/config.integration.js index 2fd6bd120d553..970c00bb68b98 100644 --- a/src/dev/jest/config.integration.js +++ b/src/dev/jest/config.integration.js @@ -31,7 +31,10 @@ export default { ), reporters: [ 'default', - ['/src/dev/jest/junit_reporter.js', { reportName: 'Jest Integration Tests' }], + [ + '/packages/kbn-test/target/jest/junit_reporter', + { reportName: 'Jest Integration Tests' }, + ], ], setupFilesAfterEnv: ['/src/dev/jest/setup/after_env.integration.js'], }; diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 3c556a4f1ba3c..f582a8f3d4410 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -99,5 +99,5 @@ export default { '/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts', '/node_modules/enzyme-to-json/serializer', ], - reporters: ['default', '/src/dev/jest/junit_reporter.js'], + reporters: ['default', '/packages/kbn-test/target/jest/junit_reporter'], }; diff --git a/src/dev/jest/integration_tests/__fixtures__/package.json b/src/dev/jest/integration_tests/__fixtures__/package.json deleted file mode 100644 index 1a9a446d524bc..0000000000000 --- a/src/dev/jest/integration_tests/__fixtures__/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "fixture", - "jest": { - "testMatch": [ - "**/test.js" - ], - "transform": { - "^.+\\.js$": "/../../babel_transform.js" - }, - "reporters": [ - ["/../../junit_reporter.js", {"rootDirectory": "."}] - ] - } -} diff --git a/src/dev/notice/generate_notice_from_source.ts b/src/dev/notice/generate_notice_from_source.ts index 9f7eb9d9e1aa4..e362427682ec0 100644 --- a/src/dev/notice/generate_notice_from_source.ts +++ b/src/dev/notice/generate_notice_from_source.ts @@ -52,7 +52,7 @@ export async function generateNoticeFromSource({ productName, directory, log }: 'src/plugins/*/{node_modules,build,dist}/**', 'x-pack/{node_modules,build,dist,data}/**', 'x-pack/packages/*/{node_modules,build,dist}/**', - 'x-pack/plugins/*/{node_modules,build,dist}/**', + 'x-pack/plugins/**/{node_modules,build,dist}/**', '**/target/**', ], }; diff --git a/src/dev/typescript/build_refs.ts b/src/dev/typescript/build_refs.ts index 2cc8283111959..fdc1dfbfffa0b 100644 --- a/src/dev/typescript/build_refs.ts +++ b/src/dev/typescript/build_refs.ts @@ -29,7 +29,7 @@ export async function buildAllRefs(log: ToolingLog) { async function buildRefs(log: ToolingLog, projectPath: string) { try { log.debug(`Building TypeScript projects refs for ${projectPath}...`); - await execa(require.resolve('typescript/bin/tsc'), ['-b', projectPath]); + await execa(require.resolve('typescript/bin/tsc'), ['-b', projectPath, '--pretty']); } catch (e) { log.error(e); process.exit(1); diff --git a/src/fixtures/telemetry_collectors/.telemetryrc.json b/src/fixtures/telemetry_collectors/.telemetryrc.json index 31203149c9b57..c25d31eb182c7 100644 --- a/src/fixtures/telemetry_collectors/.telemetryrc.json +++ b/src/fixtures/telemetry_collectors/.telemetryrc.json @@ -2,6 +2,7 @@ "root": ".", "output": ".", "exclude": [ - "./unmapped_collector.ts" + "./unmapped_collector.ts", + "./externally_defined_usage_collector/index.ts" ] } diff --git a/src/fixtures/telemetry_collectors/externally_defined_usage_collector/get_usage_collector.ts b/src/fixtures/telemetry_collectors/externally_defined_usage_collector/get_usage_collector.ts new file mode 100644 index 0000000000000..d158afe271ff4 --- /dev/null +++ b/src/fixtures/telemetry_collectors/externally_defined_usage_collector/get_usage_collector.ts @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export interface Usage { + collectorName: string; +} + +export function getUsageCollector(collectorName: string) { + return { + type: 'externally_defined_usage_collector', + isReady: () => true, + schema: { + collectorName: { + type: 'keyword' as 'keyword', + }, + }, + fetch(): Usage { + return { + collectorName, + }; + }, + }; +} diff --git a/src/fixtures/telemetry_collectors/externally_defined_usage_collector/index.ts b/src/fixtures/telemetry_collectors/externally_defined_usage_collector/index.ts new file mode 100644 index 0000000000000..b88ecfced7e8a --- /dev/null +++ b/src/fixtures/telemetry_collectors/externally_defined_usage_collector/index.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { getUsageCollector } from './get_usage_collector'; + +export function registerCollector(collectorSet: UsageCollectionSetup) { + const collectorName = 'some_configs'; + const collector = collectorSet.makeUsageCollector(getUsageCollector(collectorName)); + + collectorSet.registerCollector(collector); +} diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index 8ab2a458d78e0..2ae7c6550b0cc 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -81,7 +81,7 @@ import { getDashboardTitle } from './dashboard_strings'; import { DashboardAppScope } from './dashboard_app'; import { convertSavedDashboardPanelToPanelState } from './lib/embeddable_saved_object_converters'; import { RenderDeps } from './application'; -import { IKbnUrlStateStorage, unhashUrl } from '../../../kibana_utils/public'; +import { IKbnUrlStateStorage, setStateToKbnUrl, unhashUrl } from '../../../kibana_utils/public'; import { addFatalError, AngularHttpError, @@ -1079,7 +1079,12 @@ export class DashboardAppController { allowEmbed: true, allowShortUrl: !dashboardConfig.getHideWriteControls() || dashboardCapabilities.createShortUrl, - shareableUrl: unhashUrl(window.location.href), + shareableUrl: setStateToKbnUrl( + '_a', + dashboardStateManager.getAppState(), + { useHash: false, storeInHashQuery: true }, + unhashUrl(window.location.href) + ), objectId: dash.id, objectType: 'dashboard', sharingData: { diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts index bf9a3b2b8a217..004b1a901bca9 100644 --- a/src/plugins/dashboard/public/index.ts +++ b/src/plugins/dashboard/public/index.ts @@ -45,7 +45,6 @@ export { export { addEmbeddableToDashboardUrl } from './url_utils/url_helper'; export { SavedObjectDashboard } from './saved_dashboards'; export { SavedDashboardPanel } from './types'; -export { AttributeService, ATTRIBUTE_SERVICE_KEY } from './attribute_service'; export function plugin(initializerContext: PluginInitializerContext) { return new DashboardPlugin(initializerContext); diff --git a/src/plugins/dashboard/public/mocks.tsx b/src/plugins/dashboard/public/mocks.tsx index 07f29eca53042..be7460f3110a5 100644 --- a/src/plugins/dashboard/public/mocks.tsx +++ b/src/plugins/dashboard/public/mocks.tsx @@ -20,13 +20,10 @@ import { DashboardStart } from './plugin'; export type Start = jest.Mocked; -export { mockAttributeService } from './attribute_service/attribute_service.mock'; const createStartContract = (): DashboardStart => { // @ts-ignore - const startContract: DashboardStart = { - getAttributeService: jest.fn(), - }; + const startContract: DashboardStart = {}; return startContract; }; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 3325d193e56ed..2fda8008788e6 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -39,8 +39,6 @@ import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart, - SavedObjectEmbeddableInput, - EmbeddableInput, PANEL_NOTIFICATION_TRIGGER, } from '../../embeddable/public'; import { DataPublicPluginSetup, DataPublicPluginStart, esFilters } from '../../data/public'; @@ -53,7 +51,6 @@ import { getSavedObjectFinder, SavedObjectLoader, SavedObjectsStart, - showSaveModal, } from '../../saved_objects/public'; import { ExitFullScreenButton as ExitFullScreenButtonUi, @@ -103,11 +100,6 @@ import { DashboardConstants } from './dashboard_constants'; import { addEmbeddableToDashboardUrl } from './url_utils/url_helper'; import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder'; import { UrlGeneratorState } from '../../share/public'; -import { AttributeService } from '.'; -import { - AttributeServiceOptions, - ATTRIBUTE_SERVICE_KEY, -} from './attribute_service/attribute_service'; declare module '../../share/public' { export interface UrlGeneratorStateMapping { @@ -156,16 +148,6 @@ export interface DashboardStart { dashboardUrlGenerator?: DashboardUrlGenerator; dashboardFeatureFlagConfig: DashboardFeatureFlagConfig; DashboardContainerByValueRenderer: ReturnType; - getAttributeService: < - A extends { title: string }, - V extends EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: A } = EmbeddableInput & { - [ATTRIBUTE_SERVICE_KEY]: A; - }, - R extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput - >( - type: string, - options: AttributeServiceOptions - ) => AttributeService; } declare module '../../../plugins/ui_actions/public' { @@ -433,7 +415,6 @@ export class DashboardPlugin const { uiActions, data: { indexPatterns, search }, - embeddable, } = plugins; const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, core.uiSettings); @@ -483,15 +464,6 @@ export class DashboardPlugin DashboardContainerByValueRenderer: createDashboardContainerByValueRenderer({ factory: dashboardContainerFactory, }), - getAttributeService: (type: string, options) => - new AttributeService( - type, - showSaveModal, - core.i18n.Context, - core.notifications.toasts, - options, - embeddable.getEmbeddableFactory - ), }; } diff --git a/src/plugins/dashboard/server/saved_objects/dashboard.ts b/src/plugins/dashboard/server/saved_objects/dashboard.ts index 850b2470dd475..a85f67f5ba56a 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard.ts @@ -46,10 +46,10 @@ export const dashboardSavedObjectType: SavedObjectsType = { description: { type: 'text' }, hits: { type: 'integer', index: false, doc_values: false }, kibanaSavedObjectMeta: { - properties: { searchSourceJSON: { type: 'text', index: false, doc_values: false } }, + properties: { searchSourceJSON: { type: 'text', index: false } }, }, - optionsJSON: { type: 'text', index: false, doc_values: false }, - panelsJSON: { type: 'text', index: false, doc_values: false }, + optionsJSON: { type: 'text', index: false }, + panelsJSON: { type: 'text', index: false }, refreshInterval: { properties: { display: { type: 'keyword', index: false, doc_values: false }, diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 13f658b562d39..0477fb68b3c1e 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1290,11 +1290,11 @@ export type IndexPatternsContract = PublicMethodsOf; // Warning: (ae-missing-release-tag) "IndexPatternSelectProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export type IndexPatternSelectProps = Required, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions'>, 'onChange' | 'placeholder'> & { +export type IndexPatternSelectProps = Required, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions' | 'onChange'>, 'placeholder'> & { + onChange: (indexPatternId?: string) => void; indexPatternId: string; fieldTypes?: string[]; onNoIndexPatterns?: () => void; - savedObjectsClient: SavedObjectsClientContract; }; // Warning: (ae-missing-release-tag) "IndexPatternSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/data/public/query/timefilter/timefilter.test.ts b/src/plugins/data/public/query/timefilter/timefilter.test.ts index 1280664ac8389..6c1a4eff786f6 100644 --- a/src/plugins/data/public/query/timefilter/timefilter.test.ts +++ b/src/plugins/data/public/query/timefilter/timefilter.test.ts @@ -54,6 +54,10 @@ function clearNowTimeStub() { delete global.nowTime; } +test('isTimeTouched is initially set to false', () => { + expect(timefilter.isTimeTouched()).toBe(false); +}); + describe('setTime', () => { let update: sinon.SinonSpy; let fetch: sinon.SinonSpy; @@ -84,6 +88,10 @@ describe('setTime', () => { }); }); + test('should update isTimeTouched', () => { + expect(timefilter.isTimeTouched()).toBe(true); + }); + test('should not add unexpected object keys to time state', () => { const unexpectedKey = 'unexpectedKey'; timefilter.setTime({ diff --git a/src/plugins/data/public/query/timefilter/timefilter.ts b/src/plugins/data/public/query/timefilter/timefilter.ts index 5eb1546fa015f..01b82087cf354 100644 --- a/src/plugins/data/public/query/timefilter/timefilter.ts +++ b/src/plugins/data/public/query/timefilter/timefilter.ts @@ -41,6 +41,8 @@ export class Timefilter { private fetch$ = new Subject(); private _time: TimeRange; + // Denotes whether setTime has been called, can be used to determine if the constructor defaults are being used. + private _isTimeTouched: boolean = false; private _refreshInterval!: RefreshInterval; private _history: TimeHistoryContract; @@ -68,6 +70,10 @@ export class Timefilter { return this._isAutoRefreshSelectorEnabled; } + public isTimeTouched() { + return this._isTimeTouched; + } + public getEnabledUpdated$ = () => { return this.enabledUpdated$.asObservable(); }; @@ -112,6 +118,7 @@ export class Timefilter { from: newTime.from, to: newTime.to, }; + this._isTimeTouched = true; this._history.add(this._time); this.timeUpdate$.next(); this.fetch$.next(); diff --git a/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts b/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts index 7863000b1ace4..060257a880528 100644 --- a/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts +++ b/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts @@ -26,6 +26,7 @@ const createSetupContractMock = () => { const timefilterMock: jest.Mocked = { isAutoRefreshSelectorEnabled: jest.fn(), isTimeRangeSelectorEnabled: jest.fn(), + isTimeTouched: jest.fn(), getEnabledUpdated$: jest.fn(), getTimeUpdate$: jest.fn(), getRefreshIntervalUpdate$: jest.fn(), diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx index a9ff0c3b38ae6..007689dd0269d 100644 --- a/src/plugins/data/public/search/errors/timeout_error.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -97,7 +97,12 @@ export class SearchTimeoutError extends KbnError { <> - this.onClick(application)} size="s"> + this.onClick(application)} + size="s" + data-test-subj="searchTimeoutError" + > {actionText} diff --git a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss index 73ec14de82b43..b79b7c038f9cc 100644 --- a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss +++ b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss @@ -33,8 +33,6 @@ } .globalFilterItem-isError, .globalFilterItem-isWarning { - text-decoration: none; - .globalFilterLabel__value { font-weight: $euiFontWeightBold; } diff --git a/src/plugins/data/public/ui/index_pattern_select/create_index_pattern_select.tsx b/src/plugins/data/public/ui/index_pattern_select/create_index_pattern_select.tsx index b49cc24ba90b1..a48c2dabf1506 100644 --- a/src/plugins/data/public/ui/index_pattern_select/create_index_pattern_select.tsx +++ b/src/plugins/data/public/ui/index_pattern_select/create_index_pattern_select.tsx @@ -25,7 +25,7 @@ import { IndexPatternSelect, IndexPatternSelectProps } from './'; // Takes in stateful runtime dependencies and pre-wires them to the component export function createIndexPatternSelect(savedObjectsClient: SavedObjectsClientContract) { - return (props: Omit) => ( + return (props: IndexPatternSelectProps) => ( ); } diff --git a/src/plugins/data/public/ui/index_pattern_select/index.tsx b/src/plugins/data/public/ui/index_pattern_select/index.tsx index 2912ec401b8b6..f0db37eb963fd 100644 --- a/src/plugins/data/public/ui/index_pattern_select/index.tsx +++ b/src/plugins/data/public/ui/index_pattern_select/index.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; -import type { IndexPatternSelectProps } from './index_pattern_select'; +import type { IndexPatternSelectInternalProps } from './index_pattern_select'; const Fallback = () => ( @@ -28,7 +28,7 @@ const Fallback = () => ( ); const LazyIndexPatternSelect = React.lazy(() => import('./index_pattern_select')); -export const IndexPatternSelect = (props: IndexPatternSelectProps) => ( +export const IndexPatternSelect = (props: IndexPatternSelectInternalProps) => ( }> diff --git a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx index 8de1e2c16f159..1e0e8934778ad 100644 --- a/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx +++ b/src/plugins/data/public/ui/index_pattern_select/index_pattern_select.tsx @@ -27,12 +27,19 @@ import { SavedObjectsClientContract, SimpleSavedObject } from 'src/core/public'; import { getTitle } from '../../../common/index_patterns/lib'; export type IndexPatternSelectProps = Required< - Omit, 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions'>, - 'onChange' | 'placeholder' + Omit< + EuiComboBoxProps, + 'isLoading' | 'onSearchChange' | 'options' | 'selectedOptions' | 'onChange' + >, + 'placeholder' > & { + onChange: (indexPatternId?: string) => void; indexPatternId: string; fieldTypes?: string[]; onNoIndexPatterns?: () => void; +}; + +export type IndexPatternSelectInternalProps = IndexPatternSelectProps & { savedObjectsClient: SavedObjectsClientContract; }; @@ -60,11 +67,11 @@ const getIndexPatterns = async ( // Needed for React.lazy // eslint-disable-next-line import/no-default-export -export default class IndexPatternSelect extends Component { +export default class IndexPatternSelect extends Component { private isMounted: boolean = false; state: IndexPatternSelectState; - constructor(props: IndexPatternSelectProps) { + constructor(props: IndexPatternSelectInternalProps) { super(props); this.state = { @@ -86,7 +93,7 @@ export default class IndexPatternSelect extends Component { +// FLAKY: https://github.com/elastic/kibana/issues/79910 +describe.skip('SearchBar', () => { const SEARCH_BAR_TEST_ID = 'globalQueryBar'; const SEARCH_BAR_ROOT = '.globalQueryBar'; const FILTER_BAR = '.globalFilterBar'; diff --git a/src/plugins/data/server/index_patterns/index_patterns_service.ts b/src/plugins/data/server/index_patterns/index_patterns_service.ts index 44699993dd135..d665e3715fa72 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_service.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts @@ -17,7 +17,7 @@ * under the License. */ -import { CoreSetup, CoreStart, Plugin, KibanaRequest, Logger } from 'kibana/server'; +import { CoreSetup, CoreStart, Plugin, Logger, SavedObjectsClientContract } from 'kibana/server'; import { registerRoutes } from './routes'; import { indexPatternSavedObjectType } from '../saved_objects'; import { capabilitiesProvider } from './capabilities_provider'; @@ -29,7 +29,7 @@ import { SavedObjectsClientServerToCommon } from './saved_objects_client_wrapper export interface IndexPatternsServiceStart { indexPatternsServiceFactory: ( - kibanaRequest: KibanaRequest + savedObjectsClient: SavedObjectsClientContract ) => Promise; } @@ -47,11 +47,10 @@ export class IndexPatternsService implements Plugin { - const savedObjectsClient = savedObjects.getScopedClient(kibanaRequest); + indexPatternsServiceFactory: async (savedObjectsClient: SavedObjectsClientContract) => { const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); const formats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient); diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 1a9e7d83bc956..6e66f8027207b 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -162,7 +162,9 @@ export class SearchService implements Plugin { asScoped: async (request: KibanaRequest) => { const esClient = elasticsearch.client.asScoped(request); const savedObjectsClient = savedObjects.getScopedClient(request); - const scopedIndexPatterns = await indexPatterns.indexPatternsServiceFactory(request); + const scopedIndexPatterns = await indexPatterns.indexPatternsServiceFactory( + savedObjectsClient + ); const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); // cache ui settings, only including items which are explicitly needed by SearchSource diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 45dbdee0f846b..07afad1c96a06 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -23,7 +23,6 @@ import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource } from 'src/plugins/data/public'; import { KibanaRequest } from 'src/core/server'; -import { KibanaRequest as KibanaRequest_2 } from 'kibana/server'; import { LegacyAPICaller } from 'kibana/server'; import { Logger } from 'kibana/server'; import { LoggerFactory } from '@kbn/logging'; @@ -42,6 +41,7 @@ import { RequestHandlerContext } from 'src/core/server'; import { RequestStatistics } from 'src/plugins/inspector/common'; import { SavedObject } from 'src/core/server'; import { SavedObjectsClientContract } from 'src/core/server'; +import { SavedObjectsClientContract as SavedObjectsClientContract_2 } from 'kibana/server'; import { Search } from '@elastic/elasticsearch/api/requestParams'; import { SearchResponse } from 'elasticsearch'; import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; @@ -675,7 +675,7 @@ export class IndexPatternsService implements Plugin_3 Promise; + indexPatternsServiceFactory: (savedObjectsClient: SavedObjectsClientContract_2) => Promise; }; } @@ -879,7 +879,7 @@ export class Plugin implements Plugin_2 Promise; }; indexPatterns: { - indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick) => Promise; }; search: ISearchStart>; }; diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts index 4911f207f0a5f..17f3199b75b15 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row.ts @@ -109,7 +109,7 @@ export function createTableRowDirective($compile: ng.ICompileService) { }); $scope.inlineFilter = function inlineFilter($event: any, type: string) { - const column = $($event.target).data().column; + const column = $($event.currentTarget).data().column; const field = $scope.indexPattern.fields.getByName(column); $scope.filter(field, $scope.flattenedRow[column], type); }; diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.html b/src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.html index e8c4fceeca7ff..0d17c2ca94cac 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.html +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_row/cell.html @@ -14,24 +14,24 @@ <% if (filterable) { %> + > + > <% } %> diff --git a/src/plugins/discover/server/saved_objects/search.ts b/src/plugins/discover/server/saved_objects/search.ts index c13550e543ab6..a6e42f956a025 100644 --- a/src/plugins/discover/server/saved_objects/search.ts +++ b/src/plugins/discover/server/saved_objects/search.ts @@ -48,7 +48,7 @@ export const searchSavedObjectType: SavedObjectsType = { hits: { type: 'integer', index: false, doc_values: false }, kibanaSavedObjectMeta: { properties: { - searchSourceJSON: { type: 'text', index: false, doc_values: false }, + searchSourceJSON: { type: 'text', index: false }, }, }, sort: { type: 'keyword', index: false, doc_values: false }, diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index 7609f07d660bc..789353ca4abd7 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -77,6 +77,8 @@ export { EmbeddableRendererProps, } from './lib'; +export { AttributeService, ATTRIBUTE_SERVICE_KEY } from './lib/attribute_service'; + export { EnhancementRegistryDefinition } from './types'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service.mock.tsx b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.mock.tsx similarity index 89% rename from src/plugins/dashboard/public/attribute_service/attribute_service.mock.tsx rename to src/plugins/embeddable/public/lib/attribute_service/attribute_service.mock.tsx index 09d6f5b4f1e0d..9b08d52ed517c 100644 --- a/src/plugins/dashboard/public/attribute_service/attribute_service.mock.tsx +++ b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.mock.tsx @@ -17,11 +17,11 @@ * under the License. */ -import { EmbeddableInput, SavedObjectEmbeddableInput } from '../embeddable_plugin'; -import { coreMock } from '../../../../core/public/mocks'; +import { EmbeddableInput, SavedObjectEmbeddableInput } from '../index'; +import { coreMock } from '../../../../../core/public/mocks'; import { AttributeServiceOptions } from './attribute_service'; -import { CoreStart } from '../../../../core/public'; -import { AttributeService, ATTRIBUTE_SERVICE_KEY } from '..'; +import { CoreStart } from 'src/core/public'; +import { AttributeService, ATTRIBUTE_SERVICE_KEY } from './index'; export const mockAttributeService = < A extends { title: string }, diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.test.ts similarity index 98% rename from src/plugins/dashboard/public/attribute_service/attribute_service.test.ts rename to src/plugins/embeddable/public/lib/attribute_service/attribute_service.test.ts index d7368b299c411..868501adb9687 100644 --- a/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts +++ b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.test.ts @@ -19,8 +19,8 @@ import { ATTRIBUTE_SERVICE_KEY } from './attribute_service'; import { mockAttributeService } from './attribute_service.mock'; -import { coreMock } from '../../../../core/public/mocks'; -import { OnSaveProps } from '../../../saved_objects/public/save_modal'; +import { coreMock } from '../../../../../core/public/mocks'; +import { OnSaveProps } from 'src/plugins/saved_objects/public/save_modal'; interface TestAttributes { title: string; diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx similarity index 95% rename from src/plugins/dashboard/public/attribute_service/attribute_service.tsx rename to src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx index b46226ec4ab02..c4628ab7fbdba 100644 --- a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx +++ b/src/plugins/embeddable/public/lib/attribute_service/attribute_service.tsx @@ -20,17 +20,17 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; +import { I18nStart, NotificationsStart } from 'src/core/public'; +import { SavedObjectSaveModal, OnSaveProps, SaveResult } from '../../../../saved_objects/public'; import { EmbeddableInput, SavedObjectEmbeddableInput, isSavedObjectEmbeddableInput, IEmbeddable, Container, - EmbeddableStart, EmbeddableFactoryNotFoundError, -} from '../embeddable_plugin'; -import { I18nStart, NotificationsStart } from '../../../../core/public'; -import { SavedObjectSaveModal, OnSaveProps, SaveResult } from '../../../saved_objects/public'; + EmbeddableFactory, +} from '../index'; /** * The attribute service is a shared, generic service that embeddables can use to provide the functionality @@ -66,7 +66,7 @@ export class AttributeService< private i18nContext: I18nStart['Context'], private toasts: NotificationsStart['toasts'], private options: AttributeServiceOptions, - getEmbeddableFactory?: EmbeddableStart['getEmbeddableFactory'] + getEmbeddableFactory?: (embeddableFactoryId: string) => EmbeddableFactory ) { if (getEmbeddableFactory) { const factory = getEmbeddableFactory(this.type); @@ -113,7 +113,7 @@ export class AttributeService< return { ...originalInput } as RefType; } catch (error) { this.toasts.addDanger({ - title: i18n.translate('dashboard.attributeService.saveToLibraryError', { + title: i18n.translate('embeddableApi.attributeService.saveToLibraryError', { defaultMessage: `Panel was not saved to the library. Error: {errorMessage}`, values: { errorMessage: error.message, diff --git a/src/plugins/dashboard/public/attribute_service/index.ts b/src/plugins/embeddable/public/lib/attribute_service/index.ts similarity index 100% rename from src/plugins/dashboard/public/attribute_service/index.ts rename to src/plugins/embeddable/public/lib/attribute_service/index.ts diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_error_label.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_error_label.tsx index 1e4604af9dc09..fa4d7f466caee 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_error_label.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_error_label.tsx @@ -40,8 +40,10 @@ export function EmbeddableErrorLabel(props: Props) { return (
- - {labelText} + + + {labelText} +
diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index a2da31773696c..137f8c24b1fae 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -20,6 +20,7 @@ import { EuiContextMenuPanelDescriptor, EuiPanel, htmlIdGenerator } from '@elast import classNames from 'classnames'; import React from 'react'; import { Subscription } from 'rxjs'; +import deepEqual from 'fast-deep-equal'; import { buildContextMenuForActions, UiActionsService, Action } from '../ui_actions'; import { CoreStart, OverlayStart } from '../../../../../core/public'; import { toMountPoint } from '../../../../kibana_react/public'; @@ -123,9 +124,11 @@ export class EmbeddablePanel extends React.Component { badges = badges.filter((badge) => disabledActions.indexOf(badge.id) === -1); } - this.setState({ - badges, - }); + if (!deepEqual(this.state.badges, badges)) { + this.setState({ + badges, + }); + } } private async refreshNotifications() { @@ -139,9 +142,11 @@ export class EmbeddablePanel extends React.Component { notifications = notifications.filter((badge) => disabledActions.indexOf(badge.id) === -1); } - this.setState({ - notifications, - }); + if (!deepEqual(this.state.notifications, notifications)) { + this.setState({ + notifications, + }); + } } public UNSAFE_componentWillMount() { diff --git a/src/plugins/embeddable/public/mocks.tsx b/src/plugins/embeddable/public/mocks.tsx index 26c10121adb3d..62063cb480338 100644 --- a/src/plugins/embeddable/public/mocks.tsx +++ b/src/plugins/embeddable/public/mocks.tsx @@ -39,6 +39,7 @@ import { dataPluginMock } from '../../data/public/mocks'; import { inspectorPluginMock } from '../../inspector/public/mocks'; import { uiActionsPluginMock } from '../../ui_actions/public/mocks'; +export { mockAttributeService } from './lib/attribute_service/attribute_service.mock'; export type Setup = jest.Mocked; export type Start = jest.Mocked; @@ -125,6 +126,7 @@ const createStartContract = (): Start => { EmbeddablePanel: jest.fn(), getEmbeddablePanel: jest.fn(), getStateTransfer: jest.fn(() => createEmbeddableStateTransferMock() as EmbeddableStateTransfer), + getAttributeService: jest.fn(), }; return startContract; }; diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index 00eb923c26662..aa4d66c43c9db 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { Subscription } from 'rxjs'; import { identity } from 'lodash'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../data/public'; -import { getSavedObjectFinder } from '../../saved_objects/public'; +import { getSavedObjectFinder, showSaveModal } from '../../saved_objects/public'; import { UiActionsSetup, UiActionsStart } from '../../ui_actions/public'; import { Start as InspectorStart } from '../../inspector/public'; import { @@ -47,6 +47,7 @@ import { defaultEmbeddableFactoryProvider, IEmbeddable, EmbeddablePanel, + SavedObjectEmbeddableInput, } from './lib'; import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition'; import { EmbeddableStateTransfer } from './lib/state_transfer'; @@ -56,6 +57,8 @@ import { telemetryBaseEmbeddableInput, } from '../common/lib/migrate_base_input'; import { PersistableState, SerializableState } from '../../kibana_utils/common'; +import { ATTRIBUTE_SERVICE_KEY, AttributeService } from './lib/attribute_service'; +import { AttributeServiceOptions } from './lib/attribute_service/attribute_service'; export interface EmbeddableSetupDependencies { data: DataPublicPluginSetup; @@ -93,6 +96,16 @@ export interface EmbeddableStart extends PersistableState { EmbeddablePanel: EmbeddablePanelHOC; getEmbeddablePanel: (stateTransfer?: EmbeddableStateTransfer) => EmbeddablePanelHOC; getStateTransfer: (history?: ScopedHistory) => EmbeddableStateTransfer; + getAttributeService: < + A extends { title: string }, + V extends EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: A } = EmbeddableInput & { + [ATTRIBUTE_SERVICE_KEY]: A; + }, + R extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput + >( + type: string, + options: AttributeServiceOptions
+ ) => AttributeService; } export type EmbeddablePanelHOC = React.FC<{ embeddable: IEmbeddable; hideHeader?: boolean }>; @@ -178,6 +191,15 @@ export class EmbeddablePublicPlugin implements Plugin + new AttributeService( + type, + showSaveModal, + core.i18n.Context, + core.notifications.toasts, + options, + this.getEmbeddableFactory + ), getStateTransfer: (history?: ScopedHistory) => { return history ? new EmbeddableStateTransfer(core.application.navigateToApp, history, this.appList) diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md index b01995ccaab08..6280d3a2e4a50 100644 --- a/src/plugins/embeddable/public/public.api.md +++ b/src/plugins/embeddable/public/public.api.md @@ -31,6 +31,7 @@ import { ExclusiveUnion } from '@elastic/eui'; import { ExpressionAstFunction } from 'src/plugins/expressions/common'; import { History } from 'history'; import { Href } from 'history'; +import { I18nStart as I18nStart_2 } from 'src/core/public'; import { IconType } from '@elastic/eui'; import { ISearchOptions } from 'src/plugins/data/public'; import { ISearchSource } from 'src/plugins/data/public'; @@ -119,6 +120,42 @@ export class AddPanelAction implements Action_3 { readonly type = "ACTION_ADD_PANEL"; } +// Warning: (ae-missing-release-tag) "ATTRIBUTE_SERVICE_KEY" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const ATTRIBUTE_SERVICE_KEY = "attributes"; + +// Warning: (ae-missing-release-tag) "AttributeService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class AttributeService { + // Warning: (ae-forgotten-export) The symbol "AttributeServiceOptions" needs to be exported by the entry point index.d.ts + constructor(type: string, showSaveModal: (saveModal: React.ReactElement, I18nContext: I18nStart_2['Context']) => void, i18nContext: I18nStart_2['Context'], toasts: NotificationsStart_2['toasts'], options: AttributeServiceOptions, getEmbeddableFactory?: (embeddableFactoryId: string) => EmbeddableFactory); + // (undocumented) + getExplicitInputFromEmbeddable(embeddable: IEmbeddable): ValType | RefType; + // (undocumented) + getInputAsRefType: (input: ValType | RefType, saveOptions?: { + showSaveModal: boolean; + saveModalTitle?: string | undefined; + } | { + title: string; + } | undefined) => Promise; + // (undocumented) + getInputAsValueType: (input: ValType | RefType) => Promise; + // (undocumented) + inputIsRefType: (input: ValType | RefType) => input is RefType; + // (undocumented) + unwrapAttributes(input: RefType | ValType): Promise; + // (undocumented) + wrapAttributes(newAttributes: SavedObjectAttributes, useRefType: boolean, input?: ValType | RefType): Promise>; +} + // Warning: (ae-missing-release-tag) "ChartActionContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -527,6 +564,14 @@ export interface EmbeddableStart extends PersistableState { // (undocumented) EmbeddablePanel: EmbeddablePanelHOC; // (undocumented) + getAttributeService: (type: string, options: AttributeServiceOptions) => AttributeService; + // (undocumented) getEmbeddableFactories: () => IterableIterator; // (undocumented) getEmbeddableFactory: = IEmbeddable>(embeddableFactoryId: string) => EmbeddableFactory | undefined; diff --git a/src/plugins/embeddable/server/plugin.ts b/src/plugins/embeddable/server/plugin.ts index f79c4b7620110..1e93561e4d063 100644 --- a/src/plugins/embeddable/server/plugin.ts +++ b/src/plugins/embeddable/server/plugin.ts @@ -35,6 +35,7 @@ import { SerializableState } from '../../kibana_utils/common'; import { EmbeddableInput } from '../common/types'; export interface EmbeddableSetup { + getAttributeService: any; registerEmbeddableFactory: (factory: EmbeddableRegistryDefinition) => void; registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void; } diff --git a/src/plugins/embeddable/server/server.api.md b/src/plugins/embeddable/server/server.api.md index c4fad2917343b..d051793382ab7 100644 --- a/src/plugins/embeddable/server/server.api.md +++ b/src/plugins/embeddable/server/server.api.md @@ -23,6 +23,8 @@ export interface EmbeddableRegistryDefinition

void; // (undocumented) diff --git a/src/plugins/expressions/.eslintrc.json b/src/plugins/expressions/.eslintrc.json new file mode 100644 index 0000000000000..2aab6c2d9093b --- /dev/null +++ b/src/plugins/expressions/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "@typescript-eslint/consistent-type-definitions": 0 + } +} diff --git a/src/plugins/expressions/common/ast/types.ts b/src/plugins/expressions/common/ast/types.ts index 09fb4fae3f201..e8cf497774569 100644 --- a/src/plugins/expressions/common/ast/types.ts +++ b/src/plugins/expressions/common/ast/types.ts @@ -24,12 +24,12 @@ export type ExpressionAstNode = | ExpressionAstFunction | ExpressionAstArgument; -export interface ExpressionAstExpression { +export type ExpressionAstExpression = { type: 'expression'; chain: ExpressionAstFunction[]; -} +}; -export interface ExpressionAstFunction { +export type ExpressionAstFunction = { type: 'function'; function: string; arguments: Record; @@ -38,9 +38,9 @@ export interface ExpressionAstFunction { * Debug information added to each function when expression is executed in *debug mode*. */ debug?: ExpressionAstFunctionDebug; -} +}; -export interface ExpressionAstFunctionDebug { +export type ExpressionAstFunctionDebug = { /** * True if function successfully returned output, false if function threw. */ @@ -83,6 +83,6 @@ export interface ExpressionAstFunctionDebug { * timing starts after the arguments have been resolved. */ duration: number | undefined; -} +}; export type ExpressionAstArgument = string | boolean | number | ExpressionAstExpression; diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index d4c9b0a25d45b..69140453f486d 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -17,6 +17,7 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { keys, last, mapValues, reduce, zipObject } from 'lodash'; import { Executor, ExpressionExecOptions } from '../executor'; import { createExecutionContainer, ExecutionContainer } from './container'; @@ -217,7 +218,27 @@ export class Execution< const fn = getByAlias(this.state.get().functions, fnName); if (!fn) { - return createError({ message: `Function ${fnName} could not be found.` }); + return createError({ + name: 'fn not found', + message: i18n.translate('expressions.execution.functionNotFound', { + defaultMessage: `Function {fnName} could not be found.`, + values: { + fnName, + }, + }), + }); + } + + if (fn.disabled) { + return createError({ + name: 'fn is disabled', + message: i18n.translate('expressions.execution.functionDisabled', { + defaultMessage: `Function {fnName} is disabled.`, + values: { + fnName, + }, + }), + }); } let args: Record = {}; diff --git a/src/plugins/expressions/common/execution/execution_contract.ts b/src/plugins/expressions/common/execution/execution_contract.ts index 20c5b2dd434b5..79bb4c58ab48d 100644 --- a/src/plugins/expressions/common/execution/execution_contract.ts +++ b/src/plugins/expressions/common/execution/execution_contract.ts @@ -62,7 +62,7 @@ export class ExecutionContract< return { type: 'error', error: { - type: e.type, + name: e.name, message: e.message, stack: e.stack, }, diff --git a/src/plugins/expressions/common/executor/executor.test.ts b/src/plugins/expressions/common/executor/executor.test.ts index 81845401d32e4..a658d3457407c 100644 --- a/src/plugins/expressions/common/executor/executor.test.ts +++ b/src/plugins/expressions/common/executor/executor.test.ts @@ -21,7 +21,7 @@ import { Executor } from './executor'; import * as expressionTypes from '../expression_types'; import * as expressionFunctions from '../expression_functions'; import { Execution } from '../execution'; -import { parseExpression } from '../ast'; +import { ExpressionAstFunction, parseExpression } from '../ast'; describe('Executor', () => { test('can instantiate', () => { @@ -152,4 +152,47 @@ describe('Executor', () => { }); }); }); + + describe('.inject', () => { + const executor = new Executor(); + + const injectFn = jest.fn().mockImplementation((args, references) => args); + const extractFn = jest.fn().mockReturnValue({ args: {}, references: [] }); + + const fooFn = { + name: 'foo', + help: 'test', + args: { + bar: { + types: ['string'], + help: 'test', + }, + }, + extract: (state: ExpressionAstFunction['arguments']) => { + return extractFn(state); + }, + inject: (state: ExpressionAstFunction['arguments']) => { + return injectFn(state); + }, + fn: jest.fn(), + }; + executor.registerFunction(fooFn); + + test('calls inject function for every expression function in expression', () => { + executor.inject( + parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}'), + [] + ); + expect(injectFn).toBeCalledTimes(5); + }); + + describe('.extract', () => { + test('calls extract function for every expression function in expression', () => { + executor.extract( + parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}') + ); + expect(extractFn).toBeCalledTimes(5); + }); + }); + }); }); diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts index 2b5f9f2556d89..28aae8c8f4834 100644 --- a/src/plugins/expressions/common/executor/executor.ts +++ b/src/plugins/expressions/common/executor/executor.ts @@ -19,6 +19,7 @@ /* eslint-disable max-classes-per-file */ +import { cloneDeep, mapValues } from 'lodash'; import { ExecutorState, ExecutorContainer } from './container'; import { createExecutorContainer } from './container'; import { AnyExpressionFunctionDefinition, ExpressionFunction } from '../expression_functions'; @@ -26,9 +27,12 @@ import { Execution, ExecutionParams } from '../execution/execution'; import { IRegistry } from '../types'; import { ExpressionType } from '../expression_types/expression_type'; import { AnyExpressionTypeDefinition } from '../expression_types/types'; -import { ExpressionAstExpression } from '../ast'; +import { ExpressionAstExpression, ExpressionAstFunction } from '../ast'; import { typeSpecs } from '../expression_types/specs'; import { functionSpecs } from '../expression_functions/specs'; +import { getByAlias } from '../util'; +import { SavedObjectReference } from '../../../../core/types'; +import { PersistableState } from '../../../kibana_utils/common'; export interface ExpressionExecOptions { /** @@ -83,7 +87,8 @@ export class FunctionsRegistry implements IRegistry { } } -export class Executor = Record> { +export class Executor = Record> + implements PersistableState { static createWithDefaults = Record>( state?: ExecutorState ): Executor { @@ -197,6 +202,56 @@ export class Executor = Record void + ) { + for (const link of ast.chain) { + const { function: fnName, arguments: fnArgs } = link; + const fn = getByAlias(this.state.get().functions, fnName); + + if (fn) { + // if any of arguments are expressions we should migrate those first + link.arguments = mapValues(fnArgs, (asts, argName) => { + return asts.map((arg) => { + if (typeof arg === 'object') { + return this.walkAst(arg, action); + } + return arg; + }); + }); + + action(fn, link); + } + } + + return ast; + } + + public inject(ast: ExpressionAstExpression, references: SavedObjectReference[]) { + return this.walkAst(cloneDeep(ast), (fn, link) => { + link.arguments = fn.inject(link.arguments, references); + }); + } + + public extract(ast: ExpressionAstExpression) { + const allReferences: SavedObjectReference[] = []; + const newAst = this.walkAst(cloneDeep(ast), (fn, link) => { + const { state, references } = fn.extract(link.arguments); + link.arguments = state; + allReferences.push(...references); + }); + return { state: newAst, references: allReferences }; + } + + public telemetry(ast: ExpressionAstExpression, telemetryData: Record) { + this.walkAst(cloneDeep(ast), (fn, link) => { + telemetryData = fn.telemetry(link.arguments, telemetryData); + }); + + return telemetryData; + } + public fork(): Executor { const initialState = this.state.get(); const fork = new Executor(initialState); diff --git a/src/plugins/expressions/common/expression_functions/expression_function.ts b/src/plugins/expressions/common/expression_functions/expression_function.ts index 71f0d91510136..0b56d3c169ff4 100644 --- a/src/plugins/expressions/common/expression_functions/expression_function.ts +++ b/src/plugins/expressions/common/expression_functions/expression_function.ts @@ -17,12 +17,16 @@ * under the License. */ +import { identity } from 'lodash'; import { AnyExpressionFunctionDefinition } from './types'; import { ExpressionFunctionParameter } from './expression_function_parameter'; import { ExpressionValue } from '../expression_types/types'; import { ExecutionContext } from '../execution'; +import { ExpressionAstFunction } from '../ast'; +import { SavedObjectReference } from '../../../../core/types'; +import { PersistableState } from '../../../kibana_utils/common'; -export class ExpressionFunction { +export class ExpressionFunction implements PersistableState { /** * Name of function */ @@ -60,8 +64,34 @@ export class ExpressionFunction { */ inputTypes: string[] | undefined; + disabled: boolean; + telemetry: ( + state: ExpressionAstFunction['arguments'], + telemetryData: Record + ) => Record; + extract: ( + state: ExpressionAstFunction['arguments'] + ) => { state: ExpressionAstFunction['arguments']; references: SavedObjectReference[] }; + inject: ( + state: ExpressionAstFunction['arguments'], + references: SavedObjectReference[] + ) => ExpressionAstFunction['arguments']; + constructor(functionDefinition: AnyExpressionFunctionDefinition) { - const { name, type, aliases, fn, help, args, inputTypes, context } = functionDefinition; + const { + name, + type, + aliases, + fn, + help, + args, + inputTypes, + context, + disabled, + telemetry, + inject, + extract, + } = functionDefinition; this.name = name; this.type = type; @@ -70,6 +100,10 @@ export class ExpressionFunction { Promise.resolve(fn(input, params, handlers as ExecutionContext)); this.help = help || ''; this.inputTypes = inputTypes || context?.types; + this.disabled = disabled || false; + this.telemetry = telemetry || ((s, c) => c); + this.inject = inject || identity; + this.extract = extract || ((s) => ({ state: s, references: [] })); for (const [key, arg] of Object.entries(args || {})) { this.args[key] = new ExpressionFunctionParameter(key, arg); diff --git a/src/plugins/expressions/common/expression_functions/types.ts b/src/plugins/expressions/common/expression_functions/types.ts index d58d872aff722..caaef541aefd5 100644 --- a/src/plugins/expressions/common/expression_functions/types.ts +++ b/src/plugins/expressions/common/expression_functions/types.ts @@ -30,6 +30,8 @@ import { ExpressionFunctionVar, ExpressionFunctionTheme, } from './specs'; +import { ExpressionAstFunction } from '../ast'; +import { PersistableStateDefinition } from '../../../kibana_utils/common'; /** * `ExpressionFunctionDefinition` is the interface plugins have to implement to @@ -41,12 +43,17 @@ export interface ExpressionFunctionDefinition< Arguments extends Record, Output, Context extends ExecutionContext = ExecutionContext -> { +> extends PersistableStateDefinition { /** * The name of the function, as will be used in expression. */ name: Name; + /** + * if set to true function will be disabled (but its migrate function will still be available) + */ + disabled?: boolean; + /** * Name of type of value this function outputs. */ diff --git a/src/plugins/expressions/common/expression_renderers/types.ts b/src/plugins/expressions/common/expression_renderers/types.ts index b760e7b32a7d2..0ea3d72e75609 100644 --- a/src/plugins/expressions/common/expression_renderers/types.ts +++ b/src/plugins/expressions/common/expression_renderers/types.ts @@ -17,6 +17,8 @@ * under the License. */ +import { PersistedState } from 'src/plugins/visualizations/public'; + export interface ExpressionRenderDefinition { /** * Technical name of the renderer, used as ID to identify renderer in @@ -68,4 +70,5 @@ export interface IInterpreterRenderHandlers { reload: () => void; update: (params: any) => void; event: (event: any) => void; + uiState?: PersistedState; } diff --git a/src/plugins/expressions/common/expression_types/specs/error.ts b/src/plugins/expressions/common/expression_types/specs/error.ts index ebaedcbba0d23..7607945d8a664 100644 --- a/src/plugins/expressions/common/expression_types/specs/error.ts +++ b/src/plugins/expressions/common/expression_types/specs/error.ts @@ -20,20 +20,16 @@ import { ExpressionTypeDefinition, ExpressionValueBoxed } from '../types'; import { ExpressionValueRender } from './render'; import { getType } from '../get_type'; +import { SerializableState } from '../../../../kibana_utils/common'; +import { ErrorLike } from '../../util'; const name = 'error'; export type ExpressionValueError = ExpressionValueBoxed< 'error', { - error: { - message: string; - type?: string; - name?: string; - stack?: string; - original?: Error; - }; - info?: unknown; + error: ErrorLike; + info?: SerializableState; } >; diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index b5c98fada07c4..4a87fd9e7f331 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -23,6 +23,8 @@ import { ExpressionAstExpression } from '../ast'; import { ExecutionContract } from '../execution/execution_contract'; import { AnyExpressionTypeDefinition } from '../expression_types'; import { AnyExpressionFunctionDefinition } from '../expression_functions'; +import { SavedObjectReference } from '../../../../core/types'; +import { PersistableState } from '../../../kibana_utils/common'; /** * The public contract that `ExpressionsService` provides to other plugins @@ -154,7 +156,7 @@ export interface ExpressionServiceParams { * * so that JSDoc appears in developers IDE when they use those `plugins.expressions.registerFunction(`. */ -export class ExpressionsService { +export class ExpressionsService implements PersistableState { public readonly executor: Executor; public readonly renderers: ExpressionRendererRegistry; @@ -256,6 +258,36 @@ export class ExpressionsService { return fork; }; + /** + * Extracts telemetry from expression AST + * @param state expression AST to extract references from + */ + public readonly telemetry = ( + state: ExpressionAstExpression, + telemetryData: Record = {} + ) => { + return this.executor.telemetry(state, telemetryData); + }; + + /** + * Extracts saved object references from expression AST + * @param state expression AST to extract references from + * @returns new expression AST with references removed and array of references + */ + public readonly extract = (state: ExpressionAstExpression) => { + return this.executor.extract(state); + }; + + /** + * Injects saved object references into expression AST + * @param state expression AST to update + * @param references array of saved object references + * @returns new expression AST with references injected + */ + public readonly inject = (state: ExpressionAstExpression, references: SavedObjectReference[]) => { + return this.executor.inject(state, references); + }; + /** * Returns Kibana Platform *setup* life-cycle contract. Useful to return the * same contract on server-side and browser-side. diff --git a/src/plugins/expressions/common/util/create_error.ts b/src/plugins/expressions/common/util/create_error.ts index 9bdab74efd6f9..46306d3fbbf66 100644 --- a/src/plugins/expressions/common/util/create_error.ts +++ b/src/plugins/expressions/common/util/create_error.ts @@ -19,9 +19,17 @@ import { ExpressionValueError } from '../../common'; -type ErrorLike = Partial>; +export type SerializedError = { + name: string; + message: string; + stack?: string; +}; -export const createError = (err: string | Error | ErrorLike): ExpressionValueError => ({ +export type ErrorLike = SerializedError & { + original?: SerializedError; +}; + +export const createError = (err: string | ErrorLike): ExpressionValueError => ({ type: 'error', error: { stack: @@ -32,6 +40,6 @@ export const createError = (err: string | Error | ErrorLike): ExpressionValueErr : undefined, message: typeof err === 'string' ? err : String(err.message), name: typeof err === 'object' ? err.name || 'Error' : 'Error', - original: err instanceof Error ? err : undefined, + original: err instanceof Error ? (err as SerializedError) : undefined, }, }); diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 5c0fd8ab1a572..355bd502df3be 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -11,6 +11,7 @@ import { EnvironmentMode } from '@kbn/config'; import { EventEmitter } from 'events'; import { Observable } from 'rxjs'; import { PackageInfo } from '@kbn/config'; +import { PersistedState } from 'src/plugins/visualizations/public'; import { Plugin as Plugin_2 } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; import React from 'react'; @@ -188,10 +189,11 @@ export interface ExecutionState extends ExecutorState state: 'not-started' | 'pending' | 'result' | 'error'; } +// Warning: (ae-forgotten-export) The symbol "PersistableState" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "Executor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export class Executor = Record> { +export class Executor = Record> implements PersistableState { constructor(state?: ExecutorState); // (undocumented) get context(): Record; @@ -202,6 +204,11 @@ export class Executor = Record): void; // (undocumented) + extract(ast: ExpressionAstExpression): { + state: ExpressionAstExpression; + references: SavedObjectReference[]; + }; + // (undocumented) fork(): Executor; // @deprecated (undocumented) readonly functions: FunctionsRegistry; @@ -213,6 +220,10 @@ export class Executor = Record; + // Warning: (ae-forgotten-export) The symbol "SavedObjectReference" needs to be exported by the entry point index.d.ts + // + // (undocumented) + inject(ast: ExpressionAstExpression, references: SavedObjectReference[]): ExpressionAstExpression; // (undocumented) registerFunction(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; // (undocumented) @@ -220,9 +231,11 @@ export class Executor = Record = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; // (undocumented) readonly state: ExecutorContainer; + // (undocumented) + telemetry(ast: ExpressionAstExpression, telemetryData: Record): Record; // @deprecated (undocumented) readonly types: TypesRegistry; -} + } // Warning: (ae-forgotten-export) The symbol "ExecutorPureTransitions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ExecutorPureSelectors" needs to be exported by the entry point index.d.ts @@ -251,12 +264,10 @@ export type ExpressionAstArgument = string | boolean | number | ExpressionAstExp // Warning: (ae-missing-release-tag) "ExpressionAstExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface ExpressionAstExpression { - // (undocumented) - chain: ExpressionAstFunction[]; - // (undocumented) +export type ExpressionAstExpression = { type: 'expression'; -} + chain: ExpressionAstFunction[]; +}; // Warning: (ae-missing-release-tag) "ExpressionAstExpressionBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -272,16 +283,12 @@ export interface ExpressionAstExpressionBuilder { // Warning: (ae-missing-release-tag) "ExpressionAstFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface ExpressionAstFunction { - // (undocumented) +export type ExpressionAstFunction = { + type: 'function'; + function: string; arguments: Record; - // Warning: (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts debug?: ExpressionAstFunctionDebug; - // (undocumented) - function: string; - // (undocumented) - type: 'function'; -} +}; // Warning: (ae-missing-release-tag) "ExpressionAstFunctionBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -319,23 +326,35 @@ export interface ExpressionExecutor { // Warning: (ae-missing-release-tag) "ExpressionFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export class ExpressionFunction { +export class ExpressionFunction implements PersistableState { constructor(functionDefinition: AnyExpressionFunctionDefinition); // (undocumented) accepts: (type: string) => boolean; aliases: string[]; args: Record; + // (undocumented) + disabled: boolean; + // (undocumented) + extract: (state: ExpressionAstFunction['arguments']) => { + state: ExpressionAstFunction['arguments']; + references: SavedObjectReference[]; + }; fn: (input: ExpressionValue, params: Record, handlers: object) => ExpressionValue; help: string; + // (undocumented) + inject: (state: ExpressionAstFunction['arguments'], references: SavedObjectReference[]) => ExpressionAstFunction['arguments']; inputTypes: string[] | undefined; name: string; + // (undocumented) + telemetry: (state: ExpressionAstFunction['arguments'], telemetryData: Record) => Record; type: string; } +// Warning: (ae-forgotten-export) The symbol "PersistableStateDefinition" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> { +export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> extends PersistableStateDefinition { aliases?: string[]; args: { [key in keyof Arguments]: ArgumentType; @@ -344,6 +363,7 @@ export interface ExpressionFunctionDefinition>; @@ -539,13 +559,17 @@ export { ExpressionsPublicPlugin as Plugin } // Warning: (ae-missing-release-tag) "ExpressionsService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export class ExpressionsService { +export class ExpressionsService implements PersistableState { // Warning: (ae-forgotten-export) The symbol "ExpressionServiceParams" needs to be exported by the entry point index.d.ts constructor({ executor, renderers, }?: ExpressionServiceParams); // (undocumented) readonly execute: ExpressionsServiceStart['execute']; // (undocumented) readonly executor: Executor; + readonly extract: (state: ExpressionAstExpression) => { + state: ExpressionAstExpression; + references: SavedObjectReference[]; + }; // (undocumented) readonly fork: () => ExpressionsService; // (undocumented) @@ -557,6 +581,7 @@ export class ExpressionsService { // (undocumented) readonly getType: ExpressionsServiceStart['getType']; readonly getTypes: () => ReturnType; + readonly inject: (state: ExpressionAstExpression, references: SavedObjectReference[]) => ExpressionAstExpression; readonly registerFunction: (functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)) => void; // (undocumented) readonly registerRenderer: (definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)) => void; @@ -570,6 +595,7 @@ export class ExpressionsService { start(): ExpressionsServiceStart; // (undocumented) stop(): void; + readonly telemetry: (state: ExpressionAstExpression, telemetryData?: Record) => Record; } // Warning: (ae-missing-release-tag) "ExpressionsServiceSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -704,14 +730,8 @@ export type ExpressionValueConverter; // Warning: (ae-missing-release-tag) "ExpressionValueFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -883,6 +903,8 @@ export interface IInterpreterRenderHandlers { // (undocumented) reload: () => void; // (undocumented) + uiState?: PersistedState; + // (undocumented) update: (params: any) => void; } @@ -1159,6 +1181,12 @@ export type TypeToString = KnownTypeToString | UnmappedTypeStrings; export type UnmappedTypeStrings = 'date' | 'filter'; +// Warnings were encountered during analysis: +// +// src/plugins/expressions/common/ast/types.ts:40:3 - (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts +// src/plugins/expressions/common/expression_types/specs/error.ts:31:5 - (ae-forgotten-export) The symbol "ErrorLike" needs to be exported by the entry point index.d.ts +// src/plugins/expressions/common/expression_types/specs/error.ts:32:5 - (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index d8872ee416017..1a905de4a3bdf 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -9,6 +9,7 @@ import { CoreStart } from 'src/core/server'; import { Ensure } from '@kbn/utility-types'; import { EventEmitter } from 'events'; import { Observable } from 'rxjs'; +import { PersistedState } from 'src/plugins/visualizations/public'; import { Plugin as Plugin_2 } from 'src/core/server'; import { PluginInitializerContext } from 'src/core/server'; import { UnwrapPromiseOrReturn } from '@kbn/utility-types'; @@ -170,10 +171,11 @@ export interface ExecutionState extends ExecutorState state: 'not-started' | 'pending' | 'result' | 'error'; } +// Warning: (ae-forgotten-export) The symbol "PersistableState" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "Executor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export class Executor = Record> { +export class Executor = Record> implements PersistableState { constructor(state?: ExecutorState); // (undocumented) get context(): Record; @@ -184,6 +186,11 @@ export class Executor = Record): void; // (undocumented) + extract(ast: ExpressionAstExpression): { + state: ExpressionAstExpression; + references: SavedObjectReference[]; + }; + // (undocumented) fork(): Executor; // @deprecated (undocumented) readonly functions: FunctionsRegistry; @@ -195,6 +202,10 @@ export class Executor = Record; + // Warning: (ae-forgotten-export) The symbol "SavedObjectReference" needs to be exported by the entry point index.d.ts + // + // (undocumented) + inject(ast: ExpressionAstExpression, references: SavedObjectReference[]): ExpressionAstExpression; // (undocumented) registerFunction(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; // (undocumented) @@ -202,9 +213,11 @@ export class Executor = Record = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; // (undocumented) readonly state: ExecutorContainer; + // (undocumented) + telemetry(ast: ExpressionAstExpression, telemetryData: Record): Record; // @deprecated (undocumented) readonly types: TypesRegistry; -} + } // Warning: (ae-forgotten-export) The symbol "ExecutorPureTransitions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "ExecutorPureSelectors" needs to be exported by the entry point index.d.ts @@ -233,12 +246,10 @@ export type ExpressionAstArgument = string | boolean | number | ExpressionAstExp // Warning: (ae-missing-release-tag) "ExpressionAstExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface ExpressionAstExpression { - // (undocumented) - chain: ExpressionAstFunction[]; - // (undocumented) +export type ExpressionAstExpression = { type: 'expression'; -} + chain: ExpressionAstFunction[]; +}; // Warning: (ae-missing-release-tag) "ExpressionAstExpressionBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -254,16 +265,12 @@ export interface ExpressionAstExpressionBuilder { // Warning: (ae-missing-release-tag) "ExpressionAstFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface ExpressionAstFunction { - // (undocumented) +export type ExpressionAstFunction = { + type: 'function'; + function: string; arguments: Record; - // Warning: (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts debug?: ExpressionAstFunctionDebug; - // (undocumented) - function: string; - // (undocumented) - type: 'function'; -} +}; // Warning: (ae-missing-release-tag) "ExpressionAstFunctionBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -291,23 +298,35 @@ export type ExpressionAstNode = ExpressionAstExpression | ExpressionAstFunction // Warning: (ae-missing-release-tag) "ExpressionFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export class ExpressionFunction { +export class ExpressionFunction implements PersistableState { constructor(functionDefinition: AnyExpressionFunctionDefinition); // (undocumented) accepts: (type: string) => boolean; aliases: string[]; args: Record; + // (undocumented) + disabled: boolean; + // (undocumented) + extract: (state: ExpressionAstFunction['arguments']) => { + state: ExpressionAstFunction['arguments']; + references: SavedObjectReference[]; + }; fn: (input: ExpressionValue, params: Record, handlers: object) => ExpressionValue; help: string; + // (undocumented) + inject: (state: ExpressionAstFunction['arguments'], references: SavedObjectReference[]) => ExpressionAstFunction['arguments']; inputTypes: string[] | undefined; name: string; + // (undocumented) + telemetry: (state: ExpressionAstFunction['arguments'], telemetryData: Record) => Record; type: string; } +// Warning: (ae-forgotten-export) The symbol "PersistableStateDefinition" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> { +export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> extends PersistableStateDefinition { aliases?: string[]; args: { [key in keyof Arguments]: ArgumentType; @@ -316,6 +335,7 @@ export interface ExpressionFunctionDefinition>; @@ -564,14 +584,8 @@ export type ExpressionValueConverter; // Warning: (ae-missing-release-tag) "ExpressionValueFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -717,6 +731,8 @@ export interface IInterpreterRenderHandlers { // (undocumented) reload: () => void; // (undocumented) + uiState?: PersistedState; + // (undocumented) update: (params: any) => void; } @@ -963,6 +979,12 @@ export type TypeToString = KnownTypeToString | UnmappedTypeStrings; export type UnmappedTypeStrings = 'date' | 'filter'; +// Warnings were encountered during analysis: +// +// src/plugins/expressions/common/ast/types.ts:40:3 - (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts +// src/plugins/expressions/common/expression_types/specs/error.ts:31:5 - (ae-forgotten-export) The symbol "ErrorLike" needs to be exported by the entry point index.d.ts +// src/plugins/expressions/common/expression_types/specs/error.ts:32:5 - (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field_container.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field_container.tsx index 39c0add40e9ad..076cda484d808 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field_container.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field_container.tsx @@ -26,31 +26,31 @@ import { useKibana } from '../../../../../../plugins/kibana_react/public'; import { IndexPatternManagmentContext } from '../../../types'; import { CreateEditField } from './create_edit_field'; -export type CreateEditFieldContainerProps = RouteComponentProps<{ id: string; fieldName: string }>; +export type CreateEditFieldContainerProps = RouteComponentProps<{ id: string; fieldName?: string }>; const CreateEditFieldCont: React.FC = ({ ...props }) => { const { setBreadcrumbs, data } = useKibana().services; const [indexPattern, setIndexPattern] = useState(); + const fieldName = + props.match.params.fieldName && decodeURIComponent(props.match.params.fieldName); useEffect(() => { data.indexPatterns.get(props.match.params.id).then((ip: IndexPattern) => { setIndexPattern(ip); if (ip) { setBreadcrumbs( - props.match.params.fieldName - ? getEditFieldBreadcrumbs(ip, props.match.params.fieldName) - : getCreateFieldBreadcrumbs(ip) + fieldName ? getEditFieldBreadcrumbs(ip, fieldName) : getCreateFieldBreadcrumbs(ip) ); } }); - }, [props.match.params.id, props.match.params.fieldName, setBreadcrumbs, data.indexPatterns]); + }, [props.match.params.id, fieldName, setBreadcrumbs, data.indexPatterns]); if (indexPattern) { return ( ); } else { diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.test.ts b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.test.ts new file mode 100644 index 0000000000000..0e3ee27476fcc --- /dev/null +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.test.ts @@ -0,0 +1,30 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getPath } from './utils'; +import { IndexPatternField, IndexPattern } from '../../../../../data/public'; + +test('getPath() should encode "fieldName"', () => { + expect( + getPath( + ({ name: 'Memory: Allocated Bytes/sec' } as unknown) as IndexPatternField, + ({ id: 'id' } as unknown) as IndexPattern + ) + ).toMatchInlineSnapshot(`"/patterns/id/field/Memory%3A%20Allocated%20Bytes%2Fsec"`); +}); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts index 91c5cc1afdb49..a94ed60b7aed5 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts @@ -117,7 +117,7 @@ export function getTabs( } export function getPath(field: IndexPatternField, indexPattern: IndexPattern) { - return `/patterns/${indexPattern?.id}/field/${field.name}`; + return `/patterns/${indexPattern?.id}/field/${encodeURIComponent(field.name)}`; } const allTypesDropDown = i18n.translate( diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap index 69b192a81d097..38f630358d064 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap @@ -4,6 +4,7 @@ exports[`LabelTemplateFlyout should not render if not visible 1`] = `""`; exports[`LabelTemplateFlyout should render normally 1`] = ` diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap index f862d0ebe8477..13be0353e1640 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap @@ -1,544 +1,431 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UrlFormatEditor should render label template help 1`] = ` - - - - - } - labelType="label" +exports[`UrlFormatEditor should render normally 1`] = ` +

+
- - - + - + Type + + +
+
+
- - - } - isInvalid={false} - label={ - - } - labelType="label" - > - - - - -`; - -exports[`UrlFormatEditor should render normally 1`] = ` - - - - - } - labelType="label" +
+ +
+ + +
+
+
+
+
- - - + - + Open in a new tab + + +
+
+
- - - } - isInvalid={false} - label={ - - } - labelType="label" + + + + Off + + +
+
+
+
- - - - -`; - -exports[`UrlFormatEditor should render url template help 1`] = ` - - - - - } - labelType="label" - > - - - + - + URL template + + +
+
+
- - - } - isInvalid={false} - label={ - - } - labelType="label" - > - - - - -`; - -exports[`UrlFormatEditor should render width and height fields if image 1`] = ` - - - - - } - labelType="label" - > - - - + +
+
+
- - - } - isInvalid={false} - label={ - - } - labelType="label" + +
+
+