diff --git a/.backportrc.json b/.backportrc.json
index 8f458343c51af..3f1d639e9a480 100644
--- a/.backportrc.json
+++ b/.backportrc.json
@@ -3,6 +3,7 @@
"targetBranchChoices": [
{ "name": "master", "checked": true },
{ "name": "7.x", "checked": true },
+ "7.9",
"7.8",
"7.7",
"7.6",
@@ -26,7 +27,7 @@
"targetPRLabels": ["backport"],
"branchLabelMapping": {
"^v8.0.0$": "master",
- "^v7.9.0$": "7.x",
+ "^v7.10.0$": "7.x",
"^v(\\d+).(\\d+).\\d+$": "$1.$2"
}
}
diff --git a/.browserslistrc b/.browserslistrc
index 89114f393c462..04395b913c9c5 100644
--- a/.browserslistrc
+++ b/.browserslistrc
@@ -1,7 +1,9 @@
[production]
-last 2 versions
-> 5%
-Safari 7 # for PhantomJS support: https://github.com/elastic/kibana/issues/27136
+last 2 Firefox versions
+last 2 Chrome versions
+last 2 Safari versions
+> 0.25%
+not ie 11
[dev]
last 1 chrome versions
diff --git a/.ci/Jenkinsfile_baseline_capture b/.ci/Jenkinsfile_baseline_capture
index 7c7cc8d98c306..3f90a6bc05af0 100644
--- a/.ci/Jenkinsfile_baseline_capture
+++ b/.ci/Jenkinsfile_baseline_capture
@@ -8,12 +8,12 @@ kibanaPipeline(timeoutMinutes: 120) {
catchError {
parallel([
'oss-visualRegression': {
- workers.ci(name: 'oss-visualRegression', size: 's', ramDisk: false) {
+ workers.ci(name: 'oss-visualRegression', size: 's-highmem', ramDisk: true) {
kibanaPipeline.functionalTestProcess('oss-visualRegression', './test/scripts/jenkins_visual_regression.sh')(1)
}
},
'xpack-visualRegression': {
- workers.ci(name: 'xpack-visualRegression', size: 's', ramDisk: false) {
+ workers.ci(name: 'xpack-visualRegression', size: 's-highmem', ramDisk: true) {
kibanaPipeline.functionalTestProcess('xpack-visualRegression', './test/scripts/jenkins_xpack_visual_regression.sh')(1)
}
},
diff --git a/.ci/Jenkinsfile_coverage b/.ci/Jenkinsfile_coverage
index 3986367d660a1..ebb9c3dc86dd2 100644
--- a/.ci/Jenkinsfile_coverage
+++ b/.ci/Jenkinsfile_coverage
@@ -13,6 +13,7 @@ kibanaPipeline(timeoutMinutes: 240) {
workers.base(name: 'coverage-worker', size: 'l', ramDisk: false, bootstrapped: false) {
catchError {
kibanaCoverage.runTests()
+ kibanaTeamAssign.load('team_assignment', "### Upload Team Assignment JSON")
handleIngestion(TIME_STAMP)
}
handleFail()
diff --git a/.ci/pipeline-library/src/test/prChanges.groovy b/.ci/pipeline-library/src/test/prChanges.groovy
index 0fb750d6ff64e..f149340517ff0 100644
--- a/.ci/pipeline-library/src/test/prChanges.groovy
+++ b/.ci/pipeline-library/src/test/prChanges.groovy
@@ -84,4 +84,17 @@ class PrChangesTest extends KibanaBasePipelineTest {
assertFalse(prChanges.areChangesSkippable())
}
+
+ @Test
+ void 'areChangesSkippable() with skippable changes that are in notSkippablePaths'() {
+ props([
+ githubPrs: [
+ getChanges: { [
+ [filename: 'docs/developer/architecture/code-exploration.asciidoc'],
+ ] },
+ ],
+ ])
+
+ assertFalse(prChanges.areChangesSkippable())
+ }
}
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index f053c6da9c29b..2ad82ded6cb38 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -24,29 +24,20 @@
/src/plugins/vis_type_xy/ @elastic/kibana-app
/src/plugins/visualize/ @elastic/kibana-app
-# Core UI
-# Exclude tutorials folder for now because they are not owned by Kibana app and most will move out soon
-/src/plugins/home/public @elastic/kibana-core-ui
-/src/plugins/home/server/*.ts @elastic/kibana-core-ui
-/src/plugins/home/server/services/ @elastic/kibana-core-ui
-# Exclude tutorial resources folder for now because they are not owned by Kibana app and most will move out soon
-/src/legacy/core_plugins/kibana/public/home/*.ts @elastic/kibana-core-ui
-/src/legacy/core_plugins/kibana/public/home/*.scss @elastic/kibana-core-ui
-/src/legacy/core_plugins/kibana/public/home/np_ready/ @elastic/kibana-core-ui
-
# App Architecture
+/examples/bfetch_explorer/ @elastic/kibana-app-arch
+/examples/dashboard_embeddable_examples/ @elastic/kibana-app-arch
+/examples/demo_search/ @elastic/kibana-app-arch
/examples/developer_examples/ @elastic/kibana-app-arch
+/examples/embeddable_examples/ @elastic/kibana-app-arch
+/examples/embeddable_explorer/ @elastic/kibana-app-arch
+/examples/state_container_examples/ @elastic/kibana-app-arch
+/examples/ui_actions_examples/ @elastic/kibana-app-arch
+/examples/ui_actions_explorer/ @elastic/kibana-app-arch
/examples/url_generators_examples/ @elastic/kibana-app-arch
/examples/url_generators_explorer/ @elastic/kibana-app-arch
-/packages/kbn-interpreter/ @elastic/kibana-app-arch
/packages/elastic-datemath/ @elastic/kibana-app-arch
-/src/legacy/core_plugins/embeddable_api/ @elastic/kibana-app-arch
-/src/legacy/core_plugins/interpreter/ @elastic/kibana-app-arch
-/src/legacy/core_plugins/kibana_react/ @elastic/kibana-app-arch
-/src/legacy/core_plugins/kibana/public/management/ @elastic/kibana-app-arch
-/src/legacy/core_plugins/kibana/server/routes/api/management/ @elastic/kibana-app-arch
-/src/legacy/core_plugins/visualizations/ @elastic/kibana-app-arch
-/src/legacy/server/index_patterns/ @elastic/kibana-app-arch
+/packages/kbn-interpreter/ @elastic/kibana-app-arch
/src/plugins/advanced_settings/ @elastic/kibana-app-arch
/src/plugins/bfetch/ @elastic/kibana-app-arch
/src/plugins/data/ @elastic/kibana-app-arch
@@ -61,9 +52,10 @@
/src/plugins/share/ @elastic/kibana-app-arch
/src/plugins/ui_actions/ @elastic/kibana-app-arch
/src/plugins/visualizations/ @elastic/kibana-app-arch
-/x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch
+/x-pack/examples/ui_actions_enhanced_examples/ @elastic/kibana-app-arch
/x-pack/plugins/data_enhanced/ @elastic/kibana-app-arch
-/x-pack/plugins/drilldowns/ @elastic/kibana-app-arch
+/x-pack/plugins/embeddable_enhanced/ @elastic/kibana-app-arch
+/x-pack/plugins/ui_actions_enhanced/ @elastic/kibana-app-arch
# APM
/x-pack/plugins/apm/ @elastic/apm-ui
@@ -79,6 +71,16 @@
/x-pack/plugins/canvas/ @elastic/kibana-canvas
/x-pack/test/functional/apps/canvas/ @elastic/kibana-canvas
+# Core UI
+# Exclude tutorials folder for now because they are not owned by Kibana app and most will move out soon
+/src/plugins/home/public @elastic/kibana-core-ui
+/src/plugins/home/server/*.ts @elastic/kibana-core-ui
+/src/plugins/home/server/services/ @elastic/kibana-core-ui
+# Exclude tutorial resources folder for now because they are not owned by Kibana app and most will move out soon
+/src/legacy/core_plugins/kibana/public/home/*.ts @elastic/kibana-core-ui
+/src/legacy/core_plugins/kibana/public/home/*.scss @elastic/kibana-core-ui
+/src/legacy/core_plugins/kibana/public/home/np_ready/ @elastic/kibana-core-ui
+
# Observability UIs
/x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui
/x-pack/plugins/infra/ @elastic/logs-metrics-ui
diff --git a/NOTICE.txt b/NOTICE.txt
index 56280e6e3883e..e1552852d0349 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -26,9 +26,6 @@ This module was heavily inspired by the externals plugin that ships with webpack
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
----
-This product has relied on ASTExplorer that is licensed under MIT.
-
---
This product includes code that is based on Ace editor, which was available
under a "BSD" license.
diff --git a/docs/developer/advanced/development-basepath.asciidoc b/docs/developer/advanced/development-basepath.asciidoc
index f0b760a21ea0c..cb341b9591174 100644
--- a/docs/developer/advanced/development-basepath.asciidoc
+++ b/docs/developer/advanced/development-basepath.asciidoc
@@ -1,5 +1,5 @@
[[development-basepath]]
-=== Considerations for basepath
+== Considerations for basepath
In dev mode, {kib} by default runs behind a proxy which adds a random path component to its URL.
diff --git a/docs/developer/advanced/development-es-snapshots.asciidoc b/docs/developer/advanced/development-es-snapshots.asciidoc
index 92fae7a241edf..4c801bf750979 100644
--- a/docs/developer/advanced/development-es-snapshots.asciidoc
+++ b/docs/developer/advanced/development-es-snapshots.asciidoc
@@ -1,32 +1,32 @@
[[development-es-snapshots]]
-=== Daily Elasticsearch Snapshots
+== Daily {es} Snapshots
-For local development and CI, {kib}, by default, uses Elasticsearch snapshots that are built daily when running tasks that require Elasticsearch (e.g. functional tests).
+For local development and CI, {kib}, by default, uses {es} snapshots that are built daily when running tasks that require {es} (e.g. functional tests).
-A snapshot is just a group of tarballs, one for each supported distribution/architecture/os of Elasticsearch, and a JSON-based manifest file containing metadata about the distributions.
+A snapshot is just a group of tarballs, one for each supported distribution/architecture/os of {es}, and a JSON-based manifest file containing metadata about the distributions.
-https://ci.kibana.dev/es-snapshots[A dashboard] is available that shows the current status and compatibility of the latest Elasticsearch snapshots.
+https://ci.kibana.dev/es-snapshots[A dashboard] is available that shows the current status and compatibility of the latest {es} snapshots.
-==== Process Overview
+=== Process Overview
-1. Elasticsearch snapshots are built for each current tracked branch of {kib}.
+1. {es} snapshots are built for each current tracked branch of {kib}.
2. Each snapshot is uploaded to a public Google Cloud Storage bucket, `kibana-ci-es-snapshots-daily`.
** At this point, the snapshot is not automatically used in CI or local development. It needs to be tested/verified first.
3. Each snapshot is tested with the latest commit of the corresponding {kib} branch, using the full CI suite.
4. After CI
** If the snapshot passes, it is promoted and automatically used in CI and local development.
-** If the snapshot fails, the issue must be investigated and resolved. A new incompatibility may exist between Elasticsearch and {kib}.
+** If the snapshot fails, the issue must be investigated and resolved. A new incompatibility may exist between {es} and {kib}.
-==== Using the latest snapshot
+=== Using the latest snapshot
-When developing locally, you may wish to use the most recent Elasticsearch snapshot, even if it's failing CI. To do so, prefix your commands with the follow environment variable:
+When developing locally, you may wish to use the most recent {es} snapshot, even if it's failing CI. To do so, prefix your commands with the follow environment variable:
["source","bash"]
-----------
KBN_ES_SNAPSHOT_USE_UNVERIFIED=true
-----------
-You can use this flag with any command that downloads and runs Elasticsearch snapshots, such as `scripts/es` or the FTR.
+You can use this flag with any command that downloads and runs {es} snapshots, such as `scripts/es` or the FTR.
For example, to run functional tests with the latest snapshot:
@@ -35,7 +35,7 @@ For example, to run functional tests with the latest snapshot:
KBN_ES_SNAPSHOT_USE_UNVERIFIED=true node scripts/functional_tests_server
-----------
-===== For Pull Requests
+==== For Pull Requests
Currently, there is not a way to run your pull request with the latest unverified snapshot without a code change. You can, however, do it with a small code change.
@@ -45,9 +45,9 @@ Currently, there is not a way to run your pull request with the latest unverifie
Your pull request should then use the latest snapshot the next time that it runs. Just don't merge the change to `Jenkinsfile`!
-==== Google Cloud Storage buckets
+=== Google Cloud Storage buckets
-===== kibana-ci-es-snapshots-daily
+==== kibana-ci-es-snapshots-daily
This bucket stores snapshots that are created on a daily basis, and is the primary location used by `kbn-es` to download snapshots.
@@ -61,7 +61,7 @@ The file structure for this bucket looks like this:
* `/archives//*.tar.gz.sha512`
* `/archives//manifest.json`
-===== kibana-ci-es-snapshots-permanent
+==== kibana-ci-es-snapshots-permanent
This bucket stores only the most recently promoted snapshot for each version. Old snapshots are only deleted when new ones are uploaded.
@@ -73,18 +73,18 @@ The file structure for this bucket looks like this:
* `/*.tar.gz.sha512`
* `/manifest.json`
-==== How snapshots are built, tested, and promoted
+=== How snapshots are built, tested, and promoted
-Each day, a https://kibana-ci.elastic.co/job/elasticsearch+snapshots+trigger/[Jenkins job] runs that triggers Elasticsearch builds for each currently tracked branch/version. This job is automatically updated with the correct branches whenever we release new versions of {kib}.
+Each day, a https://kibana-ci.elastic.co/job/elasticsearch+snapshots+trigger/[Jenkins job] runs that triggers {es} builds for each currently tracked branch/version. This job is automatically updated with the correct branches whenever we release new versions of {kib}.
-===== Build
+==== Build
-https://kibana-ci.elastic.co/job/elasticsearch+snapshots+build/[This Jenkins job] builds the Elasticsearch snapshots and uploads them to GCS.
+https://kibana-ci.elastic.co/job/elasticsearch+snapshots+build/[This Jenkins job] builds the {es} snapshots and uploads them to GCS.
The Jenkins job pipeline definition is https://github.com/elastic/kibana/blob/master/.ci/es-snapshots/Jenkinsfile_build_es[in the {kib} repo].
-1. Checkout Elasticsearch repo for the given branch/version.
-2. Run `./gradlew -p distribution/archives assemble --parallel` to create all of the Elasticsearch distributions.
+1. Checkout {es} repo for the given branch/version.
+2. Run `./gradlew -p distribution/archives assemble --parallel` to create all of the {es} distributions.
3. Create a tarball for each distribution.
4. Create a manifest JSON file containing info about the distribution, as well as its download URL.
5. Upload the tarballs and manifest to a unique location in the GCS bucket `kibana-ci-es-snapshots-daily`.
@@ -93,9 +93,9 @@ The Jenkins job pipeline definition is https://github.com/elastic/kibana/blob/ma
** This allows the `KBN_ES_SNAPSHOT_USE_UNVERIFIED` flag to work.
7. Trigger the verification job, to run the full {kib} CI test suite with this snapshot.
-===== Verification and Promotion
+==== Verification and Promotion
-https://kibana-ci.elastic.co/job/elasticsearch+snapshots+verify/[This Jenkins job] tests the latest Elasticsearch snapshot with the full {kib} CI pipeline, and promotes if it there are no test failures.
+https://kibana-ci.elastic.co/job/elasticsearch+snapshots+verify/[This Jenkins job] tests the latest {es} snapshot with the full {kib} CI pipeline, and promotes if it there are no test failures.
The Jenkins job pipeline definition is https://github.com/elastic/kibana/blob/master/.ci/es-snapshots/Jenkinsfile_verify_es[in the {kib} repo].
diff --git a/docs/developer/advanced/index.asciidoc b/docs/developer/advanced/index.asciidoc
index 139940ee42fe2..5c53bedd95e72 100644
--- a/docs/developer/advanced/index.asciidoc
+++ b/docs/developer/advanced/index.asciidoc
@@ -5,8 +5,8 @@
* <>
* <>
-include::development-es-snapshots.asciidoc[]
+include::development-es-snapshots.asciidoc[leveloffset=+1]
-include::running-elasticsearch.asciidoc[]
+include::running-elasticsearch.asciidoc[leveloffset=+1]
-include::development-basepath.asciidoc[]
\ No newline at end of file
+include::development-basepath.asciidoc[leveloffset=+1]
\ No newline at end of file
diff --git a/docs/developer/advanced/running-elasticsearch.asciidoc b/docs/developer/advanced/running-elasticsearch.asciidoc
index b03c231678eee..2361f805c7635 100644
--- a/docs/developer/advanced/running-elasticsearch.asciidoc
+++ b/docs/developer/advanced/running-elasticsearch.asciidoc
@@ -1,13 +1,13 @@
[[running-elasticsearch]]
-=== Running elasticsearch during development
+== Running {es} during development
-There are many ways to run Elasticsearch while you are developing.
+There are many ways to run {es} while you are developing.
-[float]
+[discrete]
-==== By snapshot
+=== By snapshot
-This will run a snapshot of elasticsearch that is usually built nightly. Read more about <>.
+This will run a snapshot of {es} that is usually built nightly. Read more about <>.
[source,bash]
----
@@ -25,36 +25,36 @@ yarn es snapshot --help
**Keeping data between snapshots**
-If you want to keep the data inside your Elasticsearch between usages of this command, you should use the following command, to keep your data folder outside the downloaded snapshot folder:
+If you want to keep the data inside your {es} between usages of this command, you should use the following command, to keep your data folder outside the downloaded snapshot folder:
[source,bash]
----
yarn es snapshot -E path.data=../data
----
-==== By source
+=== By source
-If you have the Elasticsearch repo checked out locally and wish to run against that, use `source`. By default, it will reference an elasticsearch checkout which is a sibling to the {kib} directory named elasticsearch. If you wish to use a checkout in another location you can provide that by supplying --source-path
+If you have the {es} repo checked out locally and wish to run against that, use `source`. By default, it will reference an {es} checkout which is a sibling to the {kib} directory named elasticsearch. If you wish to use a checkout in another location you can provide that by supplying --source-path
[source,bash]
----
yarn es source
----
-==== From an archive
+=== From an archive
-Use this if you already have a distributable. For released versions, one can be obtained on the Elasticsearch downloads page.
+Use this if you already have a distributable. For released versions, one can be obtained on the {es} downloads page.
[source,bash]
----
yarn es archive
----
-Each of these will run Elasticsearch with a basic license. Additional options are available, pass --help for more information.
+Each of these will run {es} with a basic license. Additional options are available, pass --help for more information.
-==== From a remote host
+=== From a remote host
-You can save some system resources, and the effort of generating sample data, if you have a remote Elasticsearch cluster to connect to. (Elasticians: you do! Check with your team about where to find credentials)
+You can save some system resources, and the effort of generating sample data, if you have a remote {es} cluster to connect to. (Elasticians: you do! Check with your team about where to find credentials)
You'll need to create a kibana.dev.yml (<>) and add the following to it:
@@ -75,7 +75,7 @@ kibana.index: '.{YourGitHubHandle}-kibana'
xpack.task_manager.index: '.{YourGitHubHandle}-task-manager-kibana'
----
-===== Running remote clusters
+==== Running remote clusters
Setup remote clusters for cross cluster search (CCS) and cross cluster replication (CCR).
@@ -95,7 +95,7 @@ yarn es snapshot -E transport.port=9500 -E http.port=9201 -E path.data=../data_p
Once both clusters are running, start {kib}. {kib} will connect to the primary cluster.
-Setup the remote cluster in {kib} from either Management -> Elasticsearch -> Remote Clusters UI or by running the following script in Console.
+Setup the remote cluster in {kib} from either Management -> {es} -> Remote Clusters UI or by running the following script in Console.
[source,bash]
----
diff --git a/docs/developer/architecture/add-data-tutorials.asciidoc b/docs/developer/architecture/add-data-tutorials.asciidoc
index e16b1bc039a10..3891b87a00e64 100644
--- a/docs/developer/architecture/add-data-tutorials.asciidoc
+++ b/docs/developer/architecture/add-data-tutorials.asciidoc
@@ -1,16 +1,16 @@
[[add-data-tutorials]]
-=== Add data tutorials
+== Add data tutorials
`Add Data` in the {kib} Home application contains tutorials for setting up data flows in the Elastic stack.
Each tutorial contains three sets of instructions:
-* `On Premise.` Set up a data flow when both {kib} and Elasticsearch are running on premise.
-* `On Premise Elastic Cloud.` Set up a data flow when {kib} is running on premise and Elasticsearch is running on Elastic Cloud.
-* `Elastic Cloud.` Set up a data flow when both {kib} and Elasticsearch are running on Elastic Cloud.
+* `On Premise.` Set up a data flow when both {kib} and {es} are running on premise.
+* `On Premise Elastic Cloud.` Set up a data flow when {kib} is running on premise and {es} is running on Elastic Cloud.
+* `Elastic Cloud.` Set up a data flow when both {kib} and {es} are running on Elastic Cloud.
-[float]
-==== Creating a new tutorial
+[discrete]
+=== Creating a new tutorial
1. Create a new directory in the link:https://github.com/elastic/kibana/tree/master/src/plugins/home/server/tutorials[tutorials directory].
2. In the new directory, create a file called `index.ts` that exports a function.
The function must return a function object that conforms to the `TutorialSchema` interface link:{kib-repo}tree/{branch}/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts[tutorial schema].
@@ -23,15 +23,15 @@ The function must return a function object that conforms to the `TutorialSchema`
If you are creating a new plugin and the tutorial is only related to that plugin, you can also place the `TutorialSchema` object into your plugin folder. Add `home` to the `requiredPlugins` list in your `kibana.json` file.
Then register the tutorial object by calling `home.tutorials.registerTutorial(tutorialObject)` in the `setup` lifecycle of your server plugin.
-[float]
-===== Variables
+[discrete]
+==== Variables
String values can contain variables that are substituted when rendered. Variables are specified by `{}`.
For example: `{config.docs.version}` is rendered as `6.2` when running the tutorial in {kib} 6.2.
link:{kib-repo}tree/{branch}/src/legacy/core_plugins/kibana/public/home/np_ready/components/tutorial/replace_template_strings.js#L23[Provided variables]
-[float]
-===== Markdown
+[discrete]
+==== Markdown
String values can contain limited Markdown syntax.
link:{kib-repo}tree/{branch}/src/legacy/core_plugins/kibana/public/home/components/tutorial/content.js#L8[Enabled Markdown grammars]
diff --git a/docs/developer/architecture/code-exploration.asciidoc b/docs/developer/architecture/code-exploration.asciidoc
new file mode 100644
index 0000000000000..2f67ae002c916
--- /dev/null
+++ b/docs/developer/architecture/code-exploration.asciidoc
@@ -0,0 +1,589 @@
+////
+
+NOTE:
+ This is an automatically generated file. Please do not edit directly. Instead, run the
+ following from within the kibana repository:
+
+ node scripts/build_plugin_list_docs
+
+ You can update the template within packages/kbn-dev-utils/target/plugin_list/generate_plugin_list.js
+
+////
+
+[[code-exploration]]
+== Exploring Kibana code
+
+The goals of our folder heirarchy are:
+
+- Easy for developers to know where to add new services, plugins and applications.
+- Easy for developers to know where to find the code from services, plugins and applications.
+- Easy to browse and understand our folder structure.
+
+To that aim, we strive to:
+
+- Avoid too many files in any given folder.
+- Choose clear, unambigious folder names.
+- Organize by domain.
+- Every folder should contain a README that describes the contents of that folder.
+
+[discrete]
+[[kibana-services-applications]]
+=== Services and Applications
+
+[discrete]
+==== src/plugins
+
+- {kib-repo}blob/{branch}/src/plugins/advanced_settings[advancedSettings]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/apm_oss[apmOss]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/bfetch/README.md[bfetch]
+
+bfetch allows to batch HTTP requests and streams responses back.
+
+
+- {kib-repo}blob/{branch}/src/plugins/charts/README.md[charts]
+
+The Charts plugin is a way to create easier integration of shared colors, themes, types and other utilities across all Kibana charts and visualizations.
+
+
+- {kib-repo}blob/{branch}/src/plugins/console[console]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/dashboard[dashboard]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/data/README.md[data]
+
+data plugin provides common data access services.
+
+
+- {kib-repo}blob/{branch}/src/plugins/dev_tools/README.md[devTools]
+
+The ui/registry/dev_tools is removed in favor of the devTools plugin which exposes a register method in the setup contract.
+Registering app works mostly the same as registering apps in core.application.register.
+Routing will be handled by the id of the dev tool - your dev tool will be mounted when the URL matches /app/dev_tools#/.
+This API doesn't support angular, for registering angular dev tools, bootstrap a local module on mount into the given HTML element.
+
+
+- {kib-repo}blob/{branch}/src/plugins/discover[discover]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/embeddable/README.md[embeddable]
+
+Embeddables are re-usable widgets that can be rendered in any environment or plugin. Developers can embed them directly in their plugin. End users can dynamically add them to any embeddable containers.
+
+
+- {kib-repo}blob/{branch}/src/plugins/es_ui_shared[esUiShared]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/expressions/README.md[expressions]
+
+This plugin provides methods which will parse & execute an expression pipeline
+string for you, as well as a series of registries for advanced users who might
+want to incorporate their own functions, types, and renderers into the service
+for use in their own application.
+
+
+- {kib-repo}blob/{branch}/src/plugins/home/README.md[home]
+
+Moves the legacy ui/registry/feature_catalogue module for registering "features" that should be shown in the home page's feature catalogue to a service within a "home" plugin. The feature catalogue refered to here should not be confused with the "feature" plugin for registering features used to derive UI capabilities for feature controls.
+
+
+- {kib-repo}blob/{branch}/src/plugins/index_pattern_management[indexPatternManagement]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/input_control_vis[inputControlVis]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/inspector/README.md[inspector]
+
+The inspector is a contextual tool to gain insights into different elements
+in Kibana, e.g. visualizations. It has the form of a flyout panel.
+
+
+- {kib-repo}blob/{branch}/src/plugins/kibana_legacy/README.md[kibanaLegacy]
+
+This plugin will contain several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform.
+
+
+- {kib-repo}blob/{branch}/src/plugins/kibana_react/README.md[kibanaReact]
+
+Tools for building React applications in Kibana.
+
+
+- {kib-repo}blob/{branch}/src/plugins/kibana_usage_collection/README.md[kibanaUsageCollection]
+
+This plugin registers the basic usage collectors from Kibana:
+
+
+- {kib-repo}blob/{branch}/src/plugins/kibana_utils/README.md[kibanaUtils]
+
+Utilities for building Kibana plugins.
+
+
+- {kib-repo}blob/{branch}/src/plugins/legacy_export[legacyExport]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/management[management]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/maps_legacy[mapsLegacy]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/navigation/README.md[navigation]
+
+The navigation plugins exports the TopNavMenu component.
+It also provides a stateful version of it on the start contract.
+
+
+- {kib-repo}blob/{branch}/src/plugins/newsfeed[newsfeed]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/region_map[regionMap]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/saved_objects[savedObjects]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/saved_objects_management[savedObjectsManagement]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/share/README.md[share]
+
+Replaces the legacy ui/share module for registering share context menus.
+
+
+- {kib-repo}blob/{branch}/src/plugins/status_page[statusPage]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/telemetry/README.md[telemetry]
+
+Telemetry allows Kibana features to have usage tracked in the wild. The general term "telemetry" refers to multiple things:
+
+
+- {kib-repo}blob/{branch}/src/plugins/telemetry_collection_manager/README.md[telemetryCollectionManager]
+
+Telemetry's collection manager to go through all the telemetry sources when fetching it before reporting.
+
+
+- {kib-repo}blob/{branch}/src/plugins/telemetry_management_section/README.md[telemetryManagementSection]
+
+This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry).
+
+
+- {kib-repo}blob/{branch}/src/plugins/tile_map[tileMap]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/timelion[timelion]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/ui_actions/README.md[uiActions]
+
+An API for:
+
+
+- {kib-repo}blob/{branch}/src/plugins/usage_collection/README.md[usageCollection]
+
+Usage Collection allows collecting usage data for other services to consume (telemetry and monitoring).
+To integrate with the telemetry services for usage collection of your feature, there are 2 steps:
+
+
+- {kib-repo}blob/{branch}/src/plugins/vis_type_markdown[visTypeMarkdown]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/vis_type_metric[visTypeMetric]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/vis_type_table[visTypeTable]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/vis_type_tagcloud[visTypeTagcloud]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/vis_type_timelion/README.md[visTypeTimelion]
+
+If your grammar was changed in public/chain.peg you need to re-generate the static parser. You could use a grunt task:
+
+
+- {kib-repo}blob/{branch}/src/plugins/vis_type_timeseries[visTypeTimeseries]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/vis_type_vega[visTypeVega]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/vis_type_vislib[visTypeVislib]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/vis_type_xy[visTypeXy]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/visualizations[visualizations]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/src/plugins/visualize[visualize]
+
+WARNING: Missing README.
+
+
+[discrete]
+==== x-pack/plugins
+
+- {kib-repo}blob/{branch}/x-pack/plugins/actions/README.md[actions]
+
+The Kibana actions plugin provides a framework to create executable actions. You can:
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/alerting_builtins/README.md[alertingBuiltins]
+
+This plugin provides alertTypes shipped with Kibana for use with the
+the alerts plugin. When enabled, it will register
+the built-in alertTypes with the alerting plugin, register associated HTTP
+routes, etc.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/alerts/README.md[alerts]
+
+The Kibana alerting plugin provides a common place to set up alerts. You can:
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/apm/readme.md[apm]
+
+To access an elasticsearch instance that has live data you have two options:
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/audit_trail[auditTrail]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/beats_management[beats_management]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/canvas/README.md[canvas]
+
+"Never look back. The past is done. The future is a blank canvas." ― Suzy Kassem, Rise Up and Salute the Sun
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/case/README.md[case]
+
+Experimental Feature
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/cloud[cloud]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/code[code]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/console_extensions[consoleExtensions]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/cross_cluster_replication/README.md[crossClusterReplication]
+
+You can run a local cluster and simulate a remote cluster within a single Kibana directory.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/dashboard_enhanced/README.md[dashboardEnhanced]
+
+- {kib-repo}blob/{branch}/x-pack/plugins/dashboard_mode[dashboardMode]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/data_enhanced[dataEnhanced]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/discover_enhanced[discoverEnhanced]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/embeddable_enhanced[embeddableEnhanced]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/encrypted_saved_objects/README.md[encryptedSavedObjects]
+
+The purpose of this plugin is to provide a way to encrypt/decrypt attributes on the custom Saved Objects that works with
+security and spaces filtering as well as performing audit logging.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/enterprise_search/README.md[enterpriseSearch]
+
+This plugin's goal is to provide a Kibana user interface to the Enterprise Search solution's products (App Search and Workplace Search). In it's current MVP state, the plugin provides the following with the goal of gathering user feedback and raising product awareness:
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/event_log/README.md[eventLog]
+
+The purpose of this plugin is to provide a way to persist a history of events
+occuring in Kibana, initially just for the Make It Action project - alerts
+and actions.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/features[features]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/file_upload[fileUpload]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/global_search/README.md[globalSearch]
+
+The GlobalSearch plugin provides an easy way to search for various objects, such as applications
+or dashboards from the Kibana instance, from both server and client-side plugins
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/global_search_providers[globalSearchProviders]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/graph/README.md[graph]
+
+This is the main source folder of the Graph plugin. It contains all of the Kibana server and client source code. x-pack/test/functional/apps/graph contains additional functional tests.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/grokdebugger/README.md[grokdebugger]
+
+- {kib-repo}blob/{branch}/x-pack/plugins/index_lifecycle_management/README.md[indexLifecycleManagement]
+
+You can test that the Frozen badge, phase filtering, and lifecycle information is surfaced in
+Index Management by running this series of requests in Console:
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/index_management[indexManagement]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/infra/README.md[infra]
+
+This is the home of the infra plugin, which aims to provide a solution for
+the infrastructure monitoring use-case within Kibana.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/ingest_manager/README.md[ingestManager]
+
+Fleet needs to have Elasticsearch API keys enabled, and also to have TLS enabled on kibana, (if you want to run Kibana without TLS you can provide the following config flag --xpack.ingestManager.fleet.tlsCheckDisabled=false)
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/ingest_pipelines/README.md[ingestPipelines]
+
+The ingest_pipelines plugin provides Kibana support for Elasticsearch's ingest nodes. Please refer to the Elasticsearch documentation for more details.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/lens/readme.md[lens]
+
+Run all tests from the x-pack root directory
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/license_management[licenseManagement]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/licensing/README.md[licensing]
+
+The licensing plugin retrieves license data from Elasticsearch at regular configurable intervals.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/lists/README.md[lists]
+
+README.md for developers working on the backend lists on how to get started
+using the CURL scripts in the scripts folder.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/logstash[logstash]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/maps/README.md[maps]
+
+Visualize geo data from Elasticsearch or 3rd party geo-services.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/maps_legacy_licensing/README.md[mapsLegacyLicensing]
+
+This plugin provides access to the detailed tile map services from Elastic.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/ml[ml]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/monitoring[monitoring]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/observability/README.md[observability]
+
+This plugin provides shared components and services for use across observability solutions, as well as the observability landing page UI.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/oss_telemetry[ossTelemetry]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/painless_lab[painlessLab]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/remote_clusters[remoteClusters]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/reporting/README.md[reporting]
+
+An awesome Kibana reporting plugin
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/rollup/README.md[rollup]
+
+Welcome to the Kibana rollup plugin! This plugin provides Kibana support for Elasticsearch's rollup feature. Please refer to the Elasticsearch documentation to understand rollup indices and how to create rollup jobs.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/searchprofiler[searchprofiler]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/security/README.md[security]
+
+See Configuring security in Kibana.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/security_solution[securitySolution]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/snapshot_restore/README.md[snapshotRestore]
+
+or
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/spaces[spaces]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/task_manager[taskManager]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/telemetry_collection_xpack/README.md[telemetryCollectionXpack]
+
+Gathers all usage collection, retrieving them from both: OSS and X-Pack plugins.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/transform[transform]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/translations[translations]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/triggers_actions_ui/README.md[triggers_actions_ui]
+
+The Kibana alerts and actions UI plugin provides a user interface for managing alerts and actions.
+As a developer you can reuse and extend built-in alerts and actions UI functionality:
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/ui_actions_enhanced/README.md[uiActionsEnhanced]
+
+- {kib-repo}blob/{branch}/x-pack/plugins/upgrade_assistant[upgradeAssistant]
+
+WARNING: Missing README.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/uptime/README.md[uptime]
+
+The purpose of this plugin is to provide users of Heartbeat more visibility of what's happening
+in their infrastructure.
+
+
+- {kib-repo}blob/{branch}/x-pack/plugins/watcher/README.md[watcher]
+
+This plugins adopts some conventions in addition to or in place of conventions in Kibana (at the time of the plugin's creation):
+
diff --git a/docs/developer/architecture/development-visualize-index.asciidoc b/docs/developer/architecture/development-visualize-index.asciidoc
index 551c41833fb72..d41ee32c1fb27 100644
--- a/docs/developer/architecture/development-visualize-index.asciidoc
+++ b/docs/developer/architecture/development-visualize-index.asciidoc
@@ -1,5 +1,5 @@
[[development-visualize-index]]
-=== Developing Visualizations
+== Developing Visualizations
[IMPORTANT]
==============================================
diff --git a/docs/developer/architecture/index.asciidoc b/docs/developer/architecture/index.asciidoc
index d726a8bd3642d..2e6ab1a4ad6ac 100644
--- a/docs/developer/architecture/index.asciidoc
+++ b/docs/developer/architecture/index.asciidoc
@@ -17,9 +17,12 @@ A few notable services are called out below.
* <>
* <>
* <>
+* <>
-include::add-data-tutorials.asciidoc[]
+include::add-data-tutorials.asciidoc[leveloffset=+1]
-include::development-visualize-index.asciidoc[]
+include::development-visualize-index.asciidoc[leveloffset=+1]
-include::security/index.asciidoc[]
+include::security/index.asciidoc[leveloffset=+1]
+
+include::code-exploration.asciidoc[leveloffset=+1]
diff --git a/docs/developer/architecture/security/feature-registration.asciidoc b/docs/developer/architecture/security/feature-registration.asciidoc
index 164f6d1cf9c74..3724624dbb917 100644
--- a/docs/developer/architecture/security/feature-registration.asciidoc
+++ b/docs/developer/architecture/security/feature-registration.asciidoc
@@ -1,13 +1,13 @@
[[development-plugin-feature-registration]]
-==== Plugin feature registration
+== Plugin feature registration
If your plugin will be used with {kib}'s default distribution, then you have the ability to register the features that your plugin provides. Features are typically apps in {kib}; once registered, you can toggle them via Spaces, and secure them via Roles when security is enabled.
-===== UI Capabilities
+=== UI Capabilities
Registering features also gives your plugin access to “UI Capabilities”. These capabilities are boolean flags that you can use to conditionally render your interface, based on the current user's permissions. For example, you can hide or disable a Save button if the current user is not authorized.
-===== Registering a feature
+=== Registering a feature
Feature registration is controlled via the built-in `xpack_main` plugin. To register a feature, call `xpack_main`'s `registerFeature` function from your plugin's `init` function, and provide the appropriate details:
@@ -21,7 +21,7 @@ init(server) {
}
-----------
-===== Feature details
+=== Feature details
Registering a feature consists of the following fields. For more information, consult the {kib-repo}blob/{branch}/x-pack/plugins/features/server/feature_registry.ts[feature registry interface].
@@ -65,12 +65,12 @@ Registering a feature consists of the following fields. For more information, co
|The ID of the navigation link associated with your feature.
|===
-====== Privilege definition
+==== Privilege definition
The `privileges` section of feature registration allows plugins to implement read/write and read-only modes for their applications.
For a full explanation of fields and options, consult the {kib-repo}blob/{branch}/x-pack/plugins/features/server/feature_registry.ts[feature registry interface].
-===== Using UI Capabilities
+=== Using UI Capabilities
UI Capabilities are available to your public (client) plugin code. These capabilities are read-only, and are used to inform the UI. This object is namespaced by feature id. For example, if your feature id is “foo”, then your UI Capabilities are stored at `uiCapabilities.foo`.
To access capabilities, import them from `ui/capabilities`:
@@ -86,7 +86,7 @@ if (canUserSave) {
-----------
[[example-1-canvas]]
-===== Example 1: Canvas Application
+=== Example 1: Canvas Application
["source","javascript"]
-----------
init(server) {
@@ -141,7 +141,7 @@ if (canUserSave) {
Because the `read` privilege does not define the `save` capability, users with read-only access will have their `uiCapabilities.canvas.save` flag set to `false`.
[[example-2-dev-tools]]
-===== Example 2: Dev Tools
+=== Example 2: Dev Tools
["source","javascript"]
-----------
@@ -176,7 +176,7 @@ init(server) {
},
privilegesTooltip: i18n.translate('xpack.features.devToolsPrivilegesTooltip', {
defaultMessage:
- 'User should also be granted the appropriate Elasticsearch cluster and index privileges',
+ 'User should also be granted the appropriate {es} cluster and index privileges',
}),
});
}
@@ -199,7 +199,7 @@ server.route({
-----------
[[example-3-discover]]
-===== Example 3: Discover
+=== Example 3: Discover
Discover takes advantage of subfeature privileges to allow fine-grained access control. In this example,
a single "Create Short URLs" subfeature privilege is defined, which allows users to grant access to this feature without having to grant the `all` privilege to Discover. In other words, you can grant `read` access to Discover, and also grant the ability to create short URLs.
diff --git a/docs/developer/architecture/security/index.asciidoc b/docs/developer/architecture/security/index.asciidoc
index 55b2450caf7a7..09739142c8f79 100644
--- a/docs/developer/architecture/security/index.asciidoc
+++ b/docs/developer/architecture/security/index.asciidoc
@@ -1,12 +1,14 @@
[[development-security]]
-=== Security
+== Security
-{kib} has generally been able to implement security transparently to core and plugin developers, and this largely remains the case. {kib} on two methods that the elasticsearch `Cluster` provides: `callWithRequest` and `callWithInternalUser`.
+{kib} has generally been able to implement security transparently to core and plugin developers, and this largely remains the case. {kib} on two methods that the {es} `Cluster` provides: `callWithRequest` and `callWithInternalUser`.
-`callWithRequest` executes requests against Elasticsearch using the authentication credentials of the {kib} end-user. So, if you log into {kib} with the user of `foo` when `callWithRequest` is used, {kib} execute the request against Elasticsearch as the user `foo`. Historically, `callWithRequest` has been used extensively to perform actions that are initiated at the request of {kib} end-users.
+`callWithRequest` executes requests against {es} using the authentication credentials of the {kib} end-user. So, if you log into {kib} with the user of `foo` when `callWithRequest` is used, {kib} execute the request against {es} as the user `foo`. Historically, `callWithRequest` has been used extensively to perform actions that are initiated at the request of {kib} end-users.
-`callWithInternalUser` executes requests against Elasticsearch using the internal {kib} server user, and has historically been used for performing actions that aren't initiated by {kib} end users; for example, creating the initial `.kibana` index or performing health checks against Elasticsearch.
+`callWithInternalUser` executes requests against {es} using the internal {kib} server user, and has historically been used for performing actions that aren't initiated by {kib} end users; for example, creating the initial `.kibana` index or performing health checks against {es}.
-However, with the changes that role-based access control (RBAC) introduces, this is no longer cut and dry. {kib} now requires all access to the `.kibana` index goes through the `SavedObjectsClient`. This used to be a best practice, as the `SavedObjectsClient` was responsible for translating the documents stored in Elasticsearch to and from Saved Objects, but RBAC is now taking advantage of this abstraction to implement access control and determine when to use `callWithRequest` versus `callWithInternalUser`.
+However, with the changes that role-based access control (RBAC) introduces, this is no longer cut and dry. {kib} now requires all access to the `.kibana` index goes through the `SavedObjectsClient`. This used to be a best practice, as the `SavedObjectsClient` was responsible for translating the documents stored in {es} to and from Saved Objects, but RBAC is now taking advantage of this abstraction to implement access control and determine when to use `callWithRequest` versus `callWithInternalUser`.
-include::rbac.asciidoc[]
+include::rbac.asciidoc[leveloffset=+1]
+
+include::feature-registration.asciidoc[leveloffset=+1]
diff --git a/docs/developer/architecture/security/rbac.asciidoc b/docs/developer/architecture/security/rbac.asciidoc
index ae1979e856e23..7b35a91ca73d0 100644
--- a/docs/developer/architecture/security/rbac.asciidoc
+++ b/docs/developer/architecture/security/rbac.asciidoc
@@ -1,9 +1,9 @@
[[development-security-rbac]]
-==== Role-based access control
+== Role-based access control
Role-based access control (RBAC) in {kib} relies upon the
{ref}/security-privileges.html#application-privileges[application privileges]
-that Elasticsearch exposes. This allows {kib} to define the privileges that
+that {es} exposes. This allows {kib} to define the privileges that
{kib} wishes to grant to users, assign them to the relevant users using roles,
and then authorize the user to perform a specific action. This is handled within
a secured instance of the `SavedObjectsClient` and available transparently to
@@ -11,7 +11,7 @@ consumers when using `request.getSavedObjectsClient()` or
`savedObjects.getScopedSavedObjectsClient()`.
[[development-rbac-privileges]]
-===== {kib} Privileges
+=== {kib} Privileges
When {kib} first starts up, it executes the following `POST` request against {es}. This synchronizes the definition of the privileges with various `actions` which are later used to authorize a user:
@@ -56,7 +56,7 @@ The application is created by concatenating the prefix of `kibana-` with the val
==============================================
[[development-rbac-assigning-privileges]]
-===== Assigning {kib} Privileges
+=== Assigning {kib} Privileges
{kib} privileges are assigned to specific roles using the `applications` element. For example, the following role assigns the <> privilege at `*` `resources` (which will in the future be used to secure spaces) to the default {kib} `application`:
@@ -81,7 +81,7 @@ Roles that grant <> should be managed using the <>
* <>
-include::stability.asciidoc[]
+include::stability.asciidoc[leveloffset=+1]
-include::security.asciidoc[]
+include::security.asciidoc[leveloffset=+1]
diff --git a/docs/developer/best-practices/security.asciidoc b/docs/developer/best-practices/security.asciidoc
index 26fcc73ce2b90..79ecb08295064 100644
--- a/docs/developer/best-practices/security.asciidoc
+++ b/docs/developer/best-practices/security.asciidoc
@@ -1,5 +1,5 @@
[[security-best-practices]]
-=== Security best practices
+== Security best practices
* XSS
** Check for usages of `dangerouslySetInnerHtml`, `Element.innerHTML`,
@@ -44,7 +44,7 @@ sensitive information which end up in the HTTP Response
** Ensure no sensitive cookies are forwarded to external resources.
** Ensure that all user controllable variables that are used in
constructing a URL are escaped properly. This is relevant when using
-`transport.request` with the Elasticsearch client as no automatic
+`transport.request` with the {es} client as no automatic
escaping is performed.
* Reverse tabnabbing -
https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/HTML5_Security_Cheat_Sheet.md#tabnabbing
diff --git a/docs/developer/best-practices/stability.asciidoc b/docs/developer/best-practices/stability.asciidoc
index 68237a034be52..f4b7ae1229909 100644
--- a/docs/developer/best-practices/stability.asciidoc
+++ b/docs/developer/best-practices/stability.asciidoc
@@ -1,10 +1,10 @@
[[stability]]
-=== Stability
+== Stability
Ensure your feature will work under all possible {kib} scenarios.
-[float]
-==== Environmental configuration scenarios
+[discrete]
+=== Environmental configuration scenarios
* Cloud
** Does the feature work on *cloud environment*?
@@ -32,16 +32,16 @@ non-standard {kib} indices. (create their own custom roles)
sessions. (we have had many discuss/SDH tickets around this)
* If a proxy/loadbalancer is running between ES and {kib}
-[float]
-==== Kibana.yml settings
+[discrete]
+=== Kibana.yml settings
* Using a custom {kib} index alias
* When optional dependencies are disabled
** Ensure all your required dependencies are listed in kibana.json
dependency list!
-[float]
-==== Test coverage
+[discrete]
+=== Test coverage
* Does the feature have sufficient unit test coverage? (does it handle
storeinSessions?)
@@ -49,16 +49,16 @@ storeinSessions?)
* Does the feature have sufficient Rest API coverage test coverage?
* Does the feature have sufficient Integration test coverage?
-[float]
-==== Browser coverage
+[discrete]
+=== Browser coverage
Refer to the list of browsers and OS {kib} supports
https://www.elastic.co/support/matrix
Does the feature work efficiently on the list of supported browsers?
-[float]
-==== Upgrade Scenarios - Migration scenarios-
+[discrete]
+=== Upgrade Scenarios - Migration scenarios-
Does the feature affect old
indices, saved objects ? - Has the feature been tested with {kib}
diff --git a/docs/developer/contributing/development-accessibility-tests.asciidoc b/docs/developer/contributing/development-accessibility-tests.asciidoc
index a3ffefb94cd2a..facf7ff14a6c1 100644
--- a/docs/developer/contributing/development-accessibility-tests.asciidoc
+++ b/docs/developer/contributing/development-accessibility-tests.asciidoc
@@ -1,5 +1,5 @@
[[development-accessibility-tests]]
-==== Automated Accessibility Testing
+== Automated Accessibility Testing
To run the tests locally:
diff --git a/docs/developer/contributing/development-documentation.asciidoc b/docs/developer/contributing/development-documentation.asciidoc
index d9fae42eef87e..99e55963f57af 100644
--- a/docs/developer/contributing/development-documentation.asciidoc
+++ b/docs/developer/contributing/development-documentation.asciidoc
@@ -1,18 +1,18 @@
[[development-documentation]]
-=== Documentation during development
+== Documentation during development
Docs should be written during development and accompany PRs when relevant. There are multiple types of documentation, and different places to add each.
-[float]
-==== Developer services documentation
+[discrete]
+=== Developer services documentation
Documentation about specific services a plugin offers should be encapsulated in:
* README.asciidoc at the base of the plugin folder.
* Typescript comments for all public services.
-[float]
-==== End user documentation
+[discrete]
+=== End user documentation
Documentation about user facing features should be written in http://asciidoc.org/[asciidoc] at
{kib-repo}/tree/master/docs[https://github.com/elastic/kibana/tree/master/docs]
@@ -27,8 +27,8 @@ README for getting the docs tooling set up.
node scripts/docs.js --open
```
-[float]
-==== General developer documentation and guidelines
+[discrete]
+=== General developer documentation and guidelines
General developer guildlines and documentation, like this right here, should be written in http://asciidoc.org/[asciidoc]
at {kib-repo}/tree/master/docs/developer[https://github.com/elastic/kibana/tree/master/docs/developer]
diff --git a/docs/developer/contributing/development-functional-tests.asciidoc b/docs/developer/contributing/development-functional-tests.asciidoc
index 442fc1ac755d3..580a5a000f391 100644
--- a/docs/developer/contributing/development-functional-tests.asciidoc
+++ b/docs/developer/contributing/development-functional-tests.asciidoc
@@ -1,10 +1,10 @@
[[development-functional-tests]]
-=== Functional Testing
+== Functional Testing
We use functional tests to make sure the {kib} UI works as expected. It replaces hours of manual testing by automating user interaction. To have better control over our functional test environment, and to make it more accessible to plugin authors, {kib} uses a tool called the `FunctionalTestRunner`.
-[float]
-==== Running functional tests
+[discrete]
+=== Running functional tests
The `FunctionalTestRunner` is very bare bones and gets most of its functionality from its config file, located at {blob}test/functional/config.js[test/functional/config.js]. If you’re writing a plugin outside the {kib} repo, you will have your own config file.
See <> for more info.
@@ -12,27 +12,27 @@ The `FunctionalTestRunner` is very bare bones and gets most of its functionality
There are three ways to run the tests depending on your goals:
1. Easiest option:
-** Description: Starts up {kib} & Elasticsearch servers, followed by running tests. This is much slower when running the tests multiple times because slow startup time for the servers. Recommended for single-runs.
+** Description: Starts up {kib} & {es} servers, followed by running tests. This is much slower when running the tests multiple times because slow startup time for the servers. Recommended for single-runs.
** `node scripts/functional_tests`
-*** does everything in a single command, including running Elasticsearch and {kib} locally
+*** does everything in a single command, including running {es} and {kib} locally
*** tears down everything after the tests run
*** exit code reports success/failure of the tests
2. Best for development:
-** Description: Two commands, run in separate terminals, separate the components that are long-running and slow from those that are ephemeral and fast. Tests can be re-run much faster, and this still runs Elasticsearch & {kib} locally.
+** Description: Two commands, run in separate terminals, separate the components that are long-running and slow from those that are ephemeral and fast. Tests can be re-run much faster, and this still runs {es} & {kib} locally.
** `node scripts/functional_tests_server`
-*** starts Elasticsearch and {kib} servers
+*** starts {es} and {kib} servers
*** slow to start
*** can be reused for multiple executions of the tests, thereby saving some time when re-running tests
*** automatically restarts the {kib} server when relevant changes are detected
** `node scripts/functional_test_runner`
-*** runs the tests against {kib} & Elasticsearch servers that were started by `node scripts/functional_tests_server`
+*** runs the tests against {kib} & {es} servers that were started by `node scripts/functional_tests_server`
*** exit code reports success or failure of the tests
3. Custom option:
-** Description: Runs tests against instances of Elasticsearch & {kib} started some other way (like Elastic Cloud, or an instance you are managing in some other way).
+** Description: Runs tests against instances of {es} & {kib} started some other way (like Elastic Cloud, or an instance you are managing in some other way).
** just executes the functional tests
-** url, credentials, etc. for Elasticsearch and {kib} are specified via environment variables
+** url, credentials, etc. for {es} and {kib} are specified via environment variables
** Here's an example that runs against an Elastic Cloud instance. Note that you must run the same branch of tests as the version of {kib} you're testing.
+
["source","shell"]
@@ -91,15 +91,15 @@ export TEST_THROTTLE_NETWORK=1
node scripts/functional_test_runner --exclude-tag skipCloud
----------
-[float]
-===== More about `node scripts/functional_test_runner`
+[discrete]
+==== More about `node scripts/functional_test_runner`
When run without any arguments the `FunctionalTestRunner` automatically loads the configuration in the standard location, but you can override that behavior with the `--config` flag. List configs with multiple --config arguments.
-* `--config test/functional/config.js` starts Elasticsearch and {kib} servers with the WebDriver tests configured to run in Chrome.
-* `--config test/functional/config.firefox.js` starts Elasticsearch and {kib} servers with the WebDriver tests configured to run in Firefox.
-* `--config test/api_integration/config.js` starts Elasticsearch and {kib} servers with the api integration tests configuration.
-* `--config test/accessibility/config.ts` starts Elasticsearch and {kib} servers with the WebDriver tests configured to run an accessibility audit using https://www.deque.com/axe/[axe].
+* `--config test/functional/config.js` starts {es} and {kib} servers with the WebDriver tests configured to run in Chrome.
+* `--config test/functional/config.firefox.js` starts {es} and {kib} servers with the WebDriver tests configured to run in Firefox.
+* `--config test/api_integration/config.js` starts {es} and {kib} servers with the api integration tests configuration.
+* `--config test/accessibility/config.ts` starts {es} and {kib} servers with the WebDriver tests configured to run an accessibility audit using https://www.deque.com/axe/[axe].
There are also command line flags for `--bail` and `--grep`, which behave just like their mocha counterparts. For instance, use `--grep=foo` to run only tests that match a regular expression.
@@ -108,11 +108,11 @@ Logging can also be customized with `--quiet`, `--debug`, or `--verbose` flags.
Use the `--help` flag for more options.
-[float]
-==== Writing functional tests
+[discrete]
+=== Writing functional tests
-[float]
-===== Environment
+[discrete]
+==== Environment
The tests are written in https://mochajs.org[mocha] using https://github.com/elastic/kibana/tree/master/packages/kbn-expect[@kbn/expect] for assertions.
@@ -120,8 +120,8 @@ We use https://www.w3.org/TR/webdriver1/[WebDriver Protocol] to run tests in bot
The `FunctionalTestRunner` automatically transpiles functional tests using babel, so that tests can use the same ECMAScript features that {kib} source code uses. See {blob}style_guides/js_style_guide.md[style_guides/js_style_guide.md].
-[float]
-===== Definitions
+[discrete]
+==== Definitions
**Provider:**
@@ -179,8 +179,8 @@ To run tests on Firefox locally, use `config.firefox.js`:
node scripts/functional_test_runner --config test/functional/config.firefox.js
-----------
-[float]
-===== Using the test_user service
+[discrete]
+==== Using the test_user service
Tests should run at the positive security boundry condition, meaning that they should be run with the mimimum privileges required (and documented) and not as the superuser.
This prevents the type of regression where additional privleges accidentally become required to perform the same action.
@@ -198,8 +198,8 @@ Here we are setting the `test_user` to have the `kibana_user` role and also role
Tests should normally setRoles() in the before() and restoreDefaults() in the after().
-[float]
-===== Anatomy of a test file
+[discrete]
+==== Anatomy of a test file
This annotated example file shows the basic structure every test suite uses. It starts by importing https://github.com/elastic/kibana/tree/master/packages/kbn-expect[`@kbn/expect`] and defining its default export: an anonymous Test Provider. The test provider then destructures the Provider API for the `getService()` and `getPageObjects()` functions. It uses these functions to collect the dependencies of this suite. The rest of the test file will look pretty normal to mocha.js users. `describe()`, `it()`, `before()` and the lot are used to define suites that happen to automate a browser via services and objects of type `PageObject`.
@@ -222,7 +222,7 @@ export default function ({ getService, getPageObject }) {
describe('My Test Suite', () => {
// most suites start with a before hook that navigates to a specific
- // app/page and restores some archives into elasticsearch with esArchiver
+ // app/page and restores some archives into {es} with esArchiver
before(async () => {
await Promise.all([
// start with an empty .kibana index
@@ -235,7 +235,7 @@ export default function ({ getService, getPageObject }) {
});
// right after the before() hook definition, add the teardown steps
- // that will tidy up elasticsearch for other test suites
+ // that will tidy up {es} for other test suites
after(async () => {
// we unload the empty_kibana archive but not the makelogs
// archive because we don't make any changes to it, and subsequent
@@ -257,9 +257,9 @@ export default function ({ getService, getPageObject }) {
}
----
-[float]
+[discrete]
[[functional_test_runner_provider_api]]
-==== Provider API
+=== Provider API
The first and only argument to all providers is a Provider API Object. This object can be used to load service/page objects and config/test files.
@@ -280,11 +280,11 @@ Within a test Provider the API is exactly the same as the service providers API
[horizontal]
`loadTestFile(path)`::: Load the test file at path in place. Use this method to nest suites from other files into a higher-level suite
-[float]
-==== Service Index
+[discrete]
+=== Service Index
-[float]
-===== Built-in Services
+[discrete]
+==== Built-in Services
The `FunctionalTestRunner` comes with three built-in services:
@@ -304,8 +304,8 @@ The `FunctionalTestRunner` comes with three built-in services:
* Exposes lifecycle events for basic coordination. Handlers can return a promise and resolve/fail asynchronously
* Phases include: `beforeLoadTests`, `beforeTests`, `beforeEachTest`, `cleanup`
-[float]
-===== {kib} Services
+[discrete]
+==== {kib} Services
The {kib} functional tests define the vast majority of the actual functionality used by tests.
@@ -377,7 +377,7 @@ Full list of services that are used in functional tests can be found here: {blob
**Low-level utilities:**:::
* es
** Source: {blob}test/common/services/es.ts[test/common/services/es.ts]
-** Elasticsearch client
+** {es} client
** Higher level options: `kibanaServer.uiSettings` or `esArchiver`
* remote
** Source: {blob}test/functional/services/remote/remote.ts[test/functional/services/remote/remote.ts]
@@ -387,8 +387,8 @@ Full list of services that are used in functional tests can be found here: {blob
** For searching and manipulating with DOM elements, use `testSubjects` and `find` services
** See the https://seleniumhq.github.io/selenium/docs/api/javascript/[selenium-webdriver docs] for the full API.
-[float]
-===== Custom Services
+[discrete]
+==== Custom Services
Services are intentionally generic. They can be literally anything (even nothing). Some services have helpers for interacting with a specific types of UI elements, like `pointSeriesVis`, and others are more foundational, like `log` or `config`. Whenever you want to provide some functionality in a reusable package, consider making a custom service.
@@ -427,8 +427,8 @@ export default function () {
}
-----------
-[float]
-==== PageObjects
+[discrete]
+=== PageObjects
The purpose for each PageObject is pretty self-explanatory. The visualize PageObject provides helpers for interacting with the visualize app, dashboard is the same for the dashboard app, and so on.
@@ -436,13 +436,13 @@ One exception is the "common" PageObject. A holdover from the intern implementat
Please add new methods to existing or new services rather than further expanding the CommonPage class.
-[float]
-==== Gotchas
+[discrete]
+=== Gotchas
Remember that you can’t run an individual test in the file (`it` block) because the whole `describe` needs to be run in order. There should only be one top level `describe` in a file.
-[float]
-===== Functional Test Timing
+[discrete]
+==== Functional Test Timing
Another important gotcha is writing stable tests by being mindful of timing. All methods on `remote` run asynchronously. It’s better to write interactions that wait for changes on the UI to appear before moving onto the next step.
@@ -480,8 +480,8 @@ class AppPage {
Writing in this way will ensure your test timings are not flaky or based on assumptions about UI updates after interactions.
-[float]
-==== Debugging
+[discrete]
+=== Debugging
From the command line run:
@@ -503,8 +503,8 @@ const log = getService(‘log’);
log.debug(‘done clicking menu’);
-----------
-[float]
-==== MacOS testing performance tip
+[discrete]
+=== MacOS testing performance tip
macOS users on a machine with a discrete graphics card may see significant speedups (up to 2x) when running tests by changing your terminal emulator's GPU settings. In iTerm2:
* Open Preferences (Command + ,)
diff --git a/docs/developer/contributing/development-github.asciidoc b/docs/developer/contributing/development-github.asciidoc
index 027b4e73aa9de..a6d4e29940487 100644
--- a/docs/developer/contributing/development-github.asciidoc
+++ b/docs/developer/contributing/development-github.asciidoc
@@ -1,16 +1,16 @@
[[development-github]]
-=== How we use git and github
+== How we use git and github
-[float]
-==== Forking
+[discrete]
+=== Forking
We follow the https://help.github.com/articles/fork-a-repo/[GitHub
forking model] for collaborating on {kib} code. This model assumes that
you have a remote called `upstream` which points to the official {kib}
repo, which we'll refer to in later code snippets.
-[float]
-==== Branching
+[discrete]
+=== Branching
* All work on the next major release goes into master.
* Past major release branches are named `{majorVersion}.x`. They contain
@@ -24,8 +24,8 @@ if the next patch release is `5.3.1`, work for it should go into the
branches.
* Where appropriate, we'll backport changes into older release branches.
-[float]
-==== Commits and Merging
+[discrete]
+=== Commits and Merging
* Feel free to make as many commits as you want, while working on a
branch.
@@ -38,8 +38,8 @@ explanation of _why_ you made the changes that you did.
feature branch, and force-pushing (see below for instructions).
* When merging, we'll squash your commits into a single commit.
-[float]
-===== Rebasing and fixing merge conflicts
+[discrete]
+==== Rebasing and fixing merge conflicts
Rebasing can be tricky, and fixing merge conflicts can be even trickier
because it involves force pushing. This is all compounded by the fact
@@ -106,7 +106,7 @@ hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Assuming you've successfully rebased and you're happy with the code, you should force push instead.
-[float]
-==== Creating a pull request
+[discrete]
+=== Creating a pull request
See <> for the next steps on getting your code changes merged into {kib}.
\ No newline at end of file
diff --git a/docs/developer/contributing/development-pull-request.asciidoc b/docs/developer/contributing/development-pull-request.asciidoc
index 5d3c30fec7383..070eff449af5b 100644
--- a/docs/developer/contributing/development-pull-request.asciidoc
+++ b/docs/developer/contributing/development-pull-request.asciidoc
@@ -1,16 +1,16 @@
[[development-pull-request]]
-=== Submitting a pull request
+== Submitting a pull request
-[float]
-==== What Goes Into a Pull Request
+[discrete]
+=== What Goes Into a Pull Request
* Please include an explanation of your changes in your PR description.
* Links to relevant issues, external resources, or related PRs are very important and useful.
* Please update any tests that pertain to your code, and add new tests where appropriate.
* Update or add docs when appropriate. Read more about <>.
-[float]
-==== Submitting a Pull Request
+[discrete]
+=== Submitting a Pull Request
1. Push your local changes to your forked copy of the repository and submit a pull request.
2. Describe what your changes do and mention the number of the issue where discussion has taken place, e.g., “Closes #123″.
@@ -22,8 +22,8 @@ Always submit your pull against master unless the bug is only present in an olde
Then sit back and wait. There will probably be discussion about the Pull Request and, if any changes are needed, we'll work with you to get your Pull Request merged into {kib}.
-[float]
-==== What to expect during the pull request review process
+[discrete]
+=== What to expect during the pull request review process
Most PRs go through several iterations of feedback and updates. Depending on the scope and complexity of the PR, the process can take weeks. Please
be patient and understand we hold our code base to a high standard.
diff --git a/docs/developer/contributing/development-tests.asciidoc b/docs/developer/contributing/development-tests.asciidoc
index b470ea61669b2..78a2a90b69ce5 100644
--- a/docs/developer/contributing/development-tests.asciidoc
+++ b/docs/developer/contributing/development-tests.asciidoc
@@ -1,10 +1,10 @@
[[development-tests]]
-=== Testing
+== Testing
To ensure that your changes will not break other functionality, please run the test suite and build (<>) before submitting your Pull Request.
-[float]
-==== Running specific {kib} tests
+[discrete]
+=== Running specific {kib} tests
The following table outlines possible test file locations and how to
invoke them:
@@ -47,8 +47,8 @@ Examples: - Run the entire elasticsearch_service test suite:
string: ``` yarn test:ftr:server –config test/api_integration/config.js
yarn test:ftr:runner –config test/api_integration/config
-[float]
-==== Cross-browser compatibility
+[discrete]
+=== Cross-browser compatibility
**Testing IE on OS X**
@@ -71,8 +71,8 @@ your computer name).
`http://computer.local:5601` to test {kib}.
* Alternatively you can use browserstack
-[float]
-==== Running browser automation tests
+[discrete]
+=== Running browser automation tests
Check out <> to learn more about how you can run
and develop functional tests for {kib} core and plugins.
@@ -80,17 +80,17 @@ and develop functional tests for {kib} core and plugins.
You can also look into the {kib-repo}tree/{branch}/scripts/README.md[Scripts README.md]
to learn more about using the node scripts we provide for building
{kib}, running integration tests, and starting up {kib} and
-Elasticsearch while you develop.
+{es} while you develop.
-[float]
+[discrete]
==== More testing information:
* <>
* <>
* <>
-include::development-functional-tests.asciidoc[]
+include::development-functional-tests.asciidoc[leveloffset=+1]
-include::development-unit-tests.asciidoc[]
+include::development-unit-tests.asciidoc[leveloffset=+1]
-include::development-accessibility-tests.asciidoc[]
\ No newline at end of file
+include::development-accessibility-tests.asciidoc[leveloffset=+1]
\ No newline at end of file
diff --git a/docs/developer/contributing/development-unit-tests.asciidoc b/docs/developer/contributing/development-unit-tests.asciidoc
index 0009533c9a7c4..8b4954150bb5b 100644
--- a/docs/developer/contributing/development-unit-tests.asciidoc
+++ b/docs/developer/contributing/development-unit-tests.asciidoc
@@ -1,11 +1,11 @@
[[development-unit-tests]]
-==== Unit testing frameworks
+== Unit testing frameworks
{kib} is migrating unit testing from `Mocha` to `Jest`. Legacy unit tests
still exist in Mocha but all new unit tests should be written in Jest.
-[float]
-===== Mocha (legacy)
+[discrete]
+=== Mocha (legacy)
Mocha tests are contained in `__tests__` directories.
@@ -16,8 +16,8 @@ Mocha tests are contained in `__tests__` directories.
yarn test:mocha
-----------
-[float]
-==== Jest
+[discrete]
+== Jest
Jest tests are stored in the same directory as source code files with the `.test.{js,mjs,ts,tsx}` suffix.
*Running Jest Unit Tests*
@@ -27,8 +27,8 @@ Jest tests are stored in the same directory as source code files with the `.test
yarn test:jest
-----------
-[float]
-====== Writing Jest Unit Tests
+[discrete]
+==== Writing Jest Unit Tests
In order to write those tests there are two main things you need to be aware of.
The first one is the different between `jest.mock` and `jest.doMock`
@@ -37,8 +37,8 @@ test files with `babel-jest` both techniques are needed
specially for the tests implemented on Typescript in order to benefit from the
auto-inference types feature.
-[float]
-====== Jest.mock vs Jest.doMock
+[discrete]
+==== Jest.mock vs Jest.doMock
Both methods are essentially the same on their roots however the `jest.mock`
calls will get hoisted to the top of the file and can only reference variables
@@ -47,8 +47,8 @@ reference pretty much any variable we want, however we have to assure those refe
variables are instantiated at the time we need them which lead us to the next
section where we'll talk about our jest mock files pattern.
-[float]
-====== Jest Mock Files Pattern
+[discrete]
+==== Jest Mock Files Pattern
Specially on typescript it is pretty common to have in unit tests
`jest.doMock` calls which reference for example imported types. Any error
@@ -76,9 +76,9 @@ like: `import * as Mocks from './mymodule.test.mocks'`,
or just `import './mymodule.test.mocks'` if there isn't anything
exported to be used.
-[float]
+[discrete]
[[debugging-unit-tests]]
-===== Debugging Unit Tests
+=== Debugging Unit Tests
The standard `yarn test` task runs several sub tasks and can take
several minutes to complete, making debugging failures pretty painful.
@@ -127,8 +127,8 @@ description.
image:http://i.imgur.com/DwHxgfq.png[Browser test debugging]
-[float]
-===== Unit Testing Plugins
+[discrete]
+=== Unit Testing Plugins
This should work super if you’re using the
https://github.com/elastic/kibana/tree/master/packages/kbn-plugin-generator[Kibana
diff --git a/docs/developer/contributing/index.asciidoc b/docs/developer/contributing/index.asciidoc
index 4f987f31cf1f6..99ab83bc2f073 100644
--- a/docs/developer/contributing/index.asciidoc
+++ b/docs/developer/contributing/index.asciidoc
@@ -23,7 +23,7 @@ Read <> to get your environment up and running, the
Please make sure you have signed the [Contributor License Agreement](http://www.elastic.co/contributor-agreement/). We are not asking you to assign copyright to us, but to give us the right to distribute your code without restriction. We ask this of all contributors in order to assure our users of the origin and continuing existence of the code. You only need to sign the CLA once.
-[float]
+[discrete]
[[kibana-localization]]
=== Localization
@@ -32,7 +32,7 @@ Read <> for details on our localization prac
Note that we cannot support accepting contributions to the translations from any source other than the translators we have engaged to do the work.
We are still to develop a proper process to accept any contributed translations. We certainly appreciate that people care enough about the localization effort to want to help improve the quality. We aim to build out a more comprehensive localization process for the future and will notify you once contributions can be supported, but for the time being, we are not able to incorporate suggestions.
-[float]
+[discrete]
[[kibana-release-notes-process]]
=== Release Notes Process
@@ -43,7 +43,7 @@ access to GitHub labels.
The Release Notes summarize what the PRs accomplish in language that is meaningful to users.
To generate the Release Notes, the team runs a script against this repo to collect the merged PRs against the release.
-[float]
+[discrete]
==== Create the Release Notes text
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.
@@ -59,7 +59,7 @@ When you create the Release Notes text, use the following best practices:
* When you create a bug fix PR, start with `Fixes`.
* When you create a deprecation PR, start with `Deprecates`.
-[float]
+[discrete]
==== Add your labels
[arabic]
@@ -72,18 +72,18 @@ When you create the Release Notes text, use the following best practices:
* To **NOT** include your changes in the Release Notes, use `release_note:skip`.
-include::development-github.asciidoc[]
+include::development-github.asciidoc[leveloffset=+1]
-include::development-tests.asciidoc[]
+include::development-tests.asciidoc[leveloffset=+1]
-include::interpreting-ci-failures.asciidoc[]
+include::interpreting-ci-failures.asciidoc[leveloffset=+1]
-include::development-documentation.asciidoc[]
+include::development-documentation.asciidoc[leveloffset=+1]
-include::development-pull-request.asciidoc[]
+include::development-pull-request.asciidoc[leveloffset=+1]
-include::kibana-issue-reporting.asciidoc[]
+include::kibana-issue-reporting.asciidoc[leveloffset=+1]
-include::pr-review.asciidoc[]
+include::pr-review.asciidoc[leveloffset=+1]
-include::linting.asciidoc[]
+include::linting.asciidoc[leveloffset=+1]
diff --git a/docs/developer/contributing/interpreting-ci-failures.asciidoc b/docs/developer/contributing/interpreting-ci-failures.asciidoc
index ba3999a310198..bb623bc7a541c 100644
--- a/docs/developer/contributing/interpreting-ci-failures.asciidoc
+++ b/docs/developer/contributing/interpreting-ci-failures.asciidoc
@@ -1,19 +1,19 @@
[[interpreting-ci-failures]]
-=== Interpreting CI Failures
+== Interpreting CI Failures
{kib} CI uses a Jenkins feature called "Pipelines" to automate testing of the code in pull requests and on tracked branches. Pipelines are defined within the repository via the `Jenkinsfile` at the root of the project.
More information about Jenkins Pipelines can be found link:https://jenkins.io/doc/book/pipeline/[in the Jenkins book].
-[float]
-==== Github Checks
+[discrete]
+=== Github Checks
When a test fails it will be reported to Github via Github Checks. We currently bucket tests into several categories which run in parallel to make CI faster. Groups like `ciGroup{X}` get a single check in Github, and other tests like linting, or type checks, get their own checks.
Clicking the link next to the check in the conversation tab of a pull request will take you to the log output from that section of the tests. If that log output is truncated, or doesn't clearly identify what happened, you can usually get more complete information by visiting Jenkins directly.
-[float]
-==== Viewing Job Executions in Jenkins
+[discrete]
+=== Viewing Job Executions in Jenkins
To view the results of a job execution in Jenkins, either click the link in the comment left by `@elasticmachine` or search for the `kibana-ci` check in the list at the bottom of the PR. This link will take you to the top-level page for the specific job execution that failed.
@@ -24,8 +24,8 @@ image::images/job_view.png[]
3. *Google Cloud Storage (GCS) Upload Report:* Link to the screen which lists out the artifacts uploaded to GCS during this job execution.
4. *Pipeline Steps:*: A breakdown of the pipline that was executed, along with individual log output for each step in the pipeline.
-[float]
-==== Viewing ciGroup/test Logs
+[discrete]
+=== Viewing ciGroup/test Logs
To view the logs for a failed specific ciGroup, jest, mocha, type checkers, linters, etc., click on the *Pipeline Steps* link in from the Job page.
diff --git a/docs/developer/contributing/kibana-issue-reporting.asciidoc b/docs/developer/contributing/kibana-issue-reporting.asciidoc
index 36c50b612d675..63366ae2aa6bb 100644
--- a/docs/developer/contributing/kibana-issue-reporting.asciidoc
+++ b/docs/developer/contributing/kibana-issue-reporting.asciidoc
@@ -1,8 +1,8 @@
[[kibana-issue-reporting]]
-=== Effective issue reporting in {kib}
+== Effective issue reporting in {kib}
-[float]
-==== Voicing the importance of an issue
+[discrete]
+=== Voicing the importance of an issue
We seriously appreciate thoughtful comments. If an issue is important to
you, add a comment with a solid write up of your use case and explain
@@ -17,8 +17,8 @@ https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments[
thumbs up reaction] on the issue itself and on the comment which best
summarizes your thoughts.
-[float]
-==== "`My issue isn’t getting enough attention`"
+[discrete]
+=== "`My issue isn’t getting enough attention`"
First of all, *sorry about that!* We want you to have a great time with
{kib}.
@@ -31,8 +31,8 @@ more pressing issues.
Feel free to bump your issues if you think they’ve been neglected for a
prolonged period.
-[float]
-==== "`I want to help!`"
+[discrete]
+=== "`I want to help!`"
*Now we’re talking*. If you have a bug fix or new feature that you would
like to contribute to {kib}, please *find or open an issue about it
diff --git a/docs/developer/contributing/linting.asciidoc b/docs/developer/contributing/linting.asciidoc
index 234bd90478907..0d05afa504538 100644
--- a/docs/developer/contributing/linting.asciidoc
+++ b/docs/developer/contributing/linting.asciidoc
@@ -1,5 +1,5 @@
[[kibana-linting]]
-=== Linting
+== Linting
A note about linting: We use http://eslint.org[eslint] to check that the
link:STYLEGUIDE.md[styleguide] is being followed. It runs in a
@@ -34,8 +34,8 @@ for your editor, and browse our
https://github.com/elastic/kibana/blob/master/.editorconfig[`.editorconfig`]
file to see what config rules we set up.
-[float]
-==== Setup Guide for VS Code Users
+[discrete]
+== Setup Guide for VS Code Users
Note that for VSCode, to enable "`live`" linting of TypeScript (and
other) file types, you will need to modify your local settings, as shown
diff --git a/docs/developer/contributing/pr-review.asciidoc b/docs/developer/contributing/pr-review.asciidoc
index ebab3b24aaaee..885725795b0b9 100644
--- a/docs/developer/contributing/pr-review.asciidoc
+++ b/docs/developer/contributing/pr-review.asciidoc
@@ -1,5 +1,5 @@
[[pr-review]]
-=== Pull request review guidelines
+== Pull request review guidelines
Every change made to {kib} must be held to a high standard, and while the responsibility for quality in a pull request ultimately lies with the author, {kib} team members have the responsibility as reviewers to verify during their review process.
@@ -10,24 +10,24 @@ It is not expected nor intended for a PR review to take the shape of this docume
While the review process is always done by Elastic staff members, these guidelines apply to all pull requests regardless of whether they are authored by community members or Elastic staff.
-[float]
-==== Target audience
+[discrete]
+=== Target audience
The target audience for this document are pull request reviewers. For {kib} maintainers, the PR review is the only part of the contributing process in which we have complete control. The author of any given pull request may not be up to speed on the latest expectations we have for pull requests, and they may have never read our guidelines at all. It's our responsibility as reviewers to guide folks through this process, but it's hard to do that consistently without a common set of documented principles.
Pull request authors can benefit from reading this document as well because it'll help establish a common set of expectations between authors and reviewers early.
-[float]
-==== Reject fast
+[discrete]
+=== Reject fast
Every pull request is different, and before reviewing any given PR, reviewers should consider the optimal way to approach the PR review so that if the change is ultimately rejected, it is done so as early in the process as possible.
For example, a reviewer may want to do a product level review as early as possible for a PR that includes a new UI feature. On the other hand, perhaps the author is submitting a new feature that has been rejected in the past due to key architectural decisions, in which case it may be appropriate for the reviewer to focus on the soundness of the architecture before diving into anything else.
-[float]
-==== The big three
+[discrete]
+=== The big three
There are a lot of discrete requirements and guidelines we want to follow in all of our pull requests, but three things in particular stand out as important above all the rest.
@@ -58,20 +58,20 @@ This isn't simply a question of enough test files. The code in the tests themsel
All of our code should have unit tests that verify its behaviors, including not only the "happy path", but also edge cases, error handling, etc. When you change an existing API of a module, then there should always be at least one failing unit test, which in turn means we need to verify that all code consuming that API properly handles the change if necessary. For modules at a high enough level, this will mean we have breaking change in the product, which we'll need to handle accordingly.
-In addition to extensive unit test coverage, PRs should include relevant functional and integration tests. In some cases, we may simply be testing a programmatic interface (e.g. a service) that is integrating with the file system, the network, Elasticsearch, etc. In other cases, we'll be testing REST APIs over HTTP or comparing screenshots/snapshots with prior known acceptable state. In the worst case, we are doing browser-based functional testing on a running instance of {kib} using selenium.
+In addition to extensive unit test coverage, PRs should include relevant functional and integration tests. In some cases, we may simply be testing a programmatic interface (e.g. a service) that is integrating with the file system, the network, {es}, etc. In other cases, we'll be testing REST APIs over HTTP or comparing screenshots/snapshots with prior known acceptable state. In the worst case, we are doing browser-based functional testing on a running instance of {kib} using selenium.
Enhancements are pretty much always going to have extensive unit tests as a base as well as functional and integration testing. Bug fixes should always include regression tests to ensure that same bug does not manifest again in the future.
--
-[float]
-==== Product level review
+[discrete]
+=== Product level review
Reviewers are not simply evaluating the code itself, they are also evaluating the quality of the user-facing change in the product. This generally means they need to check out the branch locally and "play around" with it. In addition to the "do we want this change in the product" details, the reviewer should be looking for bugs and evaluating how approachable and useful the feature is as implemented. Special attention should be given to error scenarios and edge cases to ensure they are all handled well within the product.
-[float]
-==== Consistency, style, readability
+[discrete]
+=== Consistency, style, readability
Having a relatively consistent codebase is an important part of us building a sustainable project. With dozens of active contributors at any given time, we rely on automation to help ensure consistency - we enforce a comprehensive set of linting rules through CI. We're also rolling out prettier to make this even more automatic.
@@ -86,8 +86,8 @@ When in doubt, relying on "prior art" in the codebase, especially in and around
There may also be times when a person is inspired by a particular contribution to introduce a new way to style code that we already have different style guidelines or "prior art" for. It's OK to bring this up in a pull request, but ultimately that discussion should branch off into a separate issue or pull request to update the appropriate guide. If this change is prompted by a reviewer, then the original PR should not be blocked on this. If the change is prompted by the author, then they can either update the PR to be consistent with our existing guidelines (preferred) or they can choose to block the PR entirely on that separate styleguide discussion.
-[float]
-==== Nitpicking
+[discrete]
+=== Nitpicking
Nitpicking is when a reviewer identifies trivial and unimportant details in a pull request and asks the author to change them. This is a completely subjective category that is impossible to define universally, and it's equally impractical to define a blanket policy on nitpicking that everyone will be happy with.
@@ -96,14 +96,14 @@ Reviewers should feel comfortable giving any feedback they have on a pull reques
Often, reviewers have an opinion about whether the feedback they are about to give is a nitpick or not. While not required, it can be really helpful to identify that feedback as such, for example "nit: a newline after this would be helpful". This helps the author understand your intention.
-[float]
-==== Handling disagreements
+[discrete]
+=== Handling disagreements
Conflicting opinions between reviewers and authors happen, and sometimes it is hard to reconcile those opinions. Ideally folks can work together in the spirit of these guidelines toward a consensus, but if that doesn't work out it may be best to bring a third person into the discussion. Our pull requests generally have two reviewers, so an appropriate third person may already be obvious. Otherwise, reach out to the functional area that is most appropriate or to technical leadership if an area isn't obvious.
-[float]
-==== Inappropriate review feedback
+[discrete]
+=== Inappropriate review feedback
Whether or not a bit of feedback is appropriate for a pull request is often dependent on the motivation for giving the feedback in the first place.
@@ -112,8 +112,8 @@ _Demanding_ an author make changes based primarily on the mindset of "how would
Inflammatory feedback such as "this is crap" isn't feedback at all. It's both mean and unhelpful, and it is never appropriate.
-[float]
-==== A checklist
+[discrete]
+=== A checklist
Establishing a comprehensive checklist for all of the things that should happen in all possible pull requests is impractical, but that doesn't mean we lack a concrete set of minimum requirements that we can enumerate. The following items should be double checked for any pull request:
diff --git a/docs/developer/getting-started/building-kibana.asciidoc b/docs/developer/getting-started/building-kibana.asciidoc
index e1f1ca336a5da..72054b1628fc2 100644
--- a/docs/developer/getting-started/building-kibana.asciidoc
+++ b/docs/developer/getting-started/building-kibana.asciidoc
@@ -1,5 +1,5 @@
[[building-kibana]]
-=== Building a {kib} distributable
+== Building a {kib} distributable
The following commands will build a {kib} production distributable.
@@ -15,8 +15,8 @@ You can get all build options using the following command:
yarn build --help
----
-[float]
-==== Building OS packages
+[discrete]
+=== Building OS packages
Packages are built using fpm, dpkg, and rpm. Package building has only been tested on Linux and is not supported on any other platform.
diff --git a/docs/developer/getting-started/debugging.asciidoc b/docs/developer/getting-started/debugging.asciidoc
index b369dcda748af..a3fb12ec1f6a3 100644
--- a/docs/developer/getting-started/debugging.asciidoc
+++ b/docs/developer/getting-started/debugging.asciidoc
@@ -1,15 +1,15 @@
[[kibana-debugging]]
-=== Debugging {kib}
+== Debugging {kib}
For information about how to debug unit tests, refer to <>.
-[float]
-==== Server Code
+[discrete]
+=== Server Code
`yarn debug` will start the server with Node's inspect flag. {kib}'s development mode will start three processes on ports `9229`, `9230`, and `9231`. Chrome's developer tools need to be configured to connect to all three connections. Add `localhost:` for each {kib} process in Chrome's developer tools connection tab.
-[float]
-==== Instrumenting with Elastic APM
+[discrete]
+=== Instrumenting with Elastic APM
{kib} ships with the
https://github.com/elastic/apm-agent-nodejs[Elastic APM Node.js Agent]
@@ -18,7 +18,7 @@ built-in for debugging purposes.
Its default configuration is meant to be used by core {kib} developers
only, but it can easily be re-configured to your needs. In its default
configuration it’s disabled and will, once enabled, send APM data to a
-centrally managed Elasticsearch cluster accessible only to Elastic
+centrally managed {es} cluster accessible only to Elastic
employees.
To change the location where data is sent, use the
diff --git a/docs/developer/getting-started/development-plugin-resources.asciidoc b/docs/developer/getting-started/development-plugin-resources.asciidoc
index dfe8efc4fef57..8f81138b81ed7 100644
--- a/docs/developer/getting-started/development-plugin-resources.asciidoc
+++ b/docs/developer/getting-started/development-plugin-resources.asciidoc
@@ -1,14 +1,14 @@
[[development-plugin-resources]]
-=== Plugin Resources
+== Plugin Resources
Here are some resources that are helpful for getting started with plugin development.
-[float]
-==== Some light reading
+[discrete]
+=== Some light reading
If you haven't already, start with <>. If you are planning to add your plugin to the {kib} repo, read the <> guide, if you are building a plugin externally, read <>. In both cases, read up on our recommended <>.
-[float]
-==== Creating an empty plugin
+[discrete]
+=== Creating an empty plugin
You can use the <> to get a basic structure for a new plugin. Plugins that are not part of the
{kib} repo should be developed inside the `plugins` folder. If you are building a new plugin to check in to the {kib} repo,
@@ -18,15 +18,15 @@ you will choose between a few locations:
- {kib-repo}tree/{branch}/src/plugins[src/plugins] for open source licensed plugins
- {kib-repo}tree/{branch}/examples[examples] for developer example plugins (these will not be included in the distributables)
-[float]
-==== Elastic UI Framework
+[discrete]
+=== Elastic UI Framework
If you're developing a plugin that has a user interface, take a look at our https://elastic.github.io/eui[Elastic UI Framework].
It documents the CSS and React components we use to build {kib}'s user interface.
You're welcome to use these components, but be aware that they are rapidly evolving, and we might introduce breaking changes that will disrupt your plugin's UI.
-[float]
-==== TypeScript Support
+[discrete]
+=== TypeScript Support
We recommend your plugin code is written in http://www.typescriptlang.org/[TypeScript].
To enable TypeScript support, create a `tsconfig.json` file at the root of your plugin that looks something like this:
@@ -48,14 +48,14 @@ TypeScript code is automatically converted into JavaScript during development,
but not in the distributable version of {kib}. If you use the
{kib-repo}blob/{branch}/packages/kbn-plugin-helpers[@kbn/plugin-helpers] to build your plugin, then your `.ts` and `.tsx` files will be permanently transpiled before your plugin is archived. If you have your own build process, make sure to run the TypeScript compiler on your source files and ship the compilation output so that your plugin will work with the distributable version of {kib}.
-[float]
-==== {kib} platform migration guide
+[discrete]
+=== {kib} platform migration guide
{kib-repo}blob/{branch}/src/core/MIGRATION.md#migrating-legacy-plugins-to-the-new-platform[This guide]
provides an action plan for moving a legacy plugin to the new platform.
-[float]
-==== Externally developed plugins
+[discrete]
+=== Externally developed plugins
If you are building a plugin outside of the {kib} repo, read <>.
diff --git a/docs/developer/getting-started/index.asciidoc b/docs/developer/getting-started/index.asciidoc
index 47c4a52daf303..2ac51b6cf86f8 100644
--- a/docs/developer/getting-started/index.asciidoc
+++ b/docs/developer/getting-started/index.asciidoc
@@ -3,7 +3,7 @@
Get started building your own plugins, or contributing directly to the {kib} repo.
-[float]
+[discrete]
[[get-kibana-code]]
=== Get the code
@@ -15,7 +15,7 @@ git clone https://github.com/[YOUR_USERNAME]/kibana.git kibana
cd kibana
----
-[float]
+[discrete]
=== Install dependencies
Install the version of Node.js listed in the `.node-version` file. This
@@ -67,11 +67,11 @@ corrupted packages in your yarn cache which you can clean with:
yarn cache clean
----
-[float]
+[discrete]
=== Configure environmental settings
[[increase-nodejs-heap-size]]
-[float]
+[discrete]
==== Increase node.js heap size
{kib} is a big project and for some commands it can happen that the
@@ -81,10 +81,10 @@ by setting the `--max_old_space_size` option on the command line. To set
the limit for all commands, simply add the following line to your shell
config: `export NODE_OPTIONS="--max_old_space_size=2048"`.
-[float]
-=== Run Elasticsearch
+[discrete]
+=== Run {es}
-Run the latest Elasticsearch snapshot. Specify an optional license with the `--license` flag.
+Run the latest {es} snapshot. Specify an optional license with the `--license` flag.
[source,bash]
----
@@ -96,7 +96,7 @@ yarn es snapshot --license trial
Read about more options for <>, like connecting to a remote host, running from source,
preserving data inbetween runs, running remote cluster, etc.
-[float]
+[discrete]
=== Run {kib}
In another terminal window, start up {kib}. Include developer examples by adding an optional `--run-examples` flag.
@@ -110,13 +110,13 @@ View all available options by running `yarn start --help`
Read about more advanced options for <>.
-[float]
+[discrete]
=== Code away!
You are now ready to start developing. Changes to your files should be picked up automatically. Server side changes will
cause the {kib} server to reboot.
-[float]
+[discrete]
=== More information
* <>
@@ -129,12 +129,12 @@ cause the {kib} server to reboot.
* <>
-include::running-kibana-advanced.asciidoc[]
+include::running-kibana-advanced.asciidoc[leveloffset=+1]
-include::sample-data.asciidoc[]
+include::sample-data.asciidoc[leveloffset=+1]
-include::debugging.asciidoc[]
+include::debugging.asciidoc[leveloffset=+1]
-include::building-kibana.asciidoc[]
+include::building-kibana.asciidoc[leveloffset=+1]
-include::development-plugin-resources.asciidoc[]
\ No newline at end of file
+include::development-plugin-resources.asciidoc[leveloffset=+1]
\ No newline at end of file
diff --git a/docs/developer/getting-started/running-kibana-advanced.asciidoc b/docs/developer/getting-started/running-kibana-advanced.asciidoc
index e36f38de1b366..c3b7847b0f8ba 100644
--- a/docs/developer/getting-started/running-kibana-advanced.asciidoc
+++ b/docs/developer/getting-started/running-kibana-advanced.asciidoc
@@ -1,5 +1,5 @@
[[running-kibana-advanced]]
-=== Running {kib}
+== Running {kib}
Change to your local {kib} directory. Start the development server.
@@ -23,8 +23,8 @@ By default, you can log in with username `elastic` and password
`changeme`. See the `--help` options on `yarn es ` if
you’d like to configure a different password.
-[float]
-==== Running {kib} in Open-Source mode
+[discrete]
+=== Running {kib} in Open-Source mode
If you’re looking to only work with the open-source software, supply the
license type to `yarn es`:
@@ -41,8 +41,8 @@ And start {kib} with only open-source code:
yarn start --oss
----
-[float]
-==== Unsupported URL Type
+[discrete]
+=== Unsupported URL Type
If you’re installing dependencies and seeing an error that looks
something like
@@ -56,9 +56,9 @@ need to run `yarn kbn bootstrap`. For more info, see
link:#setting-up-your-development-environment[Setting Up Your
Development Environment] above.
-[float]
+[discrete]
[[customize-kibana-yml]]
-==== Customizing `config/kibana.dev.yml`
+=== Customizing `config/kibana.dev.yml`
The `config/kibana.yml` file stores user configuration directives.
Since this file is checked into source control, however, developer
@@ -70,8 +70,8 @@ non-dev version and accepts any of the
https://www.elastic.co/guide/en/kibana/current/settings.html[standard
settings].
-[float]
-==== Potential Optimization Pitfalls
+[discrete]
+=== Potential Optimization Pitfalls
* Webpack is trying to include a file in the bundle that I deleted and
is now complaining about it is missing
@@ -79,9 +79,9 @@ is now complaining about it is missing
directory, but webpack isn’t adapting
* (if you discover other scenarios, please send a PR!)
-[float]
-==== Setting Up SSL
+[discrete]
+=== Setting Up SSL
{kib} includes self-signed certificates that can be used for
development purposes in the browser and for communicating with
-Elasticsearch: `yarn start --ssl` & `yarn es snapshot --ssl`.
\ No newline at end of file
+{es}: `yarn start --ssl` & `yarn es snapshot --ssl`.
\ No newline at end of file
diff --git a/docs/developer/getting-started/sample-data.asciidoc b/docs/developer/getting-started/sample-data.asciidoc
index 376211ceb2634..0d313cbabe64e 100644
--- a/docs/developer/getting-started/sample-data.asciidoc
+++ b/docs/developer/getting-started/sample-data.asciidoc
@@ -1,17 +1,17 @@
[[sample-data]]
-=== Installing sample data
+== Installing sample data
-There are a couple ways to easily get data ingested into Elasticsearch.
+There are a couple ways to easily get data ingested into {es}.
-[float]
-==== Sample data packages available for one click installation
+[discrete]
+=== Sample data packages available for one click installation
The easiest is to install one or more of our vailable sample data packages. If you have no data, you should be
prompted to install when running {kib} for the first time. You can also access and install the sample data packages
by going to the home page and clicking "add sample data".
-[float]
-==== makelogs script
+[discrete]
+=== makelogs script
The provided `makelogs` script will generate sample data.
@@ -22,10 +22,10 @@ node scripts/makelogs --auth :
The default username and password combination are `elastic:changeme`
-Make sure to execute `node scripts/makelogs` *after* elasticsearch is up and running!
+Make sure to execute `node scripts/makelogs` *after* {es} is up and running!
-[float]
-==== CSV upload
+[discrete]
+=== CSV upload
If running with a platinum or trial license, you can also use the CSV uploader provided inside the Machine learning app.
Navigate to the Data visualizer to upload your data from a file.
\ No newline at end of file
diff --git a/docs/developer/plugin/external-plugin-functional-tests.asciidoc b/docs/developer/plugin/external-plugin-functional-tests.asciidoc
index 44f636d627011..706bf6af8ed9b 100644
--- a/docs/developer/plugin/external-plugin-functional-tests.asciidoc
+++ b/docs/developer/plugin/external-plugin-functional-tests.asciidoc
@@ -1,10 +1,10 @@
[[external-plugin-functional-tests]]
-=== Functional Tests for Plugins outside the {kib} repo
+== Functional Tests for Plugins outside the {kib} repo
Plugins use the `FunctionalTestRunner` by running it out of the {kib} repo. Ensure that your {kib} Development Environment is setup properly before continuing.
-[float]
-==== Writing your own configuration
+[discrete]
+=== Writing your own configuration
Every project or plugin should have its own `FunctionalTestRunner` config file. Just like {kib}'s, this config file will define all of the test files to load, providers for Services and PageObjects, as well as configuration options for certain services.
@@ -82,8 +82,8 @@ From the root of your repo you should now be able to run the `FunctionalTestRunn
node ../../kibana/scripts/functional_test_runner
-----------
-[float]
-==== Using esArchiver
+[discrete]
+=== Using esArchiver
We're working on documentation for this, but for now the best place to look is the original {kib-repo}/issues/10359[pull request].
diff --git a/docs/developer/plugin/external-plugin-localization.asciidoc b/docs/developer/plugin/external-plugin-localization.asciidoc
index c151832ab53fa..d30dec1a8f46b 100644
--- a/docs/developer/plugin/external-plugin-localization.asciidoc
+++ b/docs/developer/plugin/external-plugin-localization.asciidoc
@@ -1,10 +1,10 @@
[[external-plugin-localization]]
-=== Localization for plugins outside the {kib} repo
+== Localization for plugins outside the {kib} repo
To introduce localization for your plugin, use our i18n tool to create IDs and default messages. You can then extract these IDs with respective default messages into localization JSON files for {kib} to use when running your plugin.
-[float]
-==== Adding localization to your plugin
+[discrete]
+=== Adding localization to your plugin
You must add a `translations` directory at the root of your plugin. This directory will contain the translation files that {kib} uses.
@@ -19,8 +19,8 @@ You must add a `translations` directory at the root of your plugin. This directo
-----------
-[float]
-==== Using {kib} i18n tooling
+[discrete]
+=== Using {kib} i18n tooling
To simplify the localization process, {kib} provides tools for the following functions:
* Verify all translations have translatable strings and extract default messages from templates
@@ -51,8 +51,8 @@ An example {kib} `.i18nrc.json` is {blob}.i18nrc.json[here].
Full documentation about i18n tooling is {blob}src/dev/i18n/README.md[here].
-[float]
-==== Extracting default messages
+[discrete]
+=== Extracting default messages
To extract the default messages from your plugin, run the following command:
["source","shell"]
@@ -62,8 +62,8 @@ node scripts/i18n_extract --output-dir ./translations --include-config ../kibana
This outputs a `en.json` file inside the `translations` directory. To localize other languages, clone the file and translate each string.
-[float]
-==== Checking i18n messages
+[discrete]
+=== Checking i18n messages
Checking i18n does the following:
@@ -80,8 +80,8 @@ node scripts/i18n_check --fix --include-config ../kibana-extra/myPlugin/.i18nrc.
-----------
-[float]
-==== Implementing i18n in the UI
+[discrete]
+=== Implementing i18n in the UI
{kib} relies on several UI frameworks (ReactJS and AngularJS) and
requires localization in different environments (browser and NodeJS).
@@ -97,8 +97,8 @@ so both React and AngularJS frameworks use the same engine and the same
message syntax.
-[float]
-===== i18n for vanilla JavaScript
+[discrete]
+==== i18n for vanilla JavaScript
["source","js"]
-----------
@@ -111,8 +111,8 @@ export const HELLO_WORLD = i18n.translate('hello.wonderful.world', {
Full details are {kib-repo}tree/master/packages/kbn-i18n#vanilla-js[here].
-[float]
-===== i18n for React
+[discrete]
+==== i18n for React
To localize strings in React, use either `FormattedMessage` or `i18n.translate`.
@@ -137,8 +137,8 @@ Full details are {kib-repo}tree/master/packages/kbn-i18n#react[here].
-[float]
-===== i18n for Angular
+[discrete]
+==== i18n for Angular
You are encouraged to use `i18n.translate()` by statically importing `i18n` from `@kbn/i18n` wherever possible in your Angular code. Angular wrappers use the translation `service` with the i18n engine under the hood.
@@ -156,8 +156,8 @@ The translation directive has the following syntax:
Full details are {kib-repo}tree/master/packages/kbn-i18n#angularjs[here].
-[float]
-==== Resources
+[discrete]
+=== Resources
To learn more about i18n tooling, see {blob}src/dev/i18n/README.md[i18n dev tooling].
diff --git a/docs/developer/plugin/index.asciidoc b/docs/developer/plugin/index.asciidoc
index 73f1d2c908fa7..dd83cf234dea4 100644
--- a/docs/developer/plugin/index.asciidoc
+++ b/docs/developer/plugin/index.asciidoc
@@ -9,9 +9,9 @@ The {kib} plugin interfaces are in a state of constant development. We cannot p
Most developers who contribute code directly to the {kib} repo are writing code inside plugins, so our <> docs are the best place to
start. However, there are a few differences when developing plugins outside the {kib} repo. These differences are covered here.
-[float]
+[discrete]
[[automatic-plugin-generator]]
-==== Automatic plugin generator
+=== Automatic plugin generator
We recommend that you kick-start your plugin by generating it with the {kib-repo}tree/{branch}/packages/kbn-plugin-generator[Kibana Plugin Generator]. Run the following in the {kib} repo, and you will be asked a couple questions, see some progress bars, and have a freshly generated plugin ready for you to play with in {kib}'s `plugins` folder.
@@ -20,7 +20,7 @@ We recommend that you kick-start your plugin by generating it with the {kib-repo
node scripts/generate_plugin my_plugin_name # replace "my_plugin_name" with your desired plugin name
-----------
-[float]
+[discrete]
=== Plugin location
The {kib} directory must be named `kibana`, and your plugin directory should be located in the root of `kibana` in a `plugins` directory, for example:
@@ -37,6 +37,6 @@ The {kib} directory must be named `kibana`, and your plugin directory should be
* <>
* <>
-include::external-plugin-functional-tests.asciidoc[]
+include::external-plugin-functional-tests.asciidoc[leveloffset=+1]
-include::external-plugin-localization.asciidoc[]
+include::external-plugin-localization.asciidoc[leveloffset=+1]
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md
new file mode 100644
index 0000000000000..027ae4209b77f
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) > [embeddable](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md)
+
+## ApplyGlobalFilterActionContext.embeddable property
+
+Signature:
+
+```typescript
+embeddable?: IEmbeddable;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md
new file mode 100644
index 0000000000000..6d1d20580fb19
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) > [filters](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md)
+
+## ApplyGlobalFilterActionContext.filters property
+
+Signature:
+
+```typescript
+filters: Filter[];
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md
new file mode 100644
index 0000000000000..62817cd0a1e33
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md)
+
+## ApplyGlobalFilterActionContext interface
+
+Signature:
+
+```typescript
+export interface ApplyGlobalFilterActionContext
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [embeddable](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.embeddable.md) | IEmbeddable
| |
+| [filters](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.filters.md) | Filter[]
| |
+| [timeFieldName](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md) | string
| |
+
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md
new file mode 100644
index 0000000000000..a5cf58018ec65
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) > [timeFieldName](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.timefieldname.md)
+
+## ApplyGlobalFilterActionContext.timeFieldName property
+
+Signature:
+
+```typescript
+timeFieldName?: string;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
index 4852ad15781c7..db41936f35cca 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
@@ -48,6 +48,7 @@
| Interface | Description |
| --- | --- |
| [AggParamOption](./kibana-plugin-plugins-data-public.aggparamoption.md) | |
+| [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) | |
| [DataPublicPluginSetup](./kibana-plugin-plugins-data-public.datapublicpluginsetup.md) | |
| [DataPublicPluginStart](./kibana-plugin-plugins-data-public.datapublicpluginstart.md) | |
| [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) | |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.setup.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.setup.md
index 7bae595e75ad0..a0c9b38792825 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.setup.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.setup.md
@@ -7,14 +7,14 @@
Signature:
```typescript
-setup(core: CoreSetup, { expressions, uiActions, usageCollection }: DataSetupDependencies): DataPublicPluginSetup;
+setup(core: CoreSetup, { expressions, uiActions, usageCollection }: DataSetupDependencies): DataPublicPluginSetup;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
-| core | CoreSetup
| |
+| core | CoreSetup<DataStartDependencies, DataPublicPluginStart>
| |
| { expressions, uiActions, usageCollection } | DataSetupDependencies
| |
Returns:
diff --git a/docs/drilldowns/explore-underlying-data.asciidoc b/docs/drilldowns/explore-underlying-data.asciidoc
new file mode 100644
index 0000000000000..e0f940f73e96e
--- /dev/null
+++ b/docs/drilldowns/explore-underlying-data.asciidoc
@@ -0,0 +1,41 @@
+[[explore-underlying-data]]
+== Explore the underlying data for a visualization
+
+++++
+Explore the underlying data
+++++
+
+Dashboard panels have an *Explore underlying data* action that navigates you to *Discover*,
+where you can narrow your documents to the ones you'll most likely use in a visualization.
+This action is available for visualizations backed by a single index pattern.
+
+You can access *Explore underlying data* in two ways: from the panel context
+menu or from the menu that appears when you interact with the chart.
+
+[float]
+[[explore-data-from-panel-context-menu]]
+=== Explore data from panel context menu
+
+The *Explore underlying data* action in the panel menu navigates you to Discover,
+carrying over the index pattern, filters, query, and time range for the visualization.
+
+[role="screenshot"]
+image::images/explore_data_context_menu.png[Explore underlying data from panel context menu]
+
+[float]
+[[explore-data-from-chart]]
+=== Explore data from chart action
+
+Initiating *Explore underlying data* from the chart also navigates to Discover,
+carrying over the current context for the visualization. In addition, this action
+applies the filters and time range created by the events that triggered the action.
+
+[role="screenshot"]
+image::images/explore_data_in_chart.png[Explore underlying data from chart]
+
+You can disable this action by adding the following line to your `kibana.yml` config.
+
+["source","yml"]
+-----------
+xpack.discoverEnhanced.actions.exploreDataInChart.enabled: false
+-----------
diff --git a/docs/drilldowns/images/explore_data_context_menu.png b/docs/drilldowns/images/explore_data_context_menu.png
new file mode 100644
index 0000000000000..5742991030c89
Binary files /dev/null and b/docs/drilldowns/images/explore_data_context_menu.png differ
diff --git a/docs/drilldowns/images/explore_data_in_chart.png b/docs/drilldowns/images/explore_data_in_chart.png
new file mode 100644
index 0000000000000..05d4f5fac9b2f
Binary files /dev/null and b/docs/drilldowns/images/explore_data_in_chart.png differ
diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc
index 9a94c25bcdf6e..7dc360fd721f4 100644
--- a/docs/management/advanced-options.asciidoc
+++ b/docs/management/advanced-options.asciidoc
@@ -228,7 +228,7 @@ might increase the search time. This setting is off by default. Users must opt-i
`siem:defaultAnomalyScore`:: The threshold above which Machine Learning job anomalies are displayed in the SIEM app.
`siem:defaultIndex`:: A comma-delimited list of Elasticsearch indices from which the SIEM app collects events.
`siem:ipReputationLinks`:: A JSON array containing links for verifying the reputation of an IP address. The links are displayed on
-{siem-guide}/siem-ui-overview.html#network-ui[IP detail] pages.
+{security-guide}/siem-ui-overview.html#network-ui[IP detail] pages.
`siem:enableNewsFeed`:: Enables the security news feed on the SIEM *Overview*
page.
`siem:newsFeedUrl`:: The URL from which the security news feed content is
diff --git a/docs/siem/index.asciidoc b/docs/siem/index.asciidoc
index 9d17b5209304f..ceb4ac2bf1f34 100644
--- a/docs/siem/index.asciidoc
+++ b/docs/siem/index.asciidoc
@@ -19,7 +19,7 @@ image::siem/images/overview-ui.png[SIEM Overview in Kibana]
== Add data
Kibana provides step-by-step instructions to help you add data. The
-{siem-guide}[SIEM Guide] is a good source for more
+{security-guide}[Security Guide] is a good source for more
detailed information and instructions.
[float]
diff --git a/docs/siem/siem-ui.asciidoc b/docs/siem/siem-ui.asciidoc
index 21a2ed55fdfdc..1caa13dc6c903 100644
--- a/docs/siem/siem-ui.asciidoc
+++ b/docs/siem/siem-ui.asciidoc
@@ -43,7 +43,7 @@ for creating signals. The SIEM app comes with prebuilt rules that search for
suspicious activity on your network and hosts. Additionally, you can
create your own rules.
-See {siem-guide}/detection-engine-overview.html[Detections] in the SIEM
+See {security-guide}/detection-engine-overview.html[Detections] in the SIEM
Guide for information on managing detection rules and signals via the UI
or the Detections API.
@@ -61,7 +61,7 @@ saved Timelines. Additionally, you can send cases to external systems from
within SIEM (currently ServiceNow and Jira).
For information about opening, updating, and closing cases, see
-{siem-guide}/cases-overview.html[Cases] in the SIEM Guide.
+{security-guide}/cases-overview.html[Cases] in the SIEM Guide.
[role="screenshot"]
image::siem/images/cases-ui.png[]
@@ -82,7 +82,7 @@ Hosts and Network pages, or even from within Timeline itself.
A timeline is responsive and persists as you move through the SIEM app
collecting data.
-See the {siem-guide}[SIEM Guide] for more details on data sources and an
+See the {security-guide}[Security Guide] for more details on data sources and an
overview of UI elements and capabilities.
[float]
diff --git a/docs/user/alerting/action-types.asciidoc b/docs/user/alerting/action-types.asciidoc
index e8dcf689df8e4..1743edb10f92b 100644
--- a/docs/user/alerting/action-types.asciidoc
+++ b/docs/user/alerting/action-types.asciidoc
@@ -23,6 +23,10 @@ a| <>
| Add a message to a Kibana log.
+a| <>
+
+| Push or update data to a new incident in ServiceNow.
+
a| <>
| Send a message to a Slack channel or user.
@@ -55,3 +59,4 @@ include::action-types/server-log.asciidoc[]
include::action-types/slack.asciidoc[]
include::action-types/webhook.asciidoc[]
include::action-types/pre-configured-connectors.asciidoc[]
+include::action-types/servicenow.asciidoc[]
diff --git a/docs/user/alerting/action-types/servicenow.asciidoc b/docs/user/alerting/action-types/servicenow.asciidoc
new file mode 100644
index 0000000000000..32f828aea2357
--- /dev/null
+++ b/docs/user/alerting/action-types/servicenow.asciidoc
@@ -0,0 +1,72 @@
+[role="xpack"]
+[[servicenow-action-type]]
+=== ServiceNow action
+
+The ServiceNow action type uses the https://developer.servicenow.com/app.do#!/rest_api_doc?v=orlando&id=c_TableAPI[V2 Table API] to create ServiceNow incidents.
+
+[float]
+[[servicenow-connector-configuration]]
+==== Connector configuration
+
+ServiceNow connectors have the following configuration properties:
+
+Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action.
+URL:: ServiceNow instance URL.
+Username:: Username for HTTP Basic authentication.
+Password:: Password for HTTP Basic authentication.
+
+[float]
+[[Preconfigured-servicenow-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ my-servicenow:
+ name: preconfigured-servicenow-action-type
+ actionTypeId: .servicenow
+ config:
+ apiUrl: https://dev94428.service-now.com/
+ secrets:
+ username: testuser
+ password: passwordkeystorevalue
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+[cols="2*<"]
+|===
+
+| `apiUrl`
+| An address that corresponds to *Sender*.
+
+|===
+
+`secrets` defines sensitive information for the action type:
+
+[cols="2*<"]
+|===
+
+| `username`
+| A string that corresponds to *User*.
+
+| `password`
+| A string that corresponds to *Password*. Should be stored in the <>.
+
+|===
+
+[[servicenow-action-configuration]]
+==== Action configuration
+
+ServiceNow actions have the following configuration properties:
+
+Urgency:: The extent to which the incident resolution can delay.
+Severity:: The severity of the incident.
+Impact:: The effect an incident has on business. Can be measured by the number of affected users or by how critical it is to the business in question.
+Short description:: A short description of the incident, used for searching the contents of the knowledge base.
+Description:: The details about the incident.
+Additional comments:: Additional information for the client, such as how to troubleshoot the issue.
+
+[[configuring-servicenow]]
+==== Configuring and testing ServiceNow
+
+ServiceNow offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents.
diff --git a/docs/user/dashboard.asciidoc b/docs/user/dashboard.asciidoc
index a812d4e3bdd2d..b812af7e981bf 100644
--- a/docs/user/dashboard.asciidoc
+++ b/docs/user/dashboard.asciidoc
@@ -160,6 +160,7 @@ When you're finished adding and arranging the panels, save the dashboard.
. Enter the dashboard *Title* and optional *Description*, then *Save* the dashboard.
include::{kib-repo-dir}/drilldowns/drilldowns.asciidoc[]
+include::{kib-repo-dir}/drilldowns/explore-underlying-data.asciidoc[]
[[sharing-dashboards]]
== Share the dashboard
diff --git a/docs/user/ml/images/ml-annotations-list.jpg b/docs/user/ml/images/ml-annotations-list.jpg
deleted file mode 100644
index 8b1194dd20c0f..0000000000000
Binary files a/docs/user/ml/images/ml-annotations-list.jpg and /dev/null differ
diff --git a/docs/user/ml/images/ml-annotations-list.png b/docs/user/ml/images/ml-annotations-list.png
new file mode 100644
index 0000000000000..f1a0b66241126
Binary files /dev/null and b/docs/user/ml/images/ml-annotations-list.png differ
diff --git a/docs/user/ml/images/ml-job-management.jpg b/docs/user/ml/images/ml-job-management.jpg
deleted file mode 100644
index efdf7923c0faa..0000000000000
Binary files a/docs/user/ml/images/ml-job-management.jpg and /dev/null differ
diff --git a/docs/user/ml/images/ml-job-management.png b/docs/user/ml/images/ml-job-management.png
new file mode 100644
index 0000000000000..4589c7093a7cf
Binary files /dev/null and b/docs/user/ml/images/ml-job-management.png differ
diff --git a/docs/user/ml/images/ml-settings.jpg b/docs/user/ml/images/ml-settings.jpg
deleted file mode 100644
index 3713be005924d..0000000000000
Binary files a/docs/user/ml/images/ml-settings.jpg and /dev/null differ
diff --git a/docs/user/ml/images/ml-settings.png b/docs/user/ml/images/ml-settings.png
new file mode 100644
index 0000000000000..f5c9fca647389
Binary files /dev/null and b/docs/user/ml/images/ml-settings.png differ
diff --git a/docs/user/ml/images/ml-single-metric-viewer.jpg b/docs/user/ml/images/ml-single-metric-viewer.jpg
deleted file mode 100644
index 2fbb9387d1e29..0000000000000
Binary files a/docs/user/ml/images/ml-single-metric-viewer.jpg and /dev/null differ
diff --git a/docs/user/ml/images/ml-single-metric-viewer.png b/docs/user/ml/images/ml-single-metric-viewer.png
new file mode 100644
index 0000000000000..04c21d9bc533a
Binary files /dev/null and b/docs/user/ml/images/ml-single-metric-viewer.png differ
diff --git a/docs/user/ml/images/outliers.png b/docs/user/ml/images/outliers.png
index 3f4c5f6c6bbf0..874ebbc79201c 100644
Binary files a/docs/user/ml/images/outliers.png and b/docs/user/ml/images/outliers.png differ
diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc
index 1bc74ce87de08..214dae2b96e04 100644
--- a/docs/user/ml/index.asciidoc
+++ b/docs/user/ml/index.asciidoc
@@ -47,20 +47,20 @@ create {anomaly-jobs} and manage jobs and {dfeeds} from the *Job Management*
pane:
[role="screenshot"]
-image::user/ml/images/ml-job-management.jpg[Job Management]
+image::user/ml/images/ml-job-management.png[Job Management]
You can use the *Settings* pane to create and edit
{ml-docs}/ml-calendars.html[calendars] and the filters that are used in
{ml-docs}/ml-rules.html[custom rules]:
[role="screenshot"]
-image::user/ml/images/ml-settings.jpg[Calendar Management]
+image::user/ml/images/ml-settings.png[Calendar Management]
The *Anomaly Explorer* and *Single Metric Viewer* display the results of your
{anomaly-jobs}. For example:
[role="screenshot"]
-image::user/ml/images/ml-single-metric-viewer.jpg[Single Metric Viewer]
+image::user/ml/images/ml-single-metric-viewer.png[Single Metric Viewer]
You can optionally add annotations by drag-selecting a period of time in
the *Single Metric Viewer* and adding a description. For example, you can add an
@@ -68,7 +68,7 @@ explanation for anomalies in that time period or provide notes about what is
occurring in your operational environment at that time:
[role="screenshot"]
-image::user/ml/images/ml-annotations-list.jpg[Single Metric Viewer with annotations]
+image::user/ml/images/ml-annotations-list.png[Single Metric Viewer with annotations]
In some circumstances, annotations are also added automatically. For example, if
the {anomaly-job} detects that there is missing data, it annotates the affected
@@ -94,8 +94,8 @@ The Elastic {ml} {dfanalytics} feature enables you to analyze your data using
indices that contain the results alongside your source data.
If you have a license that includes the {ml-features}, you can create
-{dfanalytics-jobs} and view their results on the *Analytics* page
-in {kib}. For example:
+{dfanalytics-jobs} and view their results on the *Data Frame Analytics* page in
+{kib}. For example:
[role="screenshot"]
image::user/ml/images/outliers.png[{oldetection-cap} results in {kib}]
diff --git a/docs/visualize/images/vega_lite_tutorial_1.png b/docs/visualize/images/vega_lite_tutorial_1.png
new file mode 100644
index 0000000000000..4e8d0aba3635b
Binary files /dev/null and b/docs/visualize/images/vega_lite_tutorial_1.png differ
diff --git a/docs/visualize/images/vega_lite_tutorial_2.png b/docs/visualize/images/vega_lite_tutorial_2.png
new file mode 100644
index 0000000000000..523ae91514a11
Binary files /dev/null and b/docs/visualize/images/vega_lite_tutorial_2.png differ
diff --git a/docs/visualize/images/vega_tutorial_3.png b/docs/visualize/images/vega_tutorial_3.png
new file mode 100644
index 0000000000000..e025ecc585807
Binary files /dev/null and b/docs/visualize/images/vega_tutorial_3.png differ
diff --git a/docs/visualize/images/vega_tutorial_4.png b/docs/visualize/images/vega_tutorial_4.png
new file mode 100644
index 0000000000000..c8ee311e9bf5e
Binary files /dev/null and b/docs/visualize/images/vega_tutorial_4.png differ
diff --git a/docs/visualize/vega.asciidoc b/docs/visualize/vega.asciidoc
index 24bd3a44bebba..3a1c57da93f07 100644
--- a/docs/visualize/vega.asciidoc
+++ b/docs/visualize/vega.asciidoc
@@ -3,71 +3,1287 @@
experimental[]
-Build custom visualizations from multiple data sources using Vega
-and Vega-Lite.
+Build custom visualizations using Vega and Vega-Lite, backed by one or more
+data sources including {es}, Elastic Map Service, URL,
+or static data. Use the {kib} extensions to Vega to embed Vega into
+your dashboard, and to add interactivity to the visualizations.
-* *Vega* — A declarative format to create visualizations using JSON.
- Generate interactive displays using D3.
+Vega and Vega-Lite are both declarative formats to create visualizations
+using JSON. Both use a different syntax for declaring visualizations,
+and are not fully interchangeable.
-* *Vega-Lite* — An easier format to use than Vega that enables more rapid
- data analysis. Compiles into Vega.
+[float]
+[[when-to-vega]]
+=== When to use Vega
-For more information about Vega and Vega-Lite, refer to
-<>.
+Vega and Vega-Lite are capable of building most of the visualizations
+that {kib} provides, but with higher complexity. The most common reason
+to use Vega in {kib} is that {kib} is missing support for the query or
+visualization, for example:
-[float]
-[[create-vega-viz]]
-=== Create Vega visualizations
+* Aggregations using the `nested` or `parent/child` mapping
+* Aggregations without a {kib} index pattern
+* Queries using custom time filters
+* Complex calculations
+* Extracting data from _source instead of aggregation
+* Scatter charts
+* Sankey charts
+* Custom maps
+* Using a visual theme that {kib} does not provide
+
+[[vega-lite-tutorial]]
+=== Tutorial: First visualization in Vega-Lite
-You create Vega visualizations by using the text editor, which is
-preconfigured with the options you need.
+In this tutorial, you will learn about how to edit Vega-Lite in {kib} to create
+a stacked area chart from an {es} search query. It will give you a starting point
+for a more comprehensive
+https://vega.github.io/vega-lite/tutorials/getting_started.html[introduction to Vega-Lite],
+while only covering the basics.
+
+In this tutorial, you will build a stacked area chart from one of the {kib} sample data
+sets.
[role="screenshot"]
-image::images/vega_lite_default.png[]
+image::visualize/images/vega_lite_tutorial_1.png[]
-[float]
-[[vega-schema]]
-==== Change the Vega version
+Before beginning this tutorial, install the <>
+set.
+
+When you first open the Vega editor in {kib}, you will see a pre-populated
+line chart which shows the total number of documents across all your indices
+within the time range.
-The default visualization uses Vega-Lite version 2. To use Vega version 4, edit
-the `schema`.
+[role="screenshot"]
+image::visualize/images/vega_lite_default.png[]
+
+The text editor contains a Vega-Lite spec written in https://hjson.github.io/[HJSON],
+which is similar to JSON but optimized for human editing. HJSON supports:
-Go to `$schema`, enter `https://vega.github.io/schema/vega/v5.json`, then click
-*Update*.
+* Comments using // or /* syntax
+* Object keys without quotes
+* String values without quotes
+* Optional commas
+* Double or single quotes
+* Multiline strings
[float]
-[[vega-type]]
-==== Change the visualization type
+==== Small steps
-The default visualization is a line chart. To change the visualization type,
-change the `mark` value. The supported visualization types are listed in the
-text editor.
+Always work on Vega in the smallest steps possible, and save your work frequently.
+Small changes will cause unexpected results. Click the "Save" button now.
-Go to `mark`, change the value to a different visualization type, then click
-*Update*.
+The first step is to change the index to one of the <>
+sets. Change
+
+```yaml
+index: _all
+```
+
+to:
+
+```yaml
+index: kibana_sample_data_ecommerce
+```
+
+Click "Update". The result is probably not what you expect. You should see a flat
+line with 0 results.
+
+You've only changed the index, so the difference must be the query is returning
+no results. You can try the <>,
+but intuition may be faster for this particular problem.
+
+In this case, the problem is that you are querying the field `@timestamp`,
+which does not exist in the `kibana_sample_data_ecommerce` data. Find and replace
+`@timestamp` with `order_date`. This fixes the problem, leaving you with this spec:
+
+.Expand Vega-Lite spec
+[%collapsible%closed]
+====
+[source,yaml]
+----
+{
+ $schema: https://vega.github.io/schema/vega-lite/v4.json
+ title: Event counts from ecommerce
+ data: {
+ url: {
+ %context%: true
+ %timefield%: order_date
+ index: kibana_sample_data_ecommerce
+ body: {
+ aggs: {
+ time_buckets: {
+ date_histogram: {
+ field: order_date
+ interval: {%autointerval%: true}
+ extended_bounds: {
+ min: {%timefilter%: "min"}
+ max: {%timefilter%: "max"}
+ }
+ min_doc_count: 0
+ }
+ }
+ }
+ size: 0
+ }
+ }
+ format: {property: "aggregations.time_buckets.buckets" }
+ }
+
+ mark: line
+
+ encoding: {
+ x: {
+ field: key
+ type: temporal
+ axis: { title: null }
+ }
+ y: {
+ field: doc_count
+ type: quantitative
+ axis: { title: "Document count" }
+ }
+ }
+}
+----
+
+====
+
+Now, let's make the visualization more interesting by adding another aggregation
+to create a stacked area chart. To verify that you have constructed the right
+query, it is easiest to use the {kib} Dev Tools in a separate tab from the
+Vega editor. Open the Dev Tools from the Management section of the navigation.
+
+This query is roughly equivalent to the one that is used in the default
+Vega-Lite spec. Copy it into the Dev Tools:
+
+```js
+POST kibana_sample_data_ecommerce/_search
+{
+ "query": {
+ "range": {
+ "order_date": {
+ "gte": "now-7d"
+ }
+ }
+ },
+ "aggs": {
+ "time_buckets": {
+ "date_histogram": {
+ "field": "order_date",
+ "fixed_interval": "1d",
+ "extended_bounds": {
+ "min": "now-7d"
+ },
+ "min_doc_count": 0
+ }
+ }
+ },
+ "size": 0
+}
+```
+
+There's not enough data to create a stacked bar in the original query, so we
+will add a new
+{ref}/search-aggregations-bucket-terms-aggregation.html[terms aggregation]:
+
+```js
+POST kibana_sample_data_ecommerce/_search
+{
+ "query": {
+ "range": {
+ "order_date": {
+ "gte": "now-7d"
+ }
+ }
+ },
+ "aggs": {
+ "categories": {
+ "terms": { "field": "category.keyword" },
+ "aggs": {
+ "time_buckets": {
+ "date_histogram": {
+ "field": "order_date",
+ "fixed_interval": "1d",
+ "extended_bounds": {
+ "min": "now-7d"
+ },
+ "min_doc_count": 0
+ }
+ }
+ }
+ }
+ },
+ "size": 0
+}
+```
+
+You'll see that the response format looks different from the previous query:
+
+```json
+{
+ "aggregations" : {
+ "categories" : {
+ "doc_count_error_upper_bound" : 0,
+ "sum_other_doc_count" : 0,
+ "buckets" : [{
+ "key" : "Men's Clothing",
+ "doc_count" : 1661,
+ "time_buckets" : {
+ "buckets" : [{
+ "key_as_string" : "2020-06-30T00:00:00.000Z",
+ "key" : 1593475200000,
+ "doc_count" : 19
+ }, {
+ "key_as_string" : "2020-07-01T00:00:00.000Z",
+ "key" : 1593561600000,
+ "doc_count" : 71
+ }]
+ }
+ }]
+ }
+ }
+}
+```
+
+Now that we have data that we're happy with, it's time to convert from an
+isolated {es} query into a query with {kib} integration. Looking at the
+<>, you will
+see the full list of special tokens that are used in this query, such
+as `%context: true`. This query has also replaced `"fixed_interval": "1d"`
+with `interval: {%autointerval%: true}`. Copy the final query into
+your spec:
+
+```yaml
+ data: {
+ url: {
+ %context%: true
+ %timefield%: order_date
+ index: kibana_sample_data_ecommerce
+ body: {
+ aggs: {
+ categories: {
+ terms: { field: "category.keyword" }
+ aggs: {
+ time_buckets: {
+ date_histogram: {
+ field: order_date
+ interval: {%autointerval%: true}
+ extended_bounds: {
+ min: {%timefilter%: "min"}
+ max: {%timefilter%: "max"}
+ }
+ min_doc_count: 0
+ }
+ }
+ }
+ }
+ }
+ size: 0
+ }
+ }
+ format: {property: "aggregations.categories.buckets" }
+ }
+```
+
+If you copy and paste that into your Vega-Lite spec, and click "Update",
+you will see a warning saying `Infinite extent for field "key": [Infinity, -Infinity]`.
+Let's use our <> to understand why.
+
+Vega-Lite generates data using the names `source_0` and `data_0`. `source_0` contains
+the results from the {es} query, and `data_0` contains the visually encoded results
+which are shown in the chart. To debug this problem, you need to compare both.
+
+To look at the source, open the browser dev tools console and type
+`VEGA_DEBUG.view.data('source_0')`. You will see:
+
+```js
+[{
+ doc_count: 454
+ key: "Men's Clothing"
+ time_buckets: {buckets: Array(57)}
+ Symbol(vega_id): 12822
+}, ...]
+```
+
+To compare to the visually encoded data, open the browser dev tools console and type
+`VEGA_DEBUG.view.data('data_0')`. You will see:
+
+```js
+[{
+ doc_count: 454
+ key: NaN
+ time_buckets: {buckets: Array(57)}
+ Symbol(vega_id): 13879
+}]
+```
+
+The issue seems to be that the `key` property is not being converted the right way,
+which makes sense because the `key` is now `Men's Clothing` instead of a timestamp.
+
+To fix this, try updating the `encoding` of your Vega-Lite spec to:
+
+```yaml
+ encoding: {
+ x: {
+ field: time_buckets.buckets.key
+ type: temporal
+ axis: { title: null }
+ }
+ y: {
+ field: time_buckets.buckets.doc_count
+ type: quantitative
+ axis: { title: "Document count" }
+ }
+ }
+```
+
+This will show more errors, and you can inspect `VEGA_DEBUG.view.data('data_0')` to
+understand why. This now shows:
+
+```js
+[{
+ doc_count: 454
+ key: "Men's Clothing"
+ time_buckets: {buckets: Array(57)}
+ time_buckets.buckets.doc_count: undefined
+ time_buckets.buckets.key: null
+ Symbol(vega_id): 14094
+}]
+```
+
+It looks like the problem is that the `time_buckets` inner array is not being
+extracted by Vega. The solution is to use a Vega-lite
+https://vega.github.io/vega-lite/docs/flatten.html[flatten transformation], available in {kib} 7.9 and later.
+If using an older version of Kibana, the flatten transformation is available in Vega
+but not Vega-Lite.
+
+Add this section in between the `data` and `encoding` section:
+
+```yaml
+ transform: [{
+ flatten: ["time_buckets.buckets"]
+ }]
+```
+
+This does not yet produce the results you expect. Inspect the transformed data
+by typing `VEGA_DEBUG.view.data('data_0')` into the console again:
+
+```js
+[{
+ doc_count: 453
+ key: "Men's Clothing"
+ time_bucket.buckets.doc_count: undefined
+ time_buckets: {buckets: Array(57)}
+ time_buckets.buckets: {
+ key_as_string: "2020-06-30T15:00:00.000Z",
+ key: 1593529200000,
+ doc_count: 2
+ }
+ time_buckets.buckets.key: null
+ Symbol(vega_id): 21564
+}]
+```
+
+The debug view shows `undefined` values where you would expect to see numbers, and
+the cause is that there are duplicate names which are confusing Vega-Lite. This can
+be fixed by making this change to the `transform` and `encoding` blocks:
+
+```yaml
+ transform: [{
+ flatten: ["time_buckets.buckets"],
+ as: ["buckets"]
+ }]
+
+ mark: area
+
+ encoding: {
+ x: {
+ field: buckets.key
+ type: temporal
+ axis: { title: null }
+ }
+ y: {
+ field: buckets.doc_count
+ type: quantitative
+ axis: { title: "Document count" }
+ }
+ color: {
+ field: key
+ type: nominal
+ }
+ }
+```
+
+At this point, you have a stacked area chart that shows the top categories,
+but the chart is still missing some common features that we expect from a {kib}
+visualization. Let's add hover states and tooltips next.
+
+Hover states are handled differently in Vega-Lite and Vega. In Vega-Lite this is
+done using a concept called `selection`, which has many permutations that are not
+covered in this tutorial. We will be adding a simple tooltip and hover state.
+
+Because {kib} has enabled the https://vega.github.io/vega-lite/docs/tooltip.html[Vega tooltip plugin],
+tooltips can be defined in several ways:
+
+* Automatic tooltip based on the data, via `{ content: "data" }`
+* Array of fields, like `[{ field: "key", type: "nominal" }]`
+* Defining a custom Javascript object using the `calculate` transform
+
+For the simple tooltip, add this to your encoding:
+
+```yaml
+ encoding: {
+ tooltip: [{
+ field: buckets.key
+ type: temporal
+ title: "Date"
+ }, {
+ field: key
+ type: nominal
+ title: "Category"
+ }, {
+ field: buckets.doc_count
+ type: quantitative
+ title: "Count"
+ }]
+ }
+```
+
+As you hover over the area series in your chart, a multi-line tooltip will
+appear, but it won't indicate the nearest point that it's pointing to. To
+indicate the nearest point, we need to add a second layer.
+
+The first step is to remove the `mark: area` from your visualization.
+Once you've removed the previous mark, add a composite mark at the end of
+the Vega-Lite spec:
+
+```yaml
+ layer: [{
+ mark: area
+ }, {
+ mark: point
+ }]
+```
+
+You'll see that the points are not appearing to line up with the area chart,
+and the reason is that the points are not being stacked. Change your Y encoding
+to this:
+
+```yaml
+ y: {
+ field: buckets.doc_count
+ type: quantitative
+ axis: { title: "Document count" }
+ stack: true
+ }
+```
+
+Now, we will add a `selection` block inside the point mark:
+
+```yaml
+ layer: [{
+ mark: area
+ }, {
+ mark: point
+
+ selection: {
+ pointhover: {
+ type: single
+ on: mouseover
+ clear: mouseout
+ empty: none
+ fields: ["buckets.key", "key"]
+ nearest: true
+ }
+ }
+
+ encoding: {
+ size: {
+ condition: {
+ selection: pointhover
+ value: 100
+ }
+ value: 5
+ }
+ fill: {
+ condition: {
+ selection: pointhover
+ value: white
+ }
+ }
+ }
+ }]
+```
+
+Now that you've enabled a selection, try moving the mouse around the visualization
+and seeing the points respond to the nearest position:
+
+[role="screenshot"]
+image::visualize/images/vega_lite_tutorial_2.png[]
+
+The final result of this tutorial is this spec:
+
+.Expand final Vega-Lite spec
+[%collapsible%closed]
+====
+[source,yaml]
+----
+{
+ $schema: https://vega.github.io/schema/vega-lite/v4.json
+ title: Event counts from ecommerce
+ data: {
+ url: {
+ %context%: true
+ %timefield%: order_date
+ index: kibana_sample_data_ecommerce
+ body: {
+ aggs: {
+ categories: {
+ terms: { field: "category.keyword" }
+ aggs: {
+ time_buckets: {
+ date_histogram: {
+ field: order_date
+ interval: {%autointerval%: true}
+ extended_bounds: {
+ min: {%timefilter%: "min"}
+ max: {%timefilter%: "max"}
+ }
+ min_doc_count: 0
+ }
+ }
+ }
+ }
+ }
+ size: 0
+ }
+ }
+ format: {property: "aggregations.categories.buckets" }
+ }
+
+ transform: [{
+ flatten: ["time_buckets.buckets"]
+ as: ["buckets"]
+ }]
+
+ encoding: {
+ x: {
+ field: buckets.key
+ type: temporal
+ axis: { title: null }
+ }
+ y: {
+ field: buckets.doc_count
+ type: quantitative
+ axis: { title: "Document count" }
+ stack: true
+ }
+ color: {
+ field: key
+ type: nominal
+ title: "Category"
+ }
+ tooltip: [{
+ field: buckets.key
+ type: temporal
+ title: "Date"
+ }, {
+ field: key
+ type: nominal
+ title: "Category"
+ }, {
+ field: buckets.doc_count
+ type: quantitative
+ title: "Count"
+ }]
+ }
+
+ layer: [{
+ mark: area
+ }, {
+ mark: point
+
+ selection: {
+ pointhover: {
+ type: single
+ on: mouseover
+ clear: mouseout
+ empty: none
+ fields: ["buckets.key", "key"]
+ nearest: true
+ }
+ }
+
+ encoding: {
+ size: {
+ condition: {
+ selection: pointhover
+ value: 100
+ }
+ value: 5
+ }
+ fill: {
+ condition: {
+ selection: pointhover
+ value: white
+ }
+ }
+ }
+ }]
+}
+----
+
+====
+
+[[vega-tutorial]]
+=== Tutorial: Updating {kib} filters from Vega
+
+In this tutorial you will build an area chart in Vega using an {es} search query,
+and add a click handler and drag handler to update {kib} filters.
+This tutorial is not a full https://vega.github.io/vega/tutorials/[Vega tutorial],
+but will cover the basics of creating Vega visualizations into {kib}.
+
+First, create an almost-blank Vega chart by pasting this into the editor:
+
+```yaml
+{
+ $schema: "https://vega.github.io/schema/vega/v5.json"
+ data: [{
+ name: source_0
+ }]
+
+ scales: [{
+ name: x
+ type: time
+ range: width
+ }, {
+ name: y
+ type: linear
+ range: height
+ }]
+
+ axes: [{
+ orient: bottom
+ scale: x
+ }, {
+ orient: left
+ scale: y
+ }]
+
+ marks: [
+ {
+ type: area
+ from: {
+ data: source_0
+ }
+ encode: {
+ update: {
+ }
+ }
+ }
+ ]
+}
+```
+
+Despite being almost blank, this Vega spec still contains the minimum requirements:
+
+* Data
+* Scales
+* Marks
+* (optional) Axes
+
+Next, add a valid {es} search query in the `data` block:
+
+```yaml
+ data: [
+ {
+ name: source_0
+ url: {
+ %context%: true
+ %timefield%: order_date
+ index: kibana_sample_data_ecommerce
+ body: {
+ aggs: {
+ time_buckets: {
+ date_histogram: {
+ field: order_date
+ fixed_interval: "3h"
+ extended_bounds: {
+ min: {%timefilter%: "min"}
+ max: {%timefilter%: "max"}
+ }
+ min_doc_count: 0
+ }
+ }
+ }
+ size: 0
+ }
+ }
+ format: { property: "aggregations.time_buckets.buckets" }
+ }
+ ]
+```
+
+Click "Update", and nothing will change in the visualization. The first step
+is to change the X and Y scales based on the data:
+
+```yaml
+ scales: [{
+ name: x
+ type: time
+ range: width
+ domain: {
+ data: source_0
+ field: key
+ }
+ }, {
+ name: y
+ type: linear
+ range: height
+ domain: {
+ data: source_0
+ field: doc_count
+ }
+ }]
+```
+
+Click "Update", and you will see that the X and Y axes are now showing labels based
+on the real data.
+
+Next, encode the fields `key` and `doc_count` as the X and Y values:
+
+```yaml
+ marks: [
+ {
+ type: area
+ from: {
+ data: source_0
+ }
+ encode: {
+ update: {
+ x: {
+ scale: x
+ field: key
+ }
+ y: {
+ scale: y
+ value: 0
+ }
+ y2: {
+ scale: y
+ field: doc_count
+ }
+ }
+ }
+ }
+ ]
+```
+
+Click "Update" and you will get a basic area chart:
+
+[role="screenshot"]
+image::visualize/images/vega_tutorial_3.png[]
+
+Next, add a new block to the `marks` section. This will show clickable points to filter for a specific
+date:
+
+```yaml
+ {
+ name: point
+ type: symbol
+ style: ["point"]
+ from: {
+ data: source_0
+ }
+ encode: {
+ update: {
+ x: {
+ scale: x
+ field: key
+ }
+ y: {
+ scale: y
+ field: doc_count
+ }
+ size: {
+ value: 100
+ }
+ fill: {
+ value: black
+ }
+ }
+ }
+ }
+```
+
+Next, we will create a Vega signal to make the points clickable. You can access
+the clicked `datum` in the expression used to update. In this case, you want
+clicks on points to add a time filter with the 3-hour interval defined above.
+
+```yaml
+ signals: [
+ {
+ name: point_click
+ on: [{
+ events: {
+ source: scope
+ type: click
+ markname: point
+ }
+ update: '''kibanaSetTimeFilter(datum.key, datum.key + 3 * 60 * 60 * 1000)'''
+ }]
+ }
+ ]
+```
+
+This event is using the {kib} custom function `kibanaSetTimeFilter` to generate a filter that
+gets applied to the entire dashboard on click.
+
+The mouse cursor does not currently indicate that the chart is interactive. Find the `marks` section,
+and update the mark named `point` by adding `cursor: { value: "pointer" }` to
+the `encoding` section like this:
+
+```yaml
+ {
+ name: point
+ type: symbol
+ style: ["point"]
+ from: {
+ data: source_0
+ }
+ encode: {
+ update: {
+ ...
+ cursor: { value: "pointer" }
+ }
+ }
+ }
+```
+
+Next, we will add a drag interaction which will allow the user to narrow into
+a specific time range in the visualization. This will require adding more signals, and
+adding a rectangle overlay:
+
+[role="screenshot"]
+image::visualize/images/vega_tutorial_4.png[]
+
+The first step is to add a new `signal` to track the X position of the cursor:
+
+```yaml
+ {
+ name: currentX
+ value: -1
+ on: [{
+ events: {
+ type: mousemove
+ source: view
+ },
+ update: "clamp(x(), 0, width)"
+ }, {
+ events: {
+ type: mouseout
+ source: view
+ }
+ update: "-1"
+ }]
+ }
+```
+
+Now add a new `mark` to indicate the current cursor position:
+
+```yaml
+ {
+ type: rule
+ interactive: false
+ encode: {
+ update: {
+ y: {value: 0}
+ y2: {signal: "height"}
+ stroke: {value: "gray"}
+ strokeDash: {
+ value: [2, 1]
+ }
+ x: {signal: "max(currentX,0)"}
+ defined: {signal: "currentX > 0"}
+ }
+ }
+ }
+```
+
+Next, add a signal to track the current selected range, which will update
+until the user releases the mouse button or uses the escape key:
+
+
+```yaml
+ {
+ name: selected
+ value: [0, 0]
+ on: [{
+ events: {
+ type: mousedown
+ source: view
+ }
+ update: "[clamp(x(), 0, width), clamp(x(), 0, width)]"
+ }, {
+ events: {
+ type: mousemove
+ source: window
+ consume: true
+ between: [{
+ type: mousedown
+ source: view
+ }, {
+ merge: [{
+ type: mouseup
+ source: window
+ }, {
+ type: keydown
+ source: window
+ filter: "event.key === 'Escape'"
+ }]
+ }]
+ }
+ update: "[selected[0], clamp(x(), 0, width)]"
+ }, {
+ events: {
+ type: keydown
+ source: window
+ filter: "event.key === 'Escape'"
+ }
+ update: "[0, 0]"
+ }]
+ }
+```
+
+Now that there is a signal which tracks the time range from the user, we need to indicate
+the range visually by adding a new mark which only appears conditionally:
+
+```yaml
+ {
+ type: rect
+ name: selectedRect
+ encode: {
+ update: {
+ height: {signal: "height"}
+ fill: {value: "#333"}
+ fillOpacity: {value: 0.2}
+ x: {signal: "selected[0]"}
+ x2: {signal: "selected[1]"}
+ defined: {signal: "selected[0] !== selected[1]"}
+ }
+ }
+ }
+```
+
+Finally, add a new signal which will update the {kib} time filter when the mouse is released while
+dragging:
+
+```yaml
+ {
+ name: applyTimeFilter
+ value: null
+ on: [{
+ events: {
+ type: mouseup
+ source: view
+ }
+ update: '''selected[0] !== selected[1] ? kibanaSetTimeFilter(
+ invert('x',selected[0]),
+ invert('x',selected[1])) : null'''
+ }]
+ }
+```
+
+Putting this all together, your visualization now supports the main features of
+standard visualizations in {kib}, but with the potential to add even more control.
+The final Vega spec for this tutorial is here:
+
+.Expand final Vega spec
+[%collapsible%closed]
+====
+[source,yaml]
+----
+{
+ $schema: "https://vega.github.io/schema/vega/v5.json"
+ data: [
+ {
+ name: source_0
+ url: {
+ %context%: true
+ %timefield%: order_date
+ index: kibana_sample_data_ecommerce
+ body: {
+ aggs: {
+ time_buckets: {
+ date_histogram: {
+ field: order_date
+ fixed_interval: "3h"
+ extended_bounds: {
+ min: {%timefilter%: "min"}
+ max: {%timefilter%: "max"}
+ }
+ min_doc_count: 0
+ }
+ }
+ }
+ size: 0
+ }
+ }
+ format: { property: "aggregations.time_buckets.buckets" }
+ }
+ ]
+
+ scales: [{
+ name: x
+ type: time
+ range: width
+ domain: {
+ data: source_0
+ field: key
+ }
+ }, {
+ name: y
+ type: linear
+ range: height
+ domain: {
+ data: source_0
+ field: doc_count
+ }
+ }]
+
+ axes: [{
+ orient: bottom
+ scale: x
+ }, {
+ orient: left
+ scale: y
+ }]
+
+ marks: [
+ {
+ type: area
+ from: {
+ data: source_0
+ }
+ encode: {
+ update: {
+ x: {
+ scale: x
+ field: key
+ }
+ y: {
+ scale: y
+ value: 0
+ }
+ y2: {
+ scale: y
+ field: doc_count
+ }
+ }
+ }
+ },
+ {
+ name: point
+ type: symbol
+ style: ["point"]
+ from: {
+ data: source_0
+ }
+ encode: {
+ update: {
+ x: {
+ scale: x
+ field: key
+ }
+ y: {
+ scale: y
+ field: doc_count
+ }
+ size: {
+ value: 100
+ }
+ fill: {
+ value: black
+ }
+ cursor: { value: "pointer" }
+ }
+ }
+ },
+ {
+ type: rule
+ interactive: false
+ encode: {
+ update: {
+ y: {value: 0}
+ y2: {signal: "height"}
+ stroke: {value: "gray"}
+ strokeDash: {
+ value: [2, 1]
+ }
+ x: {signal: "max(currentX,0)"}
+ defined: {signal: "currentX > 0"}
+ }
+ }
+ },
+ {
+ type: rect
+ name: selectedRect
+ encode: {
+ update: {
+ height: {signal: "height"}
+ fill: {value: "#333"}
+ fillOpacity: {value: 0.2}
+ x: {signal: "selected[0]"}
+ x2: {signal: "selected[1]"}
+ defined: {signal: "selected[0] !== selected[1]"}
+ }
+ }
+ }
+ ]
+
+ signals: [
+ {
+ name: point_click
+ on: [{
+ events: {
+ source: scope
+ type: click
+ markname: point
+ }
+ update: '''kibanaSetTimeFilter(datum.key, datum.key + 3 * 60 * 60 * 1000)'''
+ }]
+ }
+ {
+ name: currentX
+ value: -1
+ on: [{
+ events: {
+ type: mousemove
+ source: view
+ },
+ update: "clamp(x(), 0, width)"
+ }, {
+ events: {
+ type: mouseout
+ source: view
+ }
+ update: "-1"
+ }]
+ }
+ {
+ name: selected
+ value: [0, 0]
+ on: [{
+ events: {
+ type: mousedown
+ source: view
+ }
+ update: "[clamp(x(), 0, width), clamp(x(), 0, width)]"
+ }, {
+ events: {
+ type: mousemove
+ source: window
+ consume: true
+ between: [{
+ type: mousedown
+ source: view
+ }, {
+ merge: [{
+ type: mouseup
+ source: window
+ }, {
+ type: keydown
+ source: window
+ filter: "event.key === 'Escape'"
+ }]
+ }]
+ }
+ update: "[selected[0], clamp(x(), 0, width)]"
+ }, {
+ events: {
+ type: keydown
+ source: window
+ filter: "event.key === 'Escape'"
+ }
+ update: "[0, 0]"
+ }]
+ }
+ {
+ name: applyTimeFilter
+ value: null
+ on: [{
+ events: {
+ type: mouseup
+ source: view
+ }
+ update: '''selected[0] !== selected[1] ? kibanaSetTimeFilter(
+ invert('x',selected[0]),
+ invert('x',selected[1])) : null'''
+ }]
+ }
+ ]
+}
+
+----
+====
+
+[[vega-reference]]
+=== Reference for {kib} extensions
+
+{kib} has extended Vega and Vega-Lite with extensions that support:
+
+* Default height and width
+* Default theme to match {kib}
+* Writing {es} queries using the time range and filters from dashboards
+* Using the Elastic Map Service in Vega maps
+* Additional tooltip styling
+* Advanced setting to enable URL loading from any domain
+* Limited debugging support using the browser dev tools
+* (Vega only) Expression functions which can update the time range and dashboard filters
-[float]
[[vega-sizing-and-positioning]]
-==== Change the layout
+==== Default height and width
By default, Vega visualizations use the `autosize = { type: 'fit', contains: 'padding' }` layout.
`fit` uses all available space, ignores `width` and `height` values,
and respects the padding values. To override this behavior, change the
`autosize` value.
-[[vega-querying-elasticsearch]]
-=== Query {es}
+[[vega-theme]]
+==== Default theme to match {kib}
+
+{kib} registers a default https://vega.github.io/vega/docs/schemes/[Vega color scheme]
+with the id `elastic`, and sets a default color for each `mark` type.
+Override it by providing a different `stroke`, `fill`, or `color` (Vega-Lite) value.
+
+[[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`.
-experimental[] Vega https://vega.github.io/vega/docs/data/[data] elements
-use embedded and external data with a `"url"` parameter. {kib} adds support for
-direct {es} queries by overloading
-the `"url"` value.
+Because of this, {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.
-NOTE: With Vega, you dynamically load your data by setting signals as data URLs.
-Since {kib} is unable to support dynamically loaded data, 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
+the object looking for special tokens that allow your query to integrate with {kib}.
+These tokens are:
-For example, count the number of documents in all indices:
+* `%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
+* `{%timefilter%: true}`: Replaced by an {es} range query with upper and lower bounds
+* `{%timefilter%: "min" | "max"}`: Replaced only by the upper or lower bounds
+* `{%timefilter: true, shift: -1, unit: 'hour'}`: Generates a time range query one hour in the past
+* `{%autointerval%: true}`: Replaced by the string which contains the automatic {kib} time interval, such as `1h`
+* `{%autointerval%: 10}`: Replaced by a string which is approximately dividing the time into 10 ranges, allowing
+ you to influence the automatic interval
+* `"%dashboard_context-must_clause%"`: String replaced by object containing filters
+* `"%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:
[source,yaml]
----
@@ -80,8 +1296,8 @@ url: {
%context%: true
// Which indexes to search
- index: _all
- // The body element may contain "aggs" and "query" subfields
+ index: kibana_sample_data_logs
+ // The body element may contain "aggs" and "query" keys
body: {
aggs: {
time_buckets: {
@@ -183,7 +1399,7 @@ except that the time range is shifted back by 10 minutes:
}
----
-NOTE: When using `"%context%": true` or defining a value for "%timefield%"` the body cannot contain a query. To customize the query within the VEGA specification (e.g. add an additional filter, or shift the timefilter), define your query and use the placeholders as in the example above. The placeholders will be replaced by the actual context of the dashboard or visualization once parsed.
+NOTE: When using `"%context%": true` or defining a value for `"%timefield%"` the body cannot contain a query. To customize the query within the VEGA specification (e.g. add an additional filter, or shift the timefilter), define your query and use the placeholders as in the example above. The placeholders will be replaced by the actual context of the dashboard or visualization once parsed.
The `"%timefilter%"` can also be used to specify a single min or max
value. The date_histogram's `extended_bounds` can be set
@@ -194,6 +1410,7 @@ also supported. The `"interval"` can also be set dynamically, depending
on the currently picked range: `"interval": {"%autointerval%": 10}` will
try to get about 10-15 data points (buckets).
+[float]
[[vega-esmfiles]]
=== Access Elastic Map Service files
@@ -260,21 +1477,44 @@ Additionally, you can use `latitude`, `longitude`, and `zoom` signals.
These signals can be used in the graph, or can be updated to modify the
position of the map.
-Vega visualization ignore the `autosize`, `width`, `height`, and `padding`
-values, using `fit` model with zero padding.
+[float]
+[[vega-tooltip]]
+==== Additional tooltip styling
+
+{kib} has installed the https://vega.github.io/vega-lite/docs/tooltip.html[Vega tooltip plugin],
+so tooltips can be defined in the ways documented there. Beyond that, {kib} also supports
+a configuration option for changing the tooltip position and padding:
+
+```js
+{
+ config: {
+ kibana: {
+ tooltips: {
+ position: 'top',
+ padding: 15
+ }
+ }
+ }
+}
+```
+
+[[vega-url-loading]]
+==== Advanced setting to enable URL loading from any domain
-[[vega-debugging]]
-=== Debugging Vega
+Vega can load data from any URL, but this is disabled by default in {kib}.
+To change this, set `vis_type_vega.enableExternalUrls: true` in `kibana.yml`,
+then restart {kib}.
[[vega-browser-debugging-console]]
==== Browser debugging console
experimental[] Use browser debugging tools (for example, F12 or Ctrl+Shift+J in Chrome) to
inspect the `VEGA_DEBUG` variable:
-+
+
* `view` — Access to the Vega View object. See https://vega.github.io/vega/docs/api/debugging/[Vega Debugging Guide]
-on how to inspect data and signals at runtime. For Vega-Lite, `VEGA_DEBUG.view.data('source_0')` gets the main data set.
-For Vega, it uses the data name as defined in your Vega spec.
+on how to inspect data and signals at runtime. For Vega-Lite,
+`VEGA_DEBUG.view.data('source_0')` gets the pre-transformed data, and `VEGA_DEBUG.view.data('data_0')`
+gets the encoded data. For Vega, it uses the data name as defined in your Vega spec.
* `vega_spec` — Vega JSON graph specification after some modifications by {kib}. In case
of Vega-Lite, this is the output of the Vega-Lite compiler.
@@ -283,7 +1523,7 @@ of Vega-Lite, this is the output of the Vega-Lite compiler.
Vega-Lite compilation.
[[vega-data]]
-==== Data
+==== Debugging data
experimental[] If you are using an {es} query, make sure your resulting data is
what you expected. The easiest way to view it is by using the "networking"
@@ -294,45 +1534,52 @@ https://www.elastic.co/guide/en/kibana/current/console-kibana.html[Dev Tools]. P
`GET /_search`, then add your query as the following lines
(just the value of the `"query"` field).
-If you need to share your graph with someone, copy the
-raw data response to https://gist.github.com/[gist.github.com], possibly
-with a `.json` extension, use the `[raw]` button, and use that url
-directly in your graph.
+[[vega-getting-help]]
+==== Asking for help with a Vega spec
-To restrict Vega from using non-ES data sources, add `vega.enableExternalUrls: false`
-to your kibana.yml file.
+Because of the dynamic nature of the data in {es}, it is hard to help you with
+Vega specs unless you can share a dataset. To do this, use the browser developer
+tools and type:
-[[vega-notes]]
-[[vega-useful-links]]
-=== Resources and examples
+`JSON.stringify(VEGA_DEBUG.vegalite_spec, null, 2)`
-experimental[] To learn more about Vega and Vega-List, refer to the resources and examples.
+Copy the response to https://gist.github.com/[gist.github.com], possibly
+with a `.json` extension, use the `[raw]` button, and share that when
+asking for help.
-==== Vega editor
-The https://vega.github.io/editor/[Vega Editor] includes examples for Vega & Vega-Lite, but does not support any
-{kib}-specific features like {es} requests and interactive base maps.
+[float]
+[[vega-expression-functions]]
+==== (Vega only) Expression functions which can update the time range and dashboard filters
-==== Vega-Lite resources
-* https://vega.github.io/vega-lite/tutorials/getting_started.html[Tutorials]
-* https://vega.github.io/vega-lite/docs/[Docs]
-* https://vega.github.io/vega-lite/examples/[Examples]
+{kib} has extended the Vega expression language with these functions:
-==== Vega resources
-* https://vega.github.io/vega/tutorials/[Tutorials]
-* https://vega.github.io/vega/docs/[Docs]
-* https://vega.github.io/vega/examples/[Examples]
+```js
+/**
+ * @param {object} query Elastic Query DSL snippet, as used in the query DSL editor
+ * @param {string} [index] as defined in Kibana, or default if missing
+ */
+kibanaAddFilter(query, index)
-TIP: When you use the examples, you may
-need to modify the "data" section to use absolute URL. For example,
-replace `"url": "data/world-110m.json"` with
-`"url": "https://vega.github.io/editor/data/world-110m.json"`.
+/**
+ * @param {object} query Elastic Query DSL snippet, as used in the query DSL editor
+ * @param {string} [index] as defined in Kibana, or default if missing
+ */
+kibanaRemoveFilter(query, index)
+
+kibanaRemoveAllFilters()
+/**
+ * Update dashboard time filter to the new values
+ * @param {number|string|Date} start
+ * @param {number|string|Date} end
+ */
+kibanaSetTimeFilter(start, end)
+```
+
+[float]
[[vega-additional-configuration-options]]
==== Additional configuration options
-These options are specific to the {kib}. link:#vega-with-a-map[Map support] has
-additional configuration options.
-
[source,yaml]
----
{
@@ -343,12 +1590,37 @@ additional configuration options.
controlsLocation: top
// Can be `vertical` or `horizontal` (default).
controlsDirection: vertical
- // If true, hides most of Vega and VegaLite warnings
+ // If true, hides most of Vega and Vega-Lite warnings
hideWarnings: true
// Vega renderer to use: `svg` or `canvas` (default)
renderer: canvas
}
}
- /* the rest of Vega code */
}
----
+
+
+[[vega-notes]]
+[[vega-useful-links]]
+=== Resources and examples
+
+experimental[] To learn more about Vega and Vega-Lite, refer to the resources and examples.
+
+==== Vega editor
+The https://vega.github.io/editor/[Vega Editor] includes examples for Vega & Vega-Lite, but does not support any
+{kib}-specific features like {es} requests and interactive base maps.
+
+==== Vega-Lite resources
+* https://vega.github.io/vega-lite/tutorials/getting_started.html[Tutorials]
+* https://vega.github.io/vega-lite/docs/[Docs]
+* https://vega.github.io/vega-lite/examples/[Examples]
+
+==== Vega resources
+* https://vega.github.io/vega/tutorials/[Tutorials]
+* https://vega.github.io/vega/docs/[Docs]
+* https://vega.github.io/vega/examples/[Examples]
+
+TIP: When you use the examples in {kib}, you may
+need to modify the "data" section to use absolute URL. For example,
+replace `"url": "data/world-110m.json"` with
+`"url": "https://vega.github.io/editor/data/world-110m.json"`.
diff --git a/examples/README.md b/examples/README.asciidoc
similarity index 68%
rename from examples/README.md
rename to examples/README.asciidoc
index 2b214a8d1eb52..d33c5e825ce12 100644
--- a/examples/README.md
+++ b/examples/README.asciidoc
@@ -1,7 +1,9 @@
-## Example plugins
+[[example-plugins]]
+== Example plugins
This folder contains example plugins. To run the plugins in this folder, use the `--run-examples` flag, via
-```
+[source,bash]
+----
yarn start --run-examples
-```
+----
diff --git a/examples/routing_example/README.md b/examples/routing_example/README.md
new file mode 100644
index 0000000000000..0a88707bf70bb
--- /dev/null
+++ b/examples/routing_example/README.md
@@ -0,0 +1,9 @@
+Team owner: Platform
+
+A working example of a plugin that registers and uses multiple custom routes.
+
+Read more:
+
+- [IRouter API Docs](../../docs/development/core/server/kibana-plugin-core-server.irouter.md)
+- [HttpHandler (core.http.fetch) API Docs](../../docs/development/core/public/kibana-plugin-core-public.httphandler.md)
+- [Routing Conventions](../../STYLEGUIDE.md#api-endpoints)
\ No newline at end of file
diff --git a/examples/routing_example/common/index.ts b/examples/routing_example/common/index.ts
new file mode 100644
index 0000000000000..5aa47b1f69cdf
--- /dev/null
+++ b/examples/routing_example/common/index.ts
@@ -0,0 +1,27 @@
+/*
+ * 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 RANDOM_NUMBER_ROUTE_PATH = '/api/random_number';
+
+export const RANDOM_NUMBER_BETWEEN_ROUTE_PATH = '/api/random_number_between';
+
+export const POST_MESSAGE_ROUTE_PATH = '/api/post_message';
+
+// Internal APIs should use the `internal` prefix, instead of the `api` prefix.
+export const INTERNAL_GET_MESSAGE_BY_ID_ROUTE = '/internal/get_message';
diff --git a/examples/routing_example/kibana.json b/examples/routing_example/kibana.json
new file mode 100644
index 0000000000000..37851a0da5a85
--- /dev/null
+++ b/examples/routing_example/kibana.json
@@ -0,0 +1,9 @@
+{
+ "id": "routingExample",
+ "version": "0.0.1",
+ "kibanaVersion": "kibana",
+ "server": true,
+ "ui": true,
+ "requiredPlugins": ["developerExamples"],
+ "optionalPlugins": []
+}
diff --git a/examples/routing_example/public/app.tsx b/examples/routing_example/public/app.tsx
new file mode 100644
index 0000000000000..3b33cb33ccb01
--- /dev/null
+++ b/examples/routing_example/public/app.tsx
@@ -0,0 +1,105 @@
+/*
+ * 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 ReactDOM from 'react-dom';
+import { AppMountParameters } from 'kibana/public';
+import {
+ EuiPage,
+ EuiPageBody,
+ EuiPageContent,
+ EuiText,
+ EuiHorizontalRule,
+ EuiPageContentHeader,
+ EuiListGroup,
+} from '@elastic/eui';
+import { RandomNumberRouteExample } from './random_number_example';
+import { RandomNumberBetweenRouteExample } from './random_number_between_example';
+import { Services } from './services';
+import { PostMessageRouteExample } from './post_message_example';
+import { GetMessageRouteExample } from './get_message_example';
+
+type Props = Services;
+
+function RoutingExplorer({
+ fetchRandomNumber,
+ fetchRandomNumberBetween,
+ addSuccessToast,
+ postMessage,
+ getMessageById,
+}: Props) {
+ return (
+
+
+
+
+
+ Routing examples
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export const renderApp = (props: Props, element: AppMountParameters['element']) => {
+ ReactDOM.render(, element);
+
+ return () => ReactDOM.unmountComponentAtNode(element);
+};
diff --git a/examples/routing_example/public/get_message_example.tsx b/examples/routing_example/public/get_message_example.tsx
new file mode 100644
index 0000000000000..3c34326564d2b
--- /dev/null
+++ b/examples/routing_example/public/get_message_example.tsx
@@ -0,0 +1,96 @@
+/*
+ * 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, { useCallback } from 'react';
+import { useState } from 'react';
+import {
+ EuiText,
+ EuiButton,
+ EuiLoadingSpinner,
+ EuiFieldText,
+ EuiCallOut,
+ EuiFormRow,
+} from '@elastic/eui';
+import { HttpFetchError } from '../../../src/core/public';
+import { isError } from './is_error';
+import { Services } from './services';
+
+interface Props {
+ getMessageById: Services['getMessageById'];
+}
+
+export function GetMessageRouteExample({ getMessageById }: Props) {
+ const [error, setError] = useState();
+ const [isFetching, setIsFetching] = useState(false);
+ const [message, setMessage] = useState('');
+ const [id, setId] = useState('');
+
+ const doFetch = useCallback(async () => {
+ if (isFetching) return;
+ setIsFetching(true);
+ const response = await getMessageById(id);
+
+ if (isError(response)) {
+ setError(response);
+ setMessage('');
+ } else {
+ setError(undefined);
+ setMessage(response);
+ }
+
+ setIsFetching(false);
+ }, [isFetching, getMessageById, setMessage, id]);
+
+ return (
+
+
+ GET example with param
+
+ This examples uses a simple GET route that takes an id as a param in the route path.
+
+ setId(e.target.value)}
+ data-test-subj="routingExampleGetMessageId"
+ />
+
+
+
+ doFetch()}
+ >
+ {isFetching ? : 'Get message'}
+
+
+
+ {error !== undefined ? (
+
+ {error.message}
+
+ ) : null}
+ {message !== '' ? (
+
+ Message is:
{message}
+
+ ) : null}
+
+
+ );
+}
diff --git a/packages/kbn-babel-code-parser/src/index.js b/examples/routing_example/public/index.ts
similarity index 79%
rename from packages/kbn-babel-code-parser/src/index.js
rename to examples/routing_example/public/index.ts
index 70e4eb18068b0..2bb703e71cbef 100644
--- a/packages/kbn-babel-code-parser/src/index.js
+++ b/examples/routing_example/public/index.ts
@@ -17,6 +17,7 @@
* under the License.
*/
-export { dependenciesParseStrategy } from './strategies';
-export { dependenciesVisitorsGenerator } from './visitors';
-export { parseSingleFile, parseSingleFileSync, parseEntries } from './code_parser';
+import { PluginInitializer } from 'kibana/public';
+import { RoutingExamplePlugin } from './plugin';
+
+export const plugin: PluginInitializer<{}, {}> = () => new RoutingExamplePlugin();
diff --git a/src/dev/build/tasks/nodejs_modules/get_dependencies.js b/examples/routing_example/public/is_error.ts
similarity index 69%
rename from src/dev/build/tasks/nodejs_modules/get_dependencies.js
rename to examples/routing_example/public/is_error.ts
index 5a39448f57d14..528cca5b50d5d 100644
--- a/src/dev/build/tasks/nodejs_modules/get_dependencies.js
+++ b/examples/routing_example/public/is_error.ts
@@ -17,11 +17,8 @@
* under the License.
*/
-import { parseEntries, dependenciesParseStrategy } from '@kbn/babel-code-parser';
+import { HttpFetchError } from '../../../src/core/public';
-export async function getDependencies(cwd, entries) {
- // Return the dependencies retrieve from the
- // provided code entries (sanitized) and
- // parseStrategy (dependencies one)
- return Object.keys(await parseEntries(cwd, entries, dependenciesParseStrategy, {}));
+export function isError(error: T | HttpFetchError): error is HttpFetchError {
+ return error instanceof HttpFetchError;
}
diff --git a/examples/routing_example/public/plugin.tsx b/examples/routing_example/public/plugin.tsx
new file mode 100644
index 0000000000000..eabdd2ade05b2
--- /dev/null
+++ b/examples/routing_example/public/plugin.tsx
@@ -0,0 +1,78 @@
+/*
+ * 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 {
+ CoreStart,
+ Plugin,
+ CoreSetup,
+ AppMountParameters,
+ AppNavLinkStatus,
+} from '../../../src/core/public';
+import { DeveloperExamplesSetup } from '../../developer_examples/public';
+import { getServices } from './services';
+
+interface SetupDeps {
+ developerExamples: DeveloperExamplesSetup;
+}
+
+export class RoutingExamplePlugin implements Plugin<{}, {}, SetupDeps, {}> {
+ public setup(core: CoreSetup, { developerExamples }: SetupDeps) {
+ core.application.register({
+ id: 'routingExample',
+ title: 'Routing',
+ navLinkStatus: AppNavLinkStatus.hidden,
+ async mount(params: AppMountParameters) {
+ const [coreStart] = await core.getStartServices();
+ const startServices = getServices(coreStart);
+ const { renderApp } = await import('./app');
+ return renderApp(startServices, params.element);
+ },
+ });
+
+ developerExamples.register({
+ appId: 'routingExample',
+ title: 'Routing',
+ description: `Examples show how to use core routing and fetch services to register and query your own custom routes.`,
+ links: [
+ {
+ label: 'IRouter',
+ href:
+ 'https://github.com/elastic/kibana/blob/master/docs/development/core/server/kibana-plugin-core-server.irouter.md',
+ iconType: 'logoGithub',
+ target: '_blank',
+ size: 's',
+ },
+ {
+ label: 'HttpHandler (core.http.fetch)',
+ href:
+ 'https://github.com/elastic/kibana/blob/master/docs/development/core/public/kibana-plugin-core-public.httphandler.md',
+ iconType: 'logoGithub',
+ target: '_blank',
+ size: 's',
+ },
+ ],
+ });
+ return {};
+ }
+
+ public start(core: CoreStart) {
+ return {};
+ }
+
+ public stop() {}
+}
diff --git a/examples/routing_example/public/post_message_example.tsx b/examples/routing_example/public/post_message_example.tsx
new file mode 100644
index 0000000000000..3004d66c4aa97
--- /dev/null
+++ b/examples/routing_example/public/post_message_example.tsx
@@ -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 React, { useCallback } from 'react';
+import { useState } from 'react';
+import {
+ EuiText,
+ EuiButton,
+ EuiLoadingSpinner,
+ EuiFieldText,
+ EuiCallOut,
+ EuiFormRow,
+ EuiTextArea,
+} from '@elastic/eui';
+import { HttpFetchError } from '../../../src/core/public';
+import { isError } from './is_error';
+import { Services } from './services';
+
+interface Props {
+ postMessage: Services['postMessage'];
+ addSuccessToast: Services['addSuccessToast'];
+}
+
+export function PostMessageRouteExample({ postMessage, addSuccessToast }: Props) {
+ const [error, setError] = useState();
+ const [isPosting, setIsPosting] = useState(false);
+ const [message, setMessage] = useState('');
+ const [id, setId] = useState('');
+
+ const doFetch = useCallback(async () => {
+ if (isPosting) return;
+ setIsPosting(true);
+ const response = await postMessage(message, id);
+
+ if (response && isError(response)) {
+ setError(response);
+ } else {
+ setError(undefined);
+ addSuccessToast('Message was added!');
+ setMessage('');
+ setId('');
+ }
+
+ setIsPosting(false);
+ }, [isPosting, postMessage, addSuccessToast, setMessage, message, id]);
+
+ return (
+
+
+ POST example with body
+
+ This examples uses a simple POST route that takes a body parameter and an id as a param in
+ the route path.
+
+
+ setId(e.target.value)}
+ data-test-subj="routingExampleSetMessageId"
+ />
+
+
+ setMessage(e.target.value)}
+ />
+
+
+
+ doFetch()}
+ >
+ {isPosting ? : 'Post message'}
+
+
+
+ {error !== undefined ? (
+
+ {error.message}
+
+ ) : null}
+
+
+ );
+}
diff --git a/examples/routing_example/public/random_number_between_example.tsx b/examples/routing_example/public/random_number_between_example.tsx
new file mode 100644
index 0000000000000..9f75060193114
--- /dev/null
+++ b/examples/routing_example/public/random_number_between_example.tsx
@@ -0,0 +1,98 @@
+/*
+ * 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, { useCallback } from 'react';
+import { useState } from 'react';
+import {
+ EuiText,
+ EuiButton,
+ EuiLoadingSpinner,
+ EuiFieldText,
+ EuiCallOut,
+ EuiFormRow,
+} from '@elastic/eui';
+import { HttpFetchError } from '../../../src/core/public';
+import { isError } from './is_error';
+import { Services } from './services';
+
+interface Props {
+ fetchRandomNumberBetween: Services['fetchRandomNumberBetween'];
+}
+
+export function RandomNumberBetweenRouteExample({ fetchRandomNumberBetween }: Props) {
+ const [error, setError] = useState();
+ const [randomNumber, setRandomNumber] = useState(0);
+ const [isFetching, setIsFetching] = useState(false);
+ const [maxInput, setMaxInput] = useState('10');
+
+ const doFetch = useCallback(async () => {
+ if (isFetching) return;
+ setIsFetching(true);
+ const response = await fetchRandomNumberBetween(Number.parseInt(maxInput, 10));
+
+ if (isError(response)) {
+ setError(response);
+ } else {
+ setRandomNumber(response);
+ }
+
+ setIsFetching(false);
+ }, [isFetching, maxInput, fetchRandomNumberBetween]);
+
+ return (
+
+
+ GET example with query
+
+ This examples uses a simple GET route that takes a query parameter in the request and
+ returns a single number.
+
+
+ setMaxInput(e.target.value)}
+ isInvalid={isNaN(Number(maxInput))}
+ />
+
+
+
+ doFetch()}
+ >
+ {isFetching ? : 'Generate random number'}
+
+
+
+ {error !== undefined ? (
+
+ {error.message}
+
+ ) : null}
+ {randomNumber > -1 ? (
+
+ Random number is
+
{randomNumber}
+
+ ) : null}
+
+
+ );
+}
diff --git a/examples/routing_example/public/random_number_example.tsx b/examples/routing_example/public/random_number_example.tsx
new file mode 100644
index 0000000000000..6b073826c854f
--- /dev/null
+++ b/examples/routing_example/public/random_number_example.tsx
@@ -0,0 +1,78 @@
+/*
+ * 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, { useCallback } from 'react';
+import { useState } from 'react';
+import { EuiText, EuiButton, EuiLoadingSpinner, EuiCallOut } from '@elastic/eui';
+import { HttpFetchError } from '../../../src/core/public';
+import { Services } from './services';
+import { isError } from './is_error';
+
+interface Props {
+ fetchRandomNumber: Services['fetchRandomNumber'];
+}
+
+export function RandomNumberRouteExample({ fetchRandomNumber }: Props) {
+ const [error, setError] = useState(undefined);
+ const [randomNumber, setRandomNumber] = useState(0);
+ const [isFetching, setIsFetching] = useState(false);
+
+ const doFetch = useCallback(async () => {
+ if (isFetching) return;
+ setIsFetching(true);
+ const response = await fetchRandomNumber();
+
+ if (isError(response)) {
+ setError(response);
+ } else {
+ setRandomNumber(response);
+ }
+
+ setIsFetching(false);
+ }, [isFetching, fetchRandomNumber]);
+
+ return (
+
+
+ GET example
+
+ This examples uses a simple GET route that takes no parameters or body in the request and
+ returns a single number.
+
+ doFetch()}
+ >
+ {isFetching ? : 'Generate a random number'}
+
+
+ {error !== undefined ? (
+
+ {error}
+
+ ) : null}
+ {randomNumber > -1 ? (
+
+ Random number is
{randomNumber}
+
+ ) : null}
+
+
+ );
+}
diff --git a/examples/routing_example/public/services.ts b/examples/routing_example/public/services.ts
new file mode 100644
index 0000000000000..08a79270372fd
--- /dev/null
+++ b/examples/routing_example/public/services.ts
@@ -0,0 +1,78 @@
+/*
+ * 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 { CoreStart, HttpFetchError } from 'kibana/public';
+import {
+ RANDOM_NUMBER_ROUTE_PATH,
+ RANDOM_NUMBER_BETWEEN_ROUTE_PATH,
+ POST_MESSAGE_ROUTE_PATH,
+ INTERNAL_GET_MESSAGE_BY_ID_ROUTE,
+} from '../common';
+
+export interface Services {
+ fetchRandomNumber: () => Promise;
+ fetchRandomNumberBetween: (max: number) => Promise;
+ postMessage: (message: string, id: string) => Promise;
+ getMessageById: (id: string) => Promise;
+ addSuccessToast: (message: string) => void;
+}
+
+export function getServices(core: CoreStart): Services {
+ return {
+ addSuccessToast: (message: string) => core.notifications.toasts.addSuccess(message),
+ fetchRandomNumber: async () => {
+ try {
+ const response = await core.http.fetch<{ randomNumber: number }>(RANDOM_NUMBER_ROUTE_PATH);
+ return response.randomNumber;
+ } catch (e) {
+ return e;
+ }
+ },
+ fetchRandomNumberBetween: async (max: number) => {
+ try {
+ const response = await core.http.fetch<{ randomNumber: number }>(
+ RANDOM_NUMBER_BETWEEN_ROUTE_PATH,
+ { query: { max } }
+ );
+ return response.randomNumber;
+ } catch (e) {
+ return e;
+ }
+ },
+ postMessage: async (message: string, id: string) => {
+ try {
+ await core.http.post(`${POST_MESSAGE_ROUTE_PATH}/${id}`, {
+ body: JSON.stringify({ message }),
+ });
+ } catch (e) {
+ return e;
+ }
+ },
+ getMessageById: async (id: string) => {
+ try {
+ const response = await core.http.get<{ message: string }>(
+ `${INTERNAL_GET_MESSAGE_BY_ID_ROUTE}/${id}`
+ );
+ return response.message;
+ } catch (e) {
+ return e;
+ }
+ },
+ };
+}
diff --git a/examples/routing_example/server/index.ts b/examples/routing_example/server/index.ts
new file mode 100644
index 0000000000000..77a0d9bb95549
--- /dev/null
+++ b/examples/routing_example/server/index.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.
+ */
+
+import { PluginInitializer } from 'kibana/server';
+
+import { RoutingExamplePlugin } from './plugin';
+
+export const plugin: PluginInitializer<{}, {}> = () => new RoutingExamplePlugin();
diff --git a/examples/routing_example/server/plugin.ts b/examples/routing_example/server/plugin.ts
new file mode 100644
index 0000000000000..8e92fafc7b30c
--- /dev/null
+++ b/examples/routing_example/server/plugin.ts
@@ -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.
+ */
+
+import { Plugin, CoreSetup, CoreStart } from 'kibana/server';
+import { registerRoutes } from './routes';
+
+export class RoutingExamplePlugin implements Plugin<{}, {}> {
+ public setup(core: CoreSetup) {
+ const router = core.http.createRouter();
+
+ registerRoutes(router);
+
+ return {};
+ }
+
+ public start(core: CoreStart) {
+ return {};
+ }
+
+ public stop() {}
+}
diff --git a/examples/routing_example/server/routes/index.ts b/examples/routing_example/server/routes/index.ts
new file mode 100644
index 0000000000000..ea575cf371bb7
--- /dev/null
+++ b/examples/routing_example/server/routes/index.ts
@@ -0,0 +1,19 @@
+/*
+ * 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 { registerRoutes } from './register_routes';
diff --git a/examples/routing_example/server/routes/message_routes.ts b/examples/routing_example/server/routes/message_routes.ts
new file mode 100644
index 0000000000000..2b4ec5e11fb4a
--- /dev/null
+++ b/examples/routing_example/server/routes/message_routes.ts
@@ -0,0 +1,90 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+import { POST_MESSAGE_ROUTE_PATH, INTERNAL_GET_MESSAGE_BY_ID_ROUTE } from '../../common';
+
+import { IRouter } from '../../../../src/core/server';
+
+/**
+ *
+ * NOTE: DON'T USE IN MEMORY DATA STRUCTURES TO STORE DATA!
+ *
+ * That won't work in a system with multiple Kibanas, which is a setup we recommend for
+ * load balancing. I'm only doing so here to simplify the routing example. In real life,
+ * Elasticsearch should be used to persist data that can be shared across multiple Kibana
+ * instances.
+ */
+
+const messages: { [key: string]: string } = {};
+
+/**
+ * @param router Pushes a message with an id onto an in memory map.
+ */
+export function registerPostMessageRoute(router: IRouter) {
+ router.post(
+ {
+ path: `${POST_MESSAGE_ROUTE_PATH}/{id}`,
+ validate: {
+ params: schema.object({
+ // This parameter name matches the one in POST_MESSAGE_ROUTE_PATH: `api/post_message/{id}`.
+ // Params are often used for ids like this.
+ id: schema.string(),
+ }),
+ body: schema.object({
+ message: schema.string({ maxLength: 100 }),
+ }),
+ },
+ },
+ async (context, request, response) => {
+ if (messages[request.params.id]) {
+ return response.badRequest({
+ body: `Message with id ${request.params.id} already exists`,
+ });
+ }
+
+ // See note above. NEVER DO THIS IN REAL CODE! Data should only be persisted in Elasticsearch.
+ messages[request.params.id] = request.body.message;
+
+ return response.ok();
+ }
+ );
+}
+
+/**
+ * @param router Returns the message with the given id from an in memory array.
+ */
+export function registerGetMessageByIdRoute(router: IRouter) {
+ router.get(
+ {
+ path: `${INTERNAL_GET_MESSAGE_BY_ID_ROUTE}/{id}`,
+ validate: {
+ params: schema.object({
+ id: schema.string(),
+ }),
+ },
+ },
+ async (context, request, response) => {
+ if (!messages[request.params.id]) {
+ return response.notFound();
+ }
+ return response.ok({ body: { message: messages[request.params.id] } });
+ }
+ );
+}
diff --git a/examples/routing_example/server/routes/random_number_between_generator.ts b/examples/routing_example/server/routes/random_number_between_generator.ts
new file mode 100644
index 0000000000000..9481578e540fe
--- /dev/null
+++ b/examples/routing_example/server/routes/random_number_between_generator.ts
@@ -0,0 +1,47 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+import { RANDOM_NUMBER_BETWEEN_ROUTE_PATH } from '../../common';
+
+import { IRouter } from '../../../../src/core/server';
+
+/**
+ *
+ * @param router Registers a get route that returns a random number between one and another number suplied by the user.
+ */
+export function registerGetRandomNumberBetweenRoute(router: IRouter) {
+ router.get(
+ {
+ path: RANDOM_NUMBER_BETWEEN_ROUTE_PATH,
+ validate: {
+ query: schema.object({
+ max: schema.number({ defaultValue: 10 }),
+ }),
+ },
+ },
+ async (context, request, response) => {
+ return response.ok({
+ body: {
+ randomNumber: Math.random() * request.query.max,
+ },
+ });
+ }
+ );
+}
diff --git a/src/optimize/dynamic_dll_plugin/dll_allowed_modules.js b/examples/routing_example/server/routes/random_number_generator.ts
similarity index 57%
rename from src/optimize/dynamic_dll_plugin/dll_allowed_modules.js
rename to examples/routing_example/server/routes/random_number_generator.ts
index 4314cebd3b7e9..2cfce45b957ae 100644
--- a/src/optimize/dynamic_dll_plugin/dll_allowed_modules.js
+++ b/examples/routing_example/server/routes/random_number_generator.ts
@@ -17,20 +17,27 @@
* under the License.
*/
-import path from 'path';
+import { RANDOM_NUMBER_ROUTE_PATH } from '../../common';
-export function notInNodeModules(checkPath) {
- return !checkPath.includes(`${path.sep}node_modules${path.sep}`);
-}
-
-export function notInNodeModulesOrWebpackShims(checkPath) {
- return notInNodeModules(checkPath) && !checkPath.includes(`${path.sep}webpackShims${path.sep}`);
-}
+import { IRouter } from '../../../../src/core/server';
-export function inPluginNodeModules(checkPath) {
- return checkPath.match(/[\/\\]plugins.*[\/\\]node_modules/);
-}
-
-export function inDllPluginPublic(checkPath) {
- return checkPath.includes(`${path.sep}dynamic_dll_plugin${path.sep}public${path.sep}`);
+/**
+ *
+ * @param router Registers a get route that returns a random number between one and ten. It has no input
+ * parameters, and returns a random number in the body.
+ */
+export function registerGetRandomNumberRoute(router: IRouter) {
+ router.get(
+ {
+ path: RANDOM_NUMBER_ROUTE_PATH,
+ validate: {},
+ },
+ async (context, request, response) => {
+ return response.ok({
+ body: {
+ randomNumber: Math.random() * 10,
+ },
+ });
+ }
+ );
}
diff --git a/examples/routing_example/server/routes/register_routes.ts b/examples/routing_example/server/routes/register_routes.ts
new file mode 100644
index 0000000000000..f556c0ed2c2fd
--- /dev/null
+++ b/examples/routing_example/server/routes/register_routes.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 { IRouter } from 'kibana/server';
+import { registerGetRandomNumberRoute } from './random_number_generator';
+import { registerGetRandomNumberBetweenRoute } from './random_number_between_generator';
+import { registerGetMessageByIdRoute, registerPostMessageRoute } from './message_routes';
+
+export function registerRoutes(router: IRouter) {
+ registerGetRandomNumberRoute(router);
+ registerGetRandomNumberBetweenRoute(router);
+ registerGetMessageByIdRoute(router);
+ registerPostMessageRoute(router);
+}
diff --git a/examples/routing_example/tsconfig.json b/examples/routing_example/tsconfig.json
new file mode 100644
index 0000000000000..9bbd9021b2e0a
--- /dev/null
+++ b/examples/routing_example/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./target",
+ "skipLibCheck": true
+ },
+ "include": [
+ "index.ts",
+ "public/**/*.ts",
+ "public/**/*.tsx",
+ "server/**/*.ts",
+ "common/**/*.ts",
+ "../../typings/**/*",
+ ],
+ "exclude": []
+}
diff --git a/examples/ui_actions_explorer/public/actions/actions.tsx b/examples/ui_actions_explorer/public/actions/actions.tsx
index 4ef8d5bf4d9c6..6d83362e998bc 100644
--- a/examples/ui_actions_explorer/public/actions/actions.tsx
+++ b/examples/ui_actions_explorer/public/actions/actions.tsx
@@ -31,7 +31,7 @@ export const ACTION_VIEW_IN_MAPS = 'ACTION_VIEW_IN_MAPS';
export const ACTION_TRAVEL_GUIDE = 'ACTION_TRAVEL_GUIDE';
export const ACTION_CALL_PHONE_NUMBER = 'ACTION_CALL_PHONE_NUMBER';
export const ACTION_EDIT_USER = 'ACTION_EDIT_USER';
-export const ACTION_PHONE_USER = 'ACTION_PHONE_USER';
+export const ACTION_TRIGGER_PHONE_USER = 'ACTION_TRIGGER_PHONE_USER';
export const ACTION_SHOWCASE_PLUGGABILITY = 'ACTION_SHOWCASE_PLUGGABILITY';
export const showcasePluggability = createAction({
@@ -120,19 +120,13 @@ export interface UserContext {
update: (user: User) => void;
}
-export const createPhoneUserAction = (getUiActionsApi: () => Promise) =>
- createAction({
- type: ACTION_PHONE_USER,
+export const createTriggerPhoneTriggerAction = (getUiActionsApi: () => Promise) =>
+ createAction({
+ type: ACTION_TRIGGER_PHONE_USER,
getDisplayName: () => 'Call phone number',
+ shouldAutoExecute: async () => true,
isCompatible: async ({ user }) => user.phone !== undefined,
execute: async ({ user }) => {
- // One option - execute the more specific action directly.
- // makePhoneCallAction.execute({ phone: user.phone });
-
- // Another option - emit the trigger and automatically get *all* the actions attached
- // to the phone number trigger.
- // TODO: we need to figure out the best way to handle these nested actions however, since
- // we don't want multiple context menu's to pop up.
if (user.phone !== undefined) {
(await getUiActionsApi()).executeTriggerActions(PHONE_TRIGGER, { phone: user.phone });
}
diff --git a/examples/ui_actions_explorer/public/plugin.tsx b/examples/ui_actions_explorer/public/plugin.tsx
index 670138b43b9c4..b28e5e7a9f692 100644
--- a/examples/ui_actions_explorer/public/plugin.tsx
+++ b/examples/ui_actions_explorer/public/plugin.tsx
@@ -23,7 +23,6 @@ import {
PHONE_TRIGGER,
USER_TRIGGER,
COUNTRY_TRIGGER,
- createPhoneUserAction,
lookUpWeatherAction,
viewInMapsAction,
createEditUserAction,
@@ -37,7 +36,8 @@ import {
ACTION_CALL_PHONE_NUMBER,
ACTION_TRAVEL_GUIDE,
ACTION_VIEW_IN_MAPS,
- ACTION_PHONE_USER,
+ ACTION_TRIGGER_PHONE_USER,
+ createTriggerPhoneTriggerAction,
} from './actions/actions';
import { DeveloperExamplesSetup } from '../../developer_examples/public';
import image from './ui_actions.png';
@@ -64,7 +64,7 @@ declare module '../../../src/plugins/ui_actions/public' {
[ACTION_CALL_PHONE_NUMBER]: PhoneContext;
[ACTION_TRAVEL_GUIDE]: CountryContext;
[ACTION_VIEW_IN_MAPS]: CountryContext;
- [ACTION_PHONE_USER]: UserContext;
+ [ACTION_TRIGGER_PHONE_USER]: UserContext;
}
}
@@ -84,7 +84,7 @@ export class UiActionsExplorerPlugin implements Plugin (await startServices)[1].uiActions)
+ createTriggerPhoneTriggerAction(async () => (await startServices)[1].uiActions)
);
deps.uiActions.addTriggerAction(
USER_TRIGGER,
diff --git a/package.json b/package.json
index 53aa6b25f190b..a22871e314bae 100644
--- a/package.json
+++ b/package.json
@@ -125,7 +125,7 @@
"@elastic/apm-rum": "^5.2.0",
"@elastic/charts": "19.8.1",
"@elastic/datemath": "5.0.3",
- "@elastic/elasticsearch": "7.8.0",
+ "@elastic/elasticsearch": "7.9.0-rc.1",
"@elastic/ems-client": "7.9.3",
"@elastic/eui": "26.3.1",
"@elastic/filesaver": "1.1.2",
@@ -137,7 +137,6 @@
"@hapi/good-squeeze": "5.2.1",
"@hapi/wreck": "^15.0.2",
"@kbn/analytics": "1.0.0",
- "@kbn/babel-code-parser": "1.0.0",
"@kbn/babel-preset": "1.0.0",
"@kbn/config-schema": "1.0.0",
"@kbn/i18n": "1.0.0",
@@ -237,7 +236,7 @@
"pug": "^2.0.4",
"query-string": "5.1.1",
"raw-loader": "3.1.0",
- "re2": "1.14.0",
+ "re2": "^1.15.4",
"react": "^16.12.0",
"react-color": "^2.13.8",
"react-dom": "^16.12.0",
diff --git a/packages/kbn-babel-code-parser/.babelrc b/packages/kbn-babel-code-parser/.babelrc
deleted file mode 100644
index 7da72d1779128..0000000000000
--- a/packages/kbn-babel-code-parser/.babelrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "presets": ["@kbn/babel-preset/node_preset"]
-}
diff --git a/packages/kbn-babel-code-parser/README.md b/packages/kbn-babel-code-parser/README.md
deleted file mode 100755
index f99d01541ee22..0000000000000
--- a/packages/kbn-babel-code-parser/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# @kbn/babel-code-parser
-
-Simple abstraction over the `@babel/parser` and the `@babel/traverse` in order
-to build a code parser on top.
-
-We have two main functions `parseSingleFile` (sync and sync version) and the
-`parseEntries` (only async version). The first one just parse one entry file
-and the second one parses recursively all the files from a list of
-start entry points.
-
-Then we have `visitors` and `strategies`. The first ones are basically the
-`visitors` to use into the ast from the `@babel/traverse`. They are the only
-way to collect info when using the `parseSingleFile`. The `strategies` are
-meant to be used with the `parseEntries` and configures the info we want
-to collect from our parsed code. After each loop, one per entry file, the
-`parseEntries` method will call the given `strategy` expecting that
-`strategy` would call the desired visitors, assemble the important
-information to collect and adds them to the final results.
-
diff --git a/packages/kbn-babel-code-parser/package.json b/packages/kbn-babel-code-parser/package.json
deleted file mode 100755
index 6f42c086ecaab..0000000000000
--- a/packages/kbn-babel-code-parser/package.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "name": "@kbn/babel-code-parser",
- "description": "babel code parser for Kibana",
- "private": true,
- "version": "1.0.0",
- "main": "./target/index.js",
- "license": "Apache-2.0",
- "repository": {
- "type": "git",
- "url": "https://github.com/elastic/kibana/tree/master/packages/kbn-babel-code-parser"
- },
- "scripts": {
- "build": "babel src --out-dir target",
- "kbn:bootstrap": "yarn build --quiet",
- "kbn:watch": "yarn build --watch"
- },
- "devDependencies": {
- "@babel/cli": "^7.10.1"
- },
- "dependencies": {
- "@kbn/babel-preset": "1.0.0",
- "@babel/parser": "^7.10.2",
- "@babel/traverse": "^7.10.1",
- "lodash": "^4.17.15"
- }
-}
diff --git a/packages/kbn-babel-code-parser/src/code_parser.js b/packages/kbn-babel-code-parser/src/code_parser.js
deleted file mode 100644
index 91927780363ac..0000000000000
--- a/packages/kbn-babel-code-parser/src/code_parser.js
+++ /dev/null
@@ -1,105 +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.
- */
-
-import { canRequire } from './can_require';
-import { readFile, readFileSync } from 'fs';
-import { extname } from 'path';
-import { promisify } from 'util';
-import * as parser from '@babel/parser';
-import traverse from '@babel/traverse';
-import * as babelParserOptions from '@kbn/babel-preset/common_babel_parser_options';
-
-const read = promisify(readFile);
-
-function _cannotParseFile(filePath) {
- return extname(filePath) !== '.js';
-}
-
-function _parseAndTraverseFileContent(fileContent, visitorsGenerator) {
- const results = [];
-
- // Parse and get the code AST
- // All the babel parser plugins
- // were enabled
- const ast = parser.parse(fileContent, babelParserOptions);
-
- // Loop through the code AST with
- // the defined visitors
- traverse(ast, visitorsGenerator(results));
-
- return results;
-}
-
-export async function parseSingleFile(filePath, visitorsGenerator) {
- // Don't parse any other files than .js ones
- if (_cannotParseFile(filePath)) {
- return [];
- }
-
- // Read the file
- const content = await read(filePath, { encoding: 'utf8' });
-
- // return the results found on parse and traverse
- // the file content with the given visitors
- return _parseAndTraverseFileContent(content, visitorsGenerator);
-}
-
-export function parseSingleFileSync(filePath, visitorsGenerator) {
- // Don't parse any other files than .js ones
- if (_cannotParseFile(filePath)) {
- return [];
- }
-
- // Read the file
- const content = readFileSync(filePath, { encoding: 'utf8' });
-
- // return the results found on parse and traverse
- // the file content with the given visitors
- return _parseAndTraverseFileContent(content, visitorsGenerator);
-}
-
-export async function parseEntries(cwd, entries, strategy, results, wasParsed = {}) {
- // Assure that we always have a cwd
- const sanitizedCwd = cwd || process.cwd();
-
- // Test each entry against canRequire function
- const entriesQueue = entries.map((entry) => canRequire(entry));
-
- while (entriesQueue.length) {
- // Get the first element in the queue as
- // select it as our current entry to parse
- const mainEntry = entriesQueue.shift();
-
- // Avoid parse the current entry if it is not valid
- // or it was already parsed
- if (typeof mainEntry !== 'string' || wasParsed[mainEntry]) {
- continue;
- }
-
- // Find new entries and adds them to the end of the queue
- entriesQueue.push(
- ...(await strategy(sanitizedCwd, parseSingleFile, mainEntry, wasParsed, results))
- );
-
- // Mark the current main entry as already parsed
- wasParsed[mainEntry] = true;
- }
-
- return results;
-}
diff --git a/packages/kbn-babel-code-parser/src/strategies.js b/packages/kbn-babel-code-parser/src/strategies.js
deleted file mode 100644
index 2369692ad434b..0000000000000
--- a/packages/kbn-babel-code-parser/src/strategies.js
+++ /dev/null
@@ -1,103 +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.
- */
-
-import { canRequire } from './can_require';
-import { dependenciesVisitorsGenerator } from './visitors';
-import { dirname, isAbsolute, resolve } from 'path';
-import { builtinModules } from 'module';
-
-export function _calculateTopLevelDependency(inputDep, outputDep = '') {
- // The path separator will be always the forward slash
- // as at this point we only have the found entries into
- // the provided source code entries where we just use it
- const pathSeparator = '/';
- const depSplitPaths = inputDep.split(pathSeparator);
- const firstPart = depSplitPaths.shift();
- const outputDepFirstArgAppend = outputDep ? pathSeparator : '';
-
- outputDep += `${outputDepFirstArgAppend}${firstPart}`;
-
- // In case our dependency isn't started by @
- // we are already done and we can return the
- // dependency value we already have
- if (firstPart.charAt(0) !== '@') {
- return outputDep;
- }
-
- // Otherwise we need to keep constructing the dependency
- // value because dependencies starting with @ points to
- // folders of dependencies. For example, in case we found
- // dependencies values with '@the-deps/a' and '@the-deps/a/b'
- // we don't want to map it to '@the-deps' but also to @'the-deps/a'
- // because inside '@the-deps' we can also have '@the-dep/b'
- return _calculateTopLevelDependency(depSplitPaths.join(pathSeparator), outputDep);
-}
-
-export async function dependenciesParseStrategy(
- cwd,
- parseSingleFile,
- mainEntry,
- wasParsed,
- results
-) {
- // Get dependencies from a single file and filter
- // out node native modules from the result
- const dependencies = (await parseSingleFile(mainEntry, dependenciesVisitorsGenerator)).filter(
- (dep) => !builtinModules.includes(dep)
- );
-
- // Return the list of all the new entries found into
- // the current mainEntry that we could use to look for
- // new dependencies
- return dependencies.reduce((filteredEntries, entry) => {
- const absEntryPath = resolve(cwd, dirname(mainEntry), entry);
-
- // NOTE: cwd for following canRequires is absEntryPath
- // because we should start looking from there
- const requiredPath = canRequire(absEntryPath, absEntryPath);
- const requiredRelativePath = canRequire(entry, absEntryPath);
-
- const isRelativeFile = !isAbsolute(entry);
- const isNodeModuleDep = isRelativeFile && !requiredPath && requiredRelativePath;
- const isNewEntry = isRelativeFile && requiredPath;
-
- // If it is a node_module add it to the results and also
- // add the resolved path for the node_module main file
- // as an entry point to look for dependencies it was
- // not already parsed
- if (isNodeModuleDep) {
- // Save the result as the top level dependency
- results[_calculateTopLevelDependency(entry)] = true;
-
- if (!wasParsed[requiredRelativePath]) {
- filteredEntries.push(requiredRelativePath);
- }
- }
-
- // If a new, not yet parsed, relative entry were found
- // add it to the list of entries to be parsed
- if (isNewEntry && !wasParsed[requiredPath]) {
- if (!wasParsed[requiredPath]) {
- filteredEntries.push(requiredPath);
- }
- }
-
- return filteredEntries;
- }, []);
-}
diff --git a/packages/kbn-babel-code-parser/src/strategies.test.js b/packages/kbn-babel-code-parser/src/strategies.test.js
deleted file mode 100644
index e61c784cdcd54..0000000000000
--- a/packages/kbn-babel-code-parser/src/strategies.test.js
+++ /dev/null
@@ -1,108 +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.
- */
-
-import { readFile } from 'fs';
-import { canRequire } from './can_require';
-import { parseSingleFile } from './code_parser';
-import { _calculateTopLevelDependency, dependenciesParseStrategy } from './strategies';
-
-jest.mock('./can_require', () => ({
- canRequire: jest.fn(),
-}));
-
-jest.mock('fs', () => ({
- readFile: jest.fn(),
-}));
-
-const mockCwd = '/tmp/project/dir/';
-
-describe('Code Parser Strategies', () => {
- it('should calculate the top level dependencies correctly', () => {
- const plainDep = 'dep1/file';
- const foldedDep = '@kbn/es/file';
- const otherFoldedDep = '@kbn/es';
-
- expect(_calculateTopLevelDependency(plainDep)).toEqual('dep1');
- expect(_calculateTopLevelDependency(foldedDep)).toEqual('@kbn/es');
- expect(_calculateTopLevelDependency(otherFoldedDep)).toEqual('@kbn/es');
- });
-
- it('should exclude native modules', async () => {
- readFile.mockImplementationOnce((path, options, cb) => {
- cb(null, `require('fs')`);
- });
-
- const results = [];
- await dependenciesParseStrategy(mockCwd, parseSingleFile, 'dep1/file.js', {}, results);
-
- expect(results.length).toBe(0);
- });
-
- it('should return a dep from_modules', async () => {
- readFile.mockImplementationOnce((path, options, cb) => {
- cb(null, `require('dep_from_node_modules')`);
- });
-
- canRequire.mockImplementation((entry, cwd) => {
- if (entry === `${cwd}dep1/dep_from_node_modules`) {
- return false;
- }
-
- if (entry === 'dep_from_node_modules') {
- return `${mockCwd}node_modules/dep_from_node_modules/index.js`;
- }
- });
-
- const results = await dependenciesParseStrategy(
- mockCwd,
- parseSingleFile,
- 'dep1/file.js',
- {},
- {}
- );
- expect(results[0]).toBe(`${mockCwd}node_modules/dep_from_node_modules/index.js`);
- });
-
- it('should return a relative dep file', async () => {
- readFile.mockImplementationOnce((path, options, cb) => {
- cb(null, `require('./relative_dep')`);
- });
-
- canRequire.mockImplementation((entry) => {
- if (entry === `${mockCwd}dep1/relative_dep`) {
- return `${entry}/index.js`;
- }
-
- return false;
- });
-
- const results = await dependenciesParseStrategy(
- mockCwd,
- parseSingleFile,
- 'dep1/file.js',
- {},
- {}
- );
- expect(results[0]).toBe(`${mockCwd}dep1/relative_dep/index.js`);
- });
-
- afterAll(() => {
- jest.clearAllMocks();
- });
-});
diff --git a/packages/kbn-babel-code-parser/src/visitors.js b/packages/kbn-babel-code-parser/src/visitors.js
deleted file mode 100644
index b159848d424fa..0000000000000
--- a/packages/kbn-babel-code-parser/src/visitors.js
+++ /dev/null
@@ -1,124 +0,0 @@
-/* eslint-disable @kbn/eslint/require-license-header */
-
-import { matches } from 'lodash';
-
-/* @notice
- *
- * This product has relied on ASTExplorer that is licensed under MIT.
- */
-export function dependenciesVisitorsGenerator(dependenciesAcc) {
- return (() => {
- // This was built with help on an ast explorer and some ESTree docs
- // like the babel parser ast spec and the main docs for the Esprima
- // which is a complete and useful docs for the ESTree spec.
- //
- // https://astexplorer.net
- // https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md
- // https://esprima.readthedocs.io/en/latest/syntax-tree-format.html
- // https://github.com/estree/estree
- return {
- // Visitors to traverse and found dependencies
- // raw values on require + require.resolve
- CallExpression: ({ node }) => {
- // AST check for require expressions
- const isRequire = (node) => {
- return matches({
- callee: {
- type: 'Identifier',
- name: 'require',
- },
- })(node);
- };
-
- // AST check for require.resolve expressions
- const isRequireResolve = (node) => {
- return matches({
- callee: {
- type: 'MemberExpression',
- object: {
- type: 'Identifier',
- name: 'require',
- },
- property: {
- type: 'Identifier',
- name: 'resolve',
- },
- },
- })(node);
- };
-
- // Get string values inside the expressions
- // whether they are require or require.resolve
- if (isRequire(node) || isRequireResolve(node)) {
- const nodeArguments = node.arguments;
- const reqArg = Array.isArray(nodeArguments) ? nodeArguments.shift() : null;
-
- if (!reqArg) {
- return;
- }
-
- if (reqArg.type === 'StringLiteral') {
- dependenciesAcc.push(reqArg.value);
- }
- }
- },
- // Visitors to traverse and found dependencies
- // raw values on import
- ImportDeclaration: ({ node }) => {
- // AST check for supported import expressions
- const isImport = (node) => {
- return matches({
- type: 'ImportDeclaration',
- source: {
- type: 'StringLiteral',
- },
- })(node);
- };
-
- // Get string values from import expressions
- if (isImport(node)) {
- const importSource = node.source;
- dependenciesAcc.push(importSource.value);
- }
- },
- // Visitors to traverse and found dependencies
- // raw values on export from
- ExportNamedDeclaration: ({ node }) => {
- // AST check for supported export from expressions
- const isExportFrom = (node) => {
- return matches({
- type: 'ExportNamedDeclaration',
- source: {
- type: 'StringLiteral',
- },
- })(node);
- };
-
- // Get string values from export from expressions
- if (isExportFrom(node)) {
- const exportFromSource = node.source;
- dependenciesAcc.push(exportFromSource.value);
- }
- },
- // Visitors to traverse and found dependencies
- // raw values on export * from
- ExportAllDeclaration: ({ node }) => {
- // AST check for supported export * from expressions
- const isExportAllFrom = (node) => {
- return matches({
- type: 'ExportAllDeclaration',
- source: {
- type: 'StringLiteral',
- },
- })(node);
- };
-
- // Get string values from export * from expressions
- if (isExportAllFrom(node)) {
- const exportAllFromSource = node.source;
- dependenciesAcc.push(exportAllFromSource.value);
- }
- },
- };
- })();
-}
diff --git a/packages/kbn-babel-code-parser/src/visitors.test.js b/packages/kbn-babel-code-parser/src/visitors.test.js
deleted file mode 100644
index d2704fa9dfb72..0000000000000
--- a/packages/kbn-babel-code-parser/src/visitors.test.js
+++ /dev/null
@@ -1,68 +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.
- */
-
-import * as parser from '@babel/parser';
-import traverse from '@babel/traverse';
-import { dependenciesVisitorsGenerator } from './visitors';
-
-const visitorsApplier = (code) => {
- const result = [];
- traverse(
- parser.parse(code, {
- sourceType: 'unambiguous',
- plugins: ['exportDefaultFrom'],
- }),
- dependenciesVisitorsGenerator(result)
- );
- return result;
-};
-
-describe('Code Parser Visitors', () => {
- it('should get values from require', () => {
- const rawCode = `/*foo*/require('dep1'); const bar = 1;`;
- const foundDeps = visitorsApplier(rawCode);
- expect(foundDeps[0] === 'dep1');
- });
-
- it('should get values from require.resolve', () => {
- const rawCode = `/*foo*/require.resolve('dep2'); const bar = 1;`;
- const foundDeps = visitorsApplier(rawCode);
- expect(foundDeps[0] === 'dep2');
- });
-
- it('should get values from import', () => {
- const rawCode = `/*foo*/import dep1 from 'dep1'; import dep2 from 'dep2';const bar = 1;`;
- const foundDeps = visitorsApplier(rawCode);
- expect(foundDeps[0] === 'dep1');
- expect(foundDeps[1] === 'dep2');
- });
-
- it('should get values from export from', () => {
- const rawCode = `/*foo*/export dep1 from 'dep1'; import dep2 from 'dep2';const bar = 1;`;
- const foundDeps = visitorsApplier(rawCode);
- expect(foundDeps[0] === 'dep1');
- });
-
- it('should get values from export * from', () => {
- const rawCode = `/*foo*/export * from 'dep1'; export dep2 from 'dep2';const bar = 1;`;
- const foundDeps = visitorsApplier(rawCode);
- expect(foundDeps[0] === 'dep1');
- expect(foundDeps[1] === 'dep2');
- });
-});
diff --git a/packages/kbn-babel-code-parser/yarn.lock b/packages/kbn-babel-code-parser/yarn.lock
deleted file mode 120000
index 3f82ebc9cdbae..0000000000000
--- a/packages/kbn-babel-code-parser/yarn.lock
+++ /dev/null
@@ -1 +0,0 @@
-../../yarn.lock
\ No newline at end of file
diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json
index d95cd1d404a1b..b307bd41bb4dd 100644
--- a/packages/kbn-dev-utils/package.json
+++ b/packages/kbn-dev-utils/package.json
@@ -17,6 +17,7 @@
"exit-hook": "^2.2.0",
"getopts": "^2.2.5",
"load-json-file": "^6.2.0",
+ "normalize-path": "^3.0.0",
"moment": "^2.24.0",
"rxjs": "^6.5.5",
"tree-kill": "^1.2.2",
diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts
index fad5f85ab5890..3e9e6238df9dc 100644
--- a/packages/kbn-dev-utils/src/index.ts
+++ b/packages/kbn-dev-utils/src/index.ts
@@ -39,3 +39,5 @@ export { KbnClient } from './kbn_client';
export * from './axios';
export * from './stdio';
export * from './ci_stats_reporter';
+export * from './plugin_list';
+export * from './simple_kibana_platform_plugin_discovery';
diff --git a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts
new file mode 100644
index 0000000000000..733b9f23a5394
--- /dev/null
+++ b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 Path from 'path';
+import Fs from 'fs';
+
+import MarkdownIt from 'markdown-it';
+import cheerio from 'cheerio';
+
+import { REPO_ROOT } from '../repo_root';
+import { simpleKibanaPlatformPluginDiscovery } from '../simple_kibana_platform_plugin_discovery';
+
+export interface Plugin {
+ id: string;
+ relativeDir?: string;
+ relativeReadmePath?: string;
+ readmeSnippet?: string;
+}
+
+export type Plugins = Plugin[];
+
+const getReadmeName = (directory: string) =>
+ Fs.readdirSync(directory).find((name) => name.toLowerCase() === 'readme.md');
+
+export const discoverPlugins = (pluginsRootDir: string): Plugins =>
+ simpleKibanaPlatformPluginDiscovery([pluginsRootDir], []).map(
+ ({ directory, manifest: { id } }): Plugin => {
+ const readmeName = getReadmeName(directory);
+
+ let relativeReadmePath: string | undefined;
+ let readmeSnippet: string | undefined;
+ if (readmeName) {
+ const readmePath = Path.resolve(directory, readmeName);
+ relativeReadmePath = Path.relative(REPO_ROOT, readmePath);
+
+ const md = new MarkdownIt();
+ const parsed = md.render(Fs.readFileSync(readmePath, 'utf8'));
+ const $ = cheerio.load(parsed);
+
+ const firstParagraph = $('p')[0];
+ if (firstParagraph) {
+ readmeSnippet = $(firstParagraph).text();
+ }
+ }
+
+ return {
+ id,
+ relativeReadmePath,
+ relativeDir: relativeReadmePath || Path.relative(REPO_ROOT, directory),
+ readmeSnippet,
+ };
+ }
+ );
diff --git a/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts b/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts
new file mode 100644
index 0000000000000..f0f799862e24e
--- /dev/null
+++ b/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts
@@ -0,0 +1,84 @@
+/*
+ * 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 Path from 'path';
+
+import normalizePath from 'normalize-path';
+
+import { REPO_ROOT } from '../repo_root';
+import { Plugins } from './discover_plugins';
+
+function* printPlugins(plugins: Plugins) {
+ for (const plugin of plugins) {
+ const path = plugin.relativeReadmePath || plugin.relativeDir;
+ yield '';
+ yield `- {kib-repo}blob/{branch}/${path}[${plugin.id}]`;
+
+ if (!plugin.relativeReadmePath || plugin.readmeSnippet) {
+ yield '';
+ yield plugin.readmeSnippet || 'WARNING: Missing README.';
+ yield '';
+ }
+ }
+}
+
+export function generatePluginList(ossPlugins: Plugins, xpackPlugins: Plugins) {
+ return `////
+
+NOTE:
+ This is an automatically generated file. Please do not edit directly. Instead, run the
+ following from within the kibana repository:
+
+ node scripts/build_plugin_list_docs
+
+ You can update the template within ${normalizePath(
+ Path.relative(REPO_ROOT, Path.resolve(__dirname, __filename))
+ )}
+
+////
+
+[[code-exploration]]
+== Exploring Kibana code
+
+The goals of our folder heirarchy are:
+
+- Easy for developers to know where to add new services, plugins and applications.
+- Easy for developers to know where to find the code from services, plugins and applications.
+- Easy to browse and understand our folder structure.
+
+To that aim, we strive to:
+
+- Avoid too many files in any given folder.
+- Choose clear, unambigious folder names.
+- Organize by domain.
+- Every folder should contain a README that describes the contents of that folder.
+
+[discrete]
+[[kibana-services-applications]]
+=== Services and Applications
+
+[discrete]
+==== src/plugins
+${Array.from(printPlugins(ossPlugins)).join('\n')}
+
+[discrete]
+==== x-pack/plugins
+${Array.from(printPlugins(xpackPlugins)).join('\n')}
+`;
+}
diff --git a/src/dev/build/tasks/nodejs_modules/index.js b/packages/kbn-dev-utils/src/plugin_list/index.ts
similarity index 90%
rename from src/dev/build/tasks/nodejs_modules/index.js
rename to packages/kbn-dev-utils/src/plugin_list/index.ts
index 1c5392662ad13..7e964f92b089b 100644
--- a/src/dev/build/tasks/nodejs_modules/index.js
+++ b/packages/kbn-dev-utils/src/plugin_list/index.ts
@@ -17,4 +17,4 @@
* under the License.
*/
-export { CleanClientModulesOnDLLTask } from './clean_client_modules_on_dll_task';
+export * from './run_plugin_list_cli';
diff --git a/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts b/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts
new file mode 100644
index 0000000000000..817534ba5b154
--- /dev/null
+++ b/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts
@@ -0,0 +1,49 @@
+/*
+ * 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 Path from 'path';
+import Fs from 'fs';
+
+import { run } from '../run';
+import { REPO_ROOT } from '../repo_root';
+
+import { discoverPlugins } from './discover_plugins';
+import { generatePluginList } from './generate_plugin_list';
+
+const OSS_PLUGIN_DIR = Path.resolve(REPO_ROOT, 'src/plugins');
+const XPACK_PLUGIN_DIR = Path.resolve(REPO_ROOT, 'x-pack/plugins');
+const OUTPUT_PATH = Path.resolve(
+ REPO_ROOT,
+ 'docs/developer/architecture/code-exploration.asciidoc'
+);
+
+export function runPluginListCli() {
+ run(async ({ log }) => {
+ log.info('looking for oss plugins');
+ const ossPlugins = discoverPlugins(OSS_PLUGIN_DIR);
+ log.success(`found ${ossPlugins.length} plugins`);
+
+ log.info('looking for x-pack plugins');
+ const xpackPlugins = discoverPlugins(XPACK_PLUGIN_DIR);
+ log.success(`found ${xpackPlugins.length} plugins`);
+
+ log.info('writing plugin list to', OUTPUT_PATH);
+ Fs.writeFileSync(OUTPUT_PATH, generatePluginList(ossPlugins, xpackPlugins));
+ });
+}
diff --git a/packages/kbn-dev-utils/src/simple_kibana_platform_plugin_discovery.ts b/packages/kbn-dev-utils/src/simple_kibana_platform_plugin_discovery.ts
new file mode 100644
index 0000000000000..c7155b2b3c51b
--- /dev/null
+++ b/packages/kbn-dev-utils/src/simple_kibana_platform_plugin_discovery.ts
@@ -0,0 +1,86 @@
+/*
+ * 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 Path from 'path';
+
+import globby from 'globby';
+import loadJsonFile from 'load-json-file';
+
+export interface KibanaPlatformPlugin {
+ readonly directory: string;
+ readonly manifestPath: string;
+ readonly manifest: {
+ id: string;
+ [key: string]: unknown;
+ };
+}
+
+/**
+ * Helper to find the new platform plugins.
+ */
+export function simpleKibanaPlatformPluginDiscovery(scanDirs: string[], paths: string[]) {
+ const patterns = Array.from(
+ new Set([
+ // find kibana.json files up to 5 levels within the scan dir
+ ...scanDirs.reduce(
+ (acc: string[], dir) => [
+ ...acc,
+ `${dir}/*/kibana.json`,
+ `${dir}/*/*/kibana.json`,
+ `${dir}/*/*/*/kibana.json`,
+ `${dir}/*/*/*/*/kibana.json`,
+ `${dir}/*/*/*/*/*/kibana.json`,
+ ],
+ []
+ ),
+ ...paths.map((path) => `${path}/kibana.json`),
+ ])
+ );
+
+ const manifestPaths = globby.sync(patterns, { absolute: true }).map((path) =>
+ // absolute paths returned from globby are using normalize or something so the path separators are `/` even on windows, Path.resolve solves this
+ Path.resolve(path)
+ );
+
+ return manifestPaths.map(
+ (manifestPath): KibanaPlatformPlugin => {
+ if (!Path.isAbsolute(manifestPath)) {
+ throw new TypeError('expected new platform manifest path to be absolute');
+ }
+
+ const manifest = loadJsonFile.sync(manifestPath);
+ if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
+ throw new TypeError('expected new platform plugin manifest to be a JSON encoded object');
+ }
+
+ if (typeof manifest.id !== 'string') {
+ throw new TypeError('expected new platform plugin manifest to have a string id');
+ }
+
+ return {
+ directory: Path.dirname(manifestPath),
+ manifestPath,
+ manifest: {
+ ...manifest,
+ id: manifest.id,
+ },
+ };
+ }
+ );
+}
diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json
index 271b4a3dc661b..f53eb694ec712 100644
--- a/packages/kbn-es/package.json
+++ b/packages/kbn-es/package.json
@@ -5,7 +5,7 @@
"license": "Apache-2.0",
"private": true,
"dependencies": {
- "@elastic/elasticsearch": "^7.4.0",
+ "@elastic/elasticsearch": "7.9.0-rc.1",
"@kbn/dev-utils": "1.0.0",
"abort-controller": "^2.0.3",
"chalk": "^2.4.2",
diff --git a/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.ts b/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.ts
index b489c53be47b9..a848d779dc9a2 100644
--- a/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.ts
+++ b/packages/kbn-optimizer/src/optimizer/kibana_platform_plugins.ts
@@ -17,10 +17,7 @@
* under the License.
*/
-import Path from 'path';
-
-import globby from 'globby';
-import loadJsonFile from 'load-json-file';
+import { simpleKibanaPlatformPluginDiscovery } from '@kbn/dev-utils';
export interface KibanaPlatformPlugin {
readonly directory: string;
@@ -30,72 +27,32 @@ export interface KibanaPlatformPlugin {
readonly extraPublicDirs: string[];
}
+const isArrayOfStrings = (input: any): input is string[] =>
+ Array.isArray(input) && input.every((p) => typeof p === 'string');
+
/**
* Helper to find the new platform plugins.
*/
export function findKibanaPlatformPlugins(scanDirs: string[], paths: string[]) {
- return globby
- .sync(
- Array.from(
- new Set([
- ...scanDirs.map(nestedScanDirPaths).reduce((dirs, current) => [...dirs, ...current], []),
- ...paths.map((path) => `${path}/kibana.json`),
- ])
- ),
- {
- absolute: true,
+ return simpleKibanaPlatformPluginDiscovery(scanDirs, paths).map(
+ ({ directory, manifestPath, manifest }): KibanaPlatformPlugin => {
+ let extraPublicDirs: string[] | undefined;
+ if (manifest.extraPublicDirs) {
+ if (!isArrayOfStrings(manifest.extraPublicDirs)) {
+ throw new TypeError(
+ 'expected new platform plugin manifest to have an array of strings `extraPublicDirs` property'
+ );
+ }
+ extraPublicDirs = manifest.extraPublicDirs;
}
- )
- .map((path) =>
- // absolute paths returned from globby are using normalize or something so the path separators are `/` even on windows, Path.resolve solves this
- readKibanaPlatformPlugin(Path.resolve(path))
- );
-}
-
-function nestedScanDirPaths(dir: string): string[] {
- // down to 5 level max
- return [
- `${dir}/*/kibana.json`,
- `${dir}/*/*/kibana.json`,
- `${dir}/*/*/*/kibana.json`,
- `${dir}/*/*/*/*/kibana.json`,
- `${dir}/*/*/*/*/*/kibana.json`,
- ];
-}
-function readKibanaPlatformPlugin(manifestPath: string): KibanaPlatformPlugin {
- if (!Path.isAbsolute(manifestPath)) {
- throw new TypeError('expected new platform manifest path to be absolute');
- }
-
- const manifest = loadJsonFile.sync(manifestPath);
- if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
- throw new TypeError('expected new platform plugin manifest to be a JSON encoded object');
- }
-
- if (typeof manifest.id !== 'string') {
- throw new TypeError('expected new platform plugin manifest to have a string id');
- }
-
- let extraPublicDirs: string[] | undefined;
- if (manifest.extraPublicDirs) {
- if (
- !Array.isArray(manifest.extraPublicDirs) ||
- !manifest.extraPublicDirs.every((p) => typeof p === 'string')
- ) {
- throw new TypeError(
- 'expected new platform plugin manifest to have an array of strings `extraPublicDirs` property'
- );
+ return {
+ directory,
+ manifestPath,
+ id: manifest.id,
+ isUiPlugin: !!manifest.ui,
+ extraPublicDirs: extraPublicDirs || [],
+ };
}
-
- extraPublicDirs = manifest.extraPublicDirs as string[];
- }
-
- return {
- directory: Path.dirname(manifestPath),
- manifestPath,
- id: manifest.id,
- isUiPlugin: !!manifest.ui,
- extraPublicDirs: extraPublicDirs || [],
- };
+ );
}
diff --git a/packages/kbn-optimizer/src/report_optimizer_stats.ts b/packages/kbn-optimizer/src/report_optimizer_stats.ts
index 2f92f3d648ab7..5057c717efcc3 100644
--- a/packages/kbn-optimizer/src/report_optimizer_stats.ts
+++ b/packages/kbn-optimizer/src/report_optimizer_stats.ts
@@ -35,6 +35,8 @@ interface Entry {
stats: Fs.Stats;
}
+const IGNORED_EXTNAME = ['.map', '.br', '.gz'];
+
const getFiles = (dir: string, parent?: string) =>
flatten(
Fs.readdirSync(dir).map((name): Entry | Entry[] => {
@@ -51,7 +53,19 @@ const getFiles = (dir: string, parent?: string) =>
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) {
return pipeClosure((update$: OptimizerUpdate$) => {
@@ -70,10 +84,7 @@ export function reportOptimizerStats(reporter: CiStatsReporter, config: Optimize
// make the cache read from the cache file since it was likely updated by the worker
bundle.cache.refresh();
- const outputFiles = getFiles(bundle.outputDir).filter(
- (file) => !(file.relPath.startsWith('.') || file.relPath.endsWith('.map'))
- );
-
+ const outputFiles = getFiles(bundle.outputDir);
const entryName = `${bundle.id}.${bundle.type}.js`;
const entry = outputFiles.find((f) => f.relPath === entryName);
if (!entry) {
diff --git a/renovate.json5 b/renovate.json5
index 1ba6dc0ff7e1b..ae32043daaf5f 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -25,7 +25,7 @@
'Team:Operations',
'renovate',
'v8.0.0',
- 'v7.9.0',
+ 'v7.10.0',
],
major: {
labels: [
@@ -33,7 +33,7 @@
'Team:Operations',
'renovate',
'v8.0.0',
- 'v7.9.0',
+ 'v7.10.0',
'renovate:major',
],
},
@@ -254,7 +254,7 @@
'Team:Operations',
'renovate',
'v8.0.0',
- 'v7.9.0',
+ 'v7.10.0',
':ml',
],
},
@@ -418,6 +418,14 @@
'@types/has-ansi',
],
},
+ {
+ groupSlug: 'he',
+ groupName: 'he related packages',
+ packageNames: [
+ 'he',
+ '@types/he',
+ ],
+ },
{
groupSlug: 'history',
groupName: 'history related packages',
diff --git a/src/optimize/dynamic_dll_plugin/index.js b/scripts/build_plugin_list_docs.js
similarity index 88%
rename from src/optimize/dynamic_dll_plugin/index.js
rename to scripts/build_plugin_list_docs.js
index 347811b5356b7..54821a1b10ee8 100644
--- a/src/optimize/dynamic_dll_plugin/index.js
+++ b/scripts/build_plugin_list_docs.js
@@ -17,5 +17,5 @@
* under the License.
*/
-export { DynamicDllPlugin } from './dynamic_dll_plugin';
-export { DllCompiler } from './dll_compiler';
+require('../src/setup_node_env/prebuilt_dev_only_entry');
+require('@kbn/dev-utils').runPluginListCli();
diff --git a/scripts/load_team_assignment.js b/scripts/load_team_assignment.js
new file mode 100644
index 0000000000000..b8f5edc833634
--- /dev/null
+++ b/scripts/load_team_assignment.js
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+require('../src/setup_node_env');
+require('../src/dev/code_coverage/ingest_coverage/team_assignment').uploadTeamAssignmentJson();
diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts
index 0662586797164..70b25cb78787a 100644
--- a/src/core/public/doc_links/doc_links_service.ts
+++ b/src/core/public/doc_links/doc_links_service.ts
@@ -111,8 +111,8 @@ export class DocLinksService {
},
kibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/index.html`,
siem: {
- guide: `${ELASTIC_WEBSITE_URL}guide/en/siem/guide/${DOC_LINK_VERSION}/index.html`,
- gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/siem/guide/${DOC_LINK_VERSION}/install-siem.html`,
+ guide: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`,
+ gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/install-siem.html`,
},
query: {
luceneQuerySyntax: `${ELASTICSEARCH_DOCS}query-dsl-query-string-query.html#query-string-syntax`,
diff --git a/src/core/server/elasticsearch/elasticsearch_service.test.ts b/src/core/server/elasticsearch/elasticsearch_service.test.ts
index b36af2a7e4671..4375f09f1ce0b 100644
--- a/src/core/server/elasticsearch/elasticsearch_service.test.ts
+++ b/src/core/server/elasticsearch/elasticsearch_service.test.ts
@@ -17,11 +17,9 @@
* under the License.
*/
-import { first } from 'rxjs/operators';
-
import { MockLegacyClusterClient, MockClusterClient } from './elasticsearch_service.test.mocks';
-
import { BehaviorSubject } from 'rxjs';
+import { first } from 'rxjs/operators';
import { Env } from '../config';
import { getEnvOptions } from '../config/__mocks__/env';
import { CoreContext } from '../core_context';
@@ -227,28 +225,34 @@ describe('#setup', () => {
});
it('esNodeVersionCompatibility$ only starts polling when subscribed to', async (done) => {
- mockLegacyClusterClientInstance.callAsInternalUser.mockRejectedValue(new Error());
+ const mockedClient = mockClusterClientInstance.asInternalUser;
+ mockedClient.nodes.info.mockImplementation(() =>
+ elasticsearchClientMock.createClientError(new Error())
+ );
const setupContract = await elasticsearchService.setup(setupDeps);
await delay(10);
- expect(mockLegacyClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(0);
+ expect(mockedClient.nodes.info).toHaveBeenCalledTimes(0);
setupContract.esNodesCompatibility$.subscribe(() => {
- expect(mockLegacyClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(1);
+ expect(mockedClient.nodes.info).toHaveBeenCalledTimes(1);
done();
});
});
it('esNodeVersionCompatibility$ stops polling when unsubscribed from', async (done) => {
- mockLegacyClusterClientInstance.callAsInternalUser.mockRejectedValue(new Error());
+ const mockedClient = mockClusterClientInstance.asInternalUser;
+ mockedClient.nodes.info.mockImplementation(() =>
+ elasticsearchClientMock.createClientError(new Error())
+ );
const setupContract = await elasticsearchService.setup(setupDeps);
- expect(mockLegacyClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(0);
+ expect(mockedClient.nodes.info).toHaveBeenCalledTimes(0);
const sub = setupContract.esNodesCompatibility$.subscribe(async () => {
sub.unsubscribe();
await delay(100);
- expect(mockLegacyClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(1);
+ expect(mockedClient.nodes.info).toHaveBeenCalledTimes(1);
done();
});
});
@@ -353,16 +357,19 @@ describe('#stop', () => {
it('stops pollEsNodeVersions even if there are active subscriptions', async (done) => {
expect.assertions(2);
- mockLegacyClusterClientInstance.callAsInternalUser.mockRejectedValue(new Error());
+ const mockedClient = mockClusterClientInstance.asInternalUser;
+ mockedClient.nodes.info.mockImplementation(() =>
+ elasticsearchClientMock.createClientError(new Error())
+ );
const setupContract = await elasticsearchService.setup(setupDeps);
setupContract.esNodesCompatibility$.subscribe(async () => {
- expect(mockLegacyClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(1);
+ expect(mockedClient.nodes.info).toHaveBeenCalledTimes(1);
await elasticsearchService.stop();
await delay(100);
- expect(mockLegacyClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(1);
+ expect(mockedClient.nodes.info).toHaveBeenCalledTimes(1);
done();
});
});
diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts
index 9b05fb9887a3b..69bf593dd5862 100644
--- a/src/core/server/elasticsearch/elasticsearch_service.ts
+++ b/src/core/server/elasticsearch/elasticsearch_service.ts
@@ -78,9 +78,10 @@ export class ElasticsearchService
this.getAuthHeaders = deps.http.getAuthHeaders;
this.legacyClient = this.createLegacyClusterClient('data', config);
+ this.client = this.createClusterClient('data', config);
const esNodesCompatibility$ = pollEsNodesVersion({
- callWithInternalUser: this.legacyClient.callAsInternalUser,
+ internalClient: this.client.asInternalUser,
log: this.log,
ignoreVersionMismatch: config.ignoreVersionMismatch,
esVersionCheckInterval: config.healthCheckDelay.asMilliseconds(),
@@ -109,7 +110,6 @@ export class ElasticsearchService
}
const config = await this.config$.pipe(first()).toPromise();
- this.client = this.createClusterClient('data', config);
const createClient = (
type: string,
@@ -120,7 +120,7 @@ export class ElasticsearchService
};
return {
- client: this.client,
+ client: this.client!,
createClient,
legacy: {
client: this.legacyClient,
@@ -133,7 +133,7 @@ export class ElasticsearchService
this.log.debug('Stopping elasticsearch service');
this.stop$.next();
if (this.client) {
- this.client.close();
+ await this.client.close();
}
if (this.legacyClient) {
this.legacyClient.close();
diff --git a/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts b/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts
index 3d1218d4a8e8b..21adac081acf7 100644
--- a/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts
+++ b/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts
@@ -18,6 +18,7 @@
*/
import { mapNodesVersionCompatibility, pollEsNodesVersion, NodesInfo } from './ensure_es_version';
import { loggingSystemMock } from '../../logging/logging_system.mock';
+import { elasticsearchClientMock } from '../client/mocks';
import { take, delay } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/testing';
import { of } from 'rxjs';
@@ -27,6 +28,9 @@ const mockLogger = mockLoggerFactory.get('mock logger');
const KIBANA_VERSION = '5.1.0';
+const createEsSuccess = elasticsearchClientMock.createClientResponse;
+const createEsError = elasticsearchClientMock.createClientError;
+
function createNodes(...versions: string[]): NodesInfo {
const nodes = {} as any;
versions
@@ -111,25 +115,34 @@ describe('mapNodesVersionCompatibility', () => {
});
describe('pollEsNodesVersion', () => {
- const callWithInternalUser = jest.fn();
+ let internalClient: ReturnType;
const getTestScheduler = () =>
new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
beforeEach(() => {
- callWithInternalUser.mockReset();
+ internalClient = elasticsearchClientMock.createInternalClient();
});
+ const nodeInfosSuccessOnce = (infos: NodesInfo) => {
+ internalClient.nodes.info.mockImplementationOnce(() => createEsSuccess(infos));
+ };
+ const nodeInfosErrorOnce = (error: any) => {
+ internalClient.nodes.info.mockImplementationOnce(() => createEsError(error));
+ };
+
it('returns iscCompatible=false and keeps polling when a poll request throws', (done) => {
expect.assertions(3);
const expectedCompatibilityResults = [false, false, true];
jest.clearAllMocks();
- callWithInternalUser.mockResolvedValueOnce(createNodes('5.1.0', '5.2.0', '5.0.0'));
- callWithInternalUser.mockRejectedValueOnce(new Error('mock request error'));
- callWithInternalUser.mockResolvedValueOnce(createNodes('5.1.0', '5.2.0', '5.1.1-Beta1'));
+
+ nodeInfosSuccessOnce(createNodes('5.1.0', '5.2.0', '5.0.0'));
+ nodeInfosErrorOnce('mock request error');
+ nodeInfosSuccessOnce(createNodes('5.1.0', '5.2.0', '5.1.1-Beta1'));
+
pollEsNodesVersion({
- callWithInternalUser,
+ internalClient,
esVersionCheckInterval: 1,
ignoreVersionMismatch: false,
kibanaVersion: KIBANA_VERSION,
@@ -148,9 +161,11 @@ describe('pollEsNodesVersion', () => {
it('returns compatibility results', (done) => {
expect.assertions(1);
const nodes = createNodes('5.1.0', '5.2.0', '5.0.0');
- callWithInternalUser.mockResolvedValueOnce(nodes);
+
+ nodeInfosSuccessOnce(nodes);
+
pollEsNodesVersion({
- callWithInternalUser,
+ internalClient,
esVersionCheckInterval: 1,
ignoreVersionMismatch: false,
kibanaVersion: KIBANA_VERSION,
@@ -168,15 +183,15 @@ describe('pollEsNodesVersion', () => {
it('only emits if the node versions changed since the previous poll', (done) => {
expect.assertions(4);
- callWithInternalUser.mockResolvedValueOnce(createNodes('5.1.0', '5.2.0', '5.0.0')); // emit
- callWithInternalUser.mockResolvedValueOnce(createNodes('5.0.0', '5.1.0', '5.2.0')); // ignore, same versions, different ordering
- callWithInternalUser.mockResolvedValueOnce(createNodes('5.1.1', '5.2.0', '5.0.0')); // emit
- callWithInternalUser.mockResolvedValueOnce(createNodes('5.1.1', '5.1.2', '5.1.3')); // emit
- callWithInternalUser.mockResolvedValueOnce(createNodes('5.1.1', '5.1.2', '5.1.3')); // ignore
- callWithInternalUser.mockResolvedValueOnce(createNodes('5.0.0', '5.1.0', '5.2.0')); // emit, different from previous version
+ nodeInfosSuccessOnce(createNodes('5.1.0', '5.2.0', '5.0.0')); // emit
+ nodeInfosSuccessOnce(createNodes('5.0.0', '5.1.0', '5.2.0')); // ignore, same versions, different ordering
+ nodeInfosSuccessOnce(createNodes('5.1.1', '5.2.0', '5.0.0')); // emit
+ nodeInfosSuccessOnce(createNodes('5.1.1', '5.1.2', '5.1.3')); // emit
+ nodeInfosSuccessOnce(createNodes('5.1.1', '5.1.2', '5.1.3')); // ignore
+ nodeInfosSuccessOnce(createNodes('5.0.0', '5.1.0', '5.2.0')); // emit, different from previous version
pollEsNodesVersion({
- callWithInternalUser,
+ internalClient,
esVersionCheckInterval: 1,
ignoreVersionMismatch: false,
kibanaVersion: KIBANA_VERSION,
@@ -192,14 +207,21 @@ describe('pollEsNodesVersion', () => {
it('starts polling immediately and then every esVersionCheckInterval', () => {
expect.assertions(1);
- callWithInternalUser.mockReturnValueOnce([createNodes('5.1.0', '5.2.0', '5.0.0')]);
- callWithInternalUser.mockReturnValueOnce([createNodes('5.1.1', '5.2.0', '5.0.0')]);
+
+ // @ts-expect-error we need to return an incompatible type to use the testScheduler here
+ internalClient.nodes.info.mockReturnValueOnce([
+ { body: createNodes('5.1.0', '5.2.0', '5.0.0') },
+ ]);
+ // @ts-expect-error we need to return an incompatible type to use the testScheduler here
+ internalClient.nodes.info.mockReturnValueOnce([
+ { body: createNodes('5.1.1', '5.2.0', '5.0.0') },
+ ]);
getTestScheduler().run(({ expectObservable }) => {
const expected = 'a 99ms (b|)';
const esNodesCompatibility$ = pollEsNodesVersion({
- callWithInternalUser,
+ internalClient,
esVersionCheckInterval: 100,
ignoreVersionMismatch: false,
kibanaVersion: KIBANA_VERSION,
@@ -227,15 +249,17 @@ describe('pollEsNodesVersion', () => {
getTestScheduler().run(({ expectObservable }) => {
const expected = '100ms a 99ms (b|)';
- callWithInternalUser.mockReturnValueOnce(
- of(createNodes('5.1.0', '5.2.0', '5.0.0')).pipe(delay(100))
+ internalClient.nodes.info.mockReturnValueOnce(
+ // @ts-expect-error we need to return an incompatible type to use the testScheduler here
+ of({ body: createNodes('5.1.0', '5.2.0', '5.0.0') }).pipe(delay(100))
);
- callWithInternalUser.mockReturnValueOnce(
- of(createNodes('5.1.1', '5.2.0', '5.0.0')).pipe(delay(100))
+ internalClient.nodes.info.mockReturnValueOnce(
+ // @ts-expect-error we need to return an incompatible type to use the testScheduler here
+ of({ body: createNodes('5.1.1', '5.2.0', '5.0.0') }).pipe(delay(100))
);
const esNodesCompatibility$ = pollEsNodesVersion({
- callWithInternalUser,
+ internalClient,
esVersionCheckInterval: 10,
ignoreVersionMismatch: false,
kibanaVersion: KIBANA_VERSION,
@@ -256,6 +280,6 @@ describe('pollEsNodesVersion', () => {
});
});
- expect(callWithInternalUser).toHaveBeenCalledTimes(2);
+ expect(internalClient.nodes.info).toHaveBeenCalledTimes(2);
});
});
diff --git a/src/core/server/elasticsearch/version_check/ensure_es_version.ts b/src/core/server/elasticsearch/version_check/ensure_es_version.ts
index dc56d982d7b4a..5f926215d167f 100644
--- a/src/core/server/elasticsearch/version_check/ensure_es_version.ts
+++ b/src/core/server/elasticsearch/version_check/ensure_es_version.ts
@@ -29,10 +29,10 @@ import {
esVersionEqualsKibana,
} from './es_kibana_version_compatability';
import { Logger } from '../../logging';
-import { LegacyAPICaller } from '../legacy';
+import type { ElasticsearchClient } from '../client';
export interface PollEsNodesVersionOptions {
- callWithInternalUser: LegacyAPICaller;
+ internalClient: ElasticsearchClient;
log: Logger;
kibanaVersion: string;
ignoreVersionMismatch: boolean;
@@ -137,7 +137,7 @@ function compareNodes(prev: NodesVersionCompatibility, curr: NodesVersionCompati
}
export const pollEsNodesVersion = ({
- callWithInternalUser,
+ internalClient,
log,
kibanaVersion,
ignoreVersionMismatch,
@@ -147,10 +147,11 @@ export const pollEsNodesVersion = ({
return timer(0, healthCheckInterval).pipe(
exhaustMap(() => {
return from(
- callWithInternalUser('nodes.info', {
- filterPath: ['nodes.*.version', 'nodes.*.http.publish_address', 'nodes.*.ip'],
+ internalClient.nodes.info({
+ filter_path: ['nodes.*.version', 'nodes.*.http.publish_address', 'nodes.*.ip'],
})
).pipe(
+ map(({ body }) => body),
catchError((_err) => {
return of({ nodes: {} });
})
diff --git a/src/dev/build/README.md b/src/dev/build/README.md
index ed8750f6fee56..460ab01794334 100644
--- a/src/dev/build/README.md
+++ b/src/dev/build/README.md
@@ -39,14 +39,3 @@ The majority of this logic is extracted from the grunt build that has existed fo
[lib/build.js]: ./lib/build.js
[build_distributables.js]: ./build_distributables.js
[../tooling_log/tooling_log.js]: ../tooling_log/tooling_log.js
-
-# Client Node Modules Cleaning
-
-We have introduced in our bundle a webpack dll for the client vendor modules in order to improve
-the optimization time both in dev and in production. As for those modules we already have the
-code into the vendors_${chunk_number}.bundle.dll.js we have decided to delete those bundled modules from the
-distributable node_modules folder. However, in order to accomplish this, we need to exclude
-every node_module used in the server side code. This logic is performed
-under `nodejs_modules/clean_client_modules_on_dll_task.js`. In case we need to add any new cli
-or any other piece of server code other than `x-pack` or `core_plugins` we'll need
-to update the globs present on `clean_client_modules_on_dll_task.js` accordingly.
diff --git a/src/dev/build/build_distributables.js b/src/dev/build/build_distributables.js
index 22a348b78dc0a..39a32fff891c2 100644
--- a/src/dev/build/build_distributables.js
+++ b/src/dev/build/build_distributables.js
@@ -22,7 +22,6 @@ import { getConfig, createRunner } from './lib';
import {
BuildKibanaPlatformPluginsTask,
BuildPackagesTask,
- CleanClientModulesOnDLLTask,
CleanEmptyFoldersTask,
CleanExtraBinScriptsTask,
CleanExtraFilesFromModulesTask,
@@ -127,7 +126,6 @@ export async function buildDistributables(options) {
await run(TranspileScssTask);
await run(BuildKibanaPlatformPluginsTask);
await run(OptimizeBuildTask);
- await run(CleanClientModulesOnDLLTask);
await run(CleanTypescriptTask);
await run(CleanExtraFilesFromModulesTask);
await run(CleanEmptyFoldersTask);
diff --git a/src/dev/build/tasks/index.js b/src/dev/build/tasks/index.js
index d96e745c10776..0a3a67313d6a4 100644
--- a/src/dev/build/tasks/index.js
+++ b/src/dev/build/tasks/index.js
@@ -30,7 +30,6 @@ export * from './create_readme_task';
export * from './install_chromium';
export * from './install_dependencies_task';
export * from './license_file_task';
-export * from './nodejs_modules';
export * from './nodejs';
export * from './notice_file_task';
export * from './optimize_task';
diff --git a/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js b/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js
deleted file mode 100644
index 05bfd3ca31a65..0000000000000
--- a/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js
+++ /dev/null
@@ -1,126 +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.
- */
-
-import {
- getDllEntries,
- cleanDllModuleFromEntryPath,
- writeEmptyFileForDllEntry,
-} from './webpack_dll';
-import { getDependencies } from './get_dependencies';
-import globby from 'globby';
-import normalizePosixPath from 'normalize-path';
-
-export const CleanClientModulesOnDLLTask = {
- description: 'Cleaning client node_modules bundled into the DLL',
-
- async run(config, log, build) {
- const baseDir = normalizePosixPath(build.resolvePath('.'));
- const kbnPkg = config.getKibanaPkg();
- const kbnPkgDependencies = (kbnPkg && kbnPkg.dependencies) || {};
- const kbnWebpackLoaders = Object.keys(kbnPkgDependencies).filter(
- (dep) => !!dep.includes('-loader')
- );
-
- // Define the entry points for the server code in order to
- // start here later looking for the server side dependencies
- const mainCodeEntries = [
- `${baseDir}/src/cli`,
- `${baseDir}/src/cli_keystore`,
- `${baseDir}/src/cli_plugin`,
- `${baseDir}/x-pack`,
- ...kbnWebpackLoaders.map((loader) => `${baseDir}/node_modules/${loader}`),
- ];
- const discoveredLegacyCorePluginEntries = await globby([
- `${baseDir}/src/legacy/core_plugins/*/index.js`,
- `!${baseDir}/src/legacy/core_plugins/**/public`,
- ]);
- const discoveredPluginEntries = await globby([
- `${baseDir}/src/plugins/*/server/index.js`,
- // Small exception to load dynamically discovered functions for timelion plugin
- `${baseDir}/src/plugins/vis_type_timelion/server/*_functions/**/*.js`,
- `!${baseDir}/src/plugins/**/public`,
- ]);
- const discoveredNewPlatformXpackPlugins = await globby([
- `${baseDir}/x-pack/plugins/*/server/index.js`,
- `!${baseDir}/x-pack/plugins/**/public`,
- ]);
-
- // Compose all the needed entries
- const serverEntries = [
- ...mainCodeEntries,
- ...discoveredLegacyCorePluginEntries,
- ...discoveredPluginEntries,
- ...discoveredNewPlatformXpackPlugins,
- ];
-
- // Get the dependencies found searching through the server
- // side code entries that were provided
- const serverDependencies = await getDependencies(baseDir, serverEntries);
-
- // This fulfill a particular exceptional case where
- // we need to keep loading a file from a node_module
- // only used in the front-end like we do when using the file-loader
- // in https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js
- //
- // manual list of exception modules
- const manualExceptionModules = ['mapbox-gl'];
-
- // consider the top modules as exceptions as the entry points
- // to look for other exceptions dependent on that one
- const manualExceptionEntries = [
- ...manualExceptionModules.map((module) => `${baseDir}/node_modules/${module}`),
- ];
-
- // dependencies for declared exception modules
- const manualExceptionModulesDependencies = await getDependencies(baseDir, [
- ...manualExceptionEntries,
- ]);
-
- // final list of manual exceptions to add
- const manualExceptions = [...manualExceptionModules, ...manualExceptionModulesDependencies];
-
- // Consider this as our whiteList for the modules we can't delete
- const whiteListedModules = [...serverDependencies, ...kbnWebpackLoaders, ...manualExceptions];
-
- // Resolve the client vendors dll manifest paths
- // excluding the runtime one
- const dllManifestPaths = await globby([
- `${baseDir}/built_assets/dlls/vendors_*.manifest.dll.json`,
- `!${baseDir}/built_assets/dlls/vendors_runtime.manifest.dll.json`,
- ]);
-
- // Get dll entries filtering out the ones
- // from any whitelisted module
- const dllEntries = await getDllEntries(dllManifestPaths, whiteListedModules, baseDir);
-
- for (const relativeEntryPath of dllEntries) {
- const entryPath = `${baseDir}/${relativeEntryPath}`;
-
- if (entryPath.endsWith('package.json')) {
- continue;
- }
-
- // Clean a module included into the dll
- // and then write a blank file for each
- // entry file present into the dll
- await cleanDllModuleFromEntryPath(log, entryPath);
- await writeEmptyFileForDllEntry(entryPath);
- }
- },
-};
diff --git a/src/dev/build/tasks/nodejs_modules/webpack_dll.js b/src/dev/build/tasks/nodejs_modules/webpack_dll.js
deleted file mode 100644
index 8de5e582c3d36..0000000000000
--- a/src/dev/build/tasks/nodejs_modules/webpack_dll.js
+++ /dev/null
@@ -1,131 +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.
- */
-
-import { deleteAll, isFileAccessible, read, write } from '../../lib';
-import { dirname, relative, resolve } from 'path';
-import pkgUp from 'pkg-up';
-import globby from 'globby';
-import normalizePosixPath from 'normalize-path';
-
-function checkDllEntryAccess(entry, baseDir = '') {
- const resolvedPath = baseDir ? resolve(baseDir, entry) : entry;
- return isFileAccessible(resolvedPath);
-}
-
-export async function getDllEntries(manifestPaths, whiteListedModules, baseDir = '') {
- // Read and parse all manifests
- const manifests = await Promise.all(
- manifestPaths.map(async (manifestPath) => JSON.parse(await read(manifestPath)))
- );
-
- // Process and group modules from all manifests
- const manifestsModules = manifests.flatMap((manifest, idx) => {
- if (!manifest || !manifest.content) {
- // It should fails because if we don't have the manifest file
- // or it is malformed something wrong is happening and we
- // should stop
- throw new Error(`The following dll manifest doesn't exists: ${manifestPaths[idx]}`);
- }
-
- const modules = Object.keys(manifest.content);
- if (!modules.length) {
- // It should fails because if we don't have any
- // module inside the client vendors dll something
- // wrong is happening and we should stop too
- throw new Error(
- `The following dll manifest is reporting an empty dll: ${manifestPaths[idx]}`
- );
- }
-
- return modules;
- });
-
- // Only includes modules who are not in the white list of modules
- // and that are node_modules
- return manifestsModules.filter((entry) => {
- const isWhiteListed = whiteListedModules.some((nonEntry) =>
- normalizePosixPath(entry).includes(`node_modules/${nonEntry}`)
- );
- const isNodeModule = entry.includes('node_modules');
-
- // NOTE: when using dynamic imports on webpack the entry paths could be created
- // with special context module (ex: lazy recursive) values over directories that are not real files
- // and only exists in runtime, so we need to check if the entry is a real file.
- // We found that problem through the issue https://github.com/elastic/kibana/issues/38481
- //
- // More info:
- // https://github.com/webpack/webpack/blob/master/examples/code-splitting-harmony/README.md
- // https://webpack.js.org/guides/dependency-management/#require-with-expression
- const isAccessible = checkDllEntryAccess(entry, baseDir);
-
- return !isWhiteListed && isNodeModule && isAccessible;
- });
-}
-
-export async function cleanDllModuleFromEntryPath(logger, entryPath) {
- const modulePkgPath = await pkgUp(entryPath);
- const modulePkg = JSON.parse(await read(modulePkgPath));
- const moduleDir = dirname(modulePkgPath);
- const normalizedModuleDir = normalizePosixPath(moduleDir);
-
- // Cancel the cleanup for this module as it
- // was already done.
- if (modulePkg.cleaned) {
- return;
- }
-
- // Clear dependencies from dll module package.json
- if (modulePkg.dependencies) {
- modulePkg.dependencies = {};
- }
-
- // Clear devDependencies from dll module package.json
- if (modulePkg.devDependencies) {
- modulePkg.devDependencies = {};
- }
-
- // Delete module contents. It will delete everything
- // excepts package.json, images and css
- //
- // NOTE: We can't use cwd option with globby
- // until the following issue gets closed
- // https://github.com/sindresorhus/globby/issues/87
- const filesToDelete = await globby([
- `${normalizedModuleDir}/**`,
- `!${normalizedModuleDir}/**/*.+(css)`,
- `!${normalizedModuleDir}/**/*.+(gif|ico|jpeg|jpg|tiff|tif|svg|png|webp)`,
- ]);
-
- await deleteAll(
- filesToDelete.filter((path) => {
- const relativePath = relative(moduleDir, path);
- return !relativePath.endsWith('package.json') || relativePath.includes('node_modules');
- })
- );
-
- // Mark this module as cleaned
- modulePkg.cleaned = true;
-
- // Rewrite modified package.json
- await write(modulePkgPath, JSON.stringify(modulePkg, null, 2));
-}
-
-export async function writeEmptyFileForDllEntry(entryPath) {
- await write(entryPath, '');
-}
diff --git a/src/dev/build/tasks/nodejs_modules/webpack_dll.test.js b/src/dev/build/tasks/nodejs_modules/webpack_dll.test.js
deleted file mode 100644
index ce305169a777b..0000000000000
--- a/src/dev/build/tasks/nodejs_modules/webpack_dll.test.js
+++ /dev/null
@@ -1,123 +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.
- */
-
-import { isFileAccessible, read } from '../../lib';
-import { getDllEntries } from './webpack_dll';
-
-jest.mock('../../lib', () => ({
- read: jest.fn(),
- isFileAccessible: jest.fn(),
-}));
-
-const manifestContentMock = JSON.stringify({
- name: 'vendors',
- content: {
- '/mock/node_modules/dep1': {},
- '/mock/node_modules/dep2': {},
- '/mock/node_modules/dep3': {},
- '/mock/tmp/dep2': {},
- },
-});
-
-const emptyManifestContentMock = JSON.stringify({
- name: 'vendors',
- content: {},
-});
-
-const noManifestMock = JSON.stringify(null);
-
-const noContentFieldManifestMock = JSON.stringify({
- name: 'vendors',
-});
-
-describe('Webpack DLL Build Tasks Utils', () => {
- it('should get dll entries correctly', async () => {
- read.mockImplementationOnce(async () => manifestContentMock);
-
- isFileAccessible.mockImplementation(() => true);
-
- const mockManifestPath = ['/mock/mock_dll_manifest.json'];
- const mockModulesWhitelist = ['dep1'];
- const dllEntries = await getDllEntries(mockManifestPath, mockModulesWhitelist);
-
- expect(dllEntries).toEqual(
- expect.arrayContaining(['/mock/node_modules/dep2', '/mock/node_modules/dep3'])
- );
- });
-
- it('should only include accessible files', async () => {
- read.mockImplementationOnce(async () => manifestContentMock);
-
- isFileAccessible.mockImplementation(() => false);
-
- const mockManifestPath = ['/mock/mock_dll_manifest.json'];
- const mockModulesWhitelist = ['dep1'];
- const dllEntries = await getDllEntries(mockManifestPath, mockModulesWhitelist);
-
- isFileAccessible.mockRestore();
-
- expect(dllEntries.length).toEqual(0);
- });
-
- it('should throw an error for no manifest file', async () => {
- read.mockImplementationOnce(async () => noManifestMock);
-
- const mockManifestPath = ['/mock/mock_dll_manifest.json'];
-
- try {
- await getDllEntries(mockManifestPath, []);
- } catch (error) {
- expect(error.message).toEqual(
- `The following dll manifest doesn't exists: /mock/mock_dll_manifest.json`
- );
- }
- });
-
- it('should throw an error for no manifest content field', async () => {
- read.mockImplementation(async () => noContentFieldManifestMock);
-
- const mockManifestPath = ['/mock/mock_dll_manifest.json'];
-
- try {
- await getDllEntries(mockManifestPath, []);
- } catch (error) {
- expect(error.message).toEqual(
- `The following dll manifest doesn't exists: /mock/mock_dll_manifest.json`
- );
- }
- });
-
- it('should throw an error for manifest file without any content', async () => {
- read.mockImplementation(async () => emptyManifestContentMock);
-
- const mockManifestPath = ['/mock/mock_dll_manifest.json'];
-
- try {
- await getDllEntries(mockManifestPath, []);
- } catch (error) {
- expect(error.message).toEqual(
- `The following dll manifest is reporting an empty dll: /mock/mock_dll_manifest.json`
- );
- }
- });
-
- afterAll(() => {
- jest.clearAllMocks();
- });
-});
diff --git a/src/dev/build/tasks/optimize_task.js b/src/dev/build/tasks/optimize_task.js
index 4034092273239..16a7537b8ac9e 100644
--- a/src/dev/build/tasks/optimize_task.js
+++ b/src/dev/build/tasks/optimize_task.js
@@ -41,7 +41,6 @@ export const OptimizeBuildTask = {
await exec(log, kibanaScript, kibanaArgs, {
cwd: build.resolvePath('.'),
env: {
- FORCE_DLL_CREATION: 'true',
KBN_CACHE_LOADER_WRITABLE: 'true',
NODE_OPTIONS: '--max-old-space-size=4096',
},
diff --git a/src/dev/build/tasks/patch_native_modules_task.js b/src/dev/build/tasks/patch_native_modules_task.js
index a10010ed5255f..c30d1fd774b55 100644
--- a/src/dev/build/tasks/patch_native_modules_task.js
+++ b/src/dev/build/tasks/patch_native_modules_task.js
@@ -26,21 +26,21 @@ const DOWNLOAD_DIRECTORY = '.native_modules';
const packages = [
{
name: 're2',
- version: '1.14.0',
+ version: '1.15.4',
destinationPath: 'node_modules/re2/build/Release/re2.node',
extractMethod: 'gunzip',
archives: {
darwin: {
- url: 'https://github.com/uhop/node-re2/releases/download/1.14.0/darwin-x64-64.gz',
- sha256: '54c8386cb7cd53895cf379522114bfe82378e300e127e58d392ddd40a77e396f',
+ url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/darwin-x64-64.gz',
+ sha256: '595c6653d796493ddb288fc0732a0d1df8560099796f55a1dd242357d96bb8d6',
},
linux: {
- url: 'https://github.com/uhop/node-re2/releases/download/1.14.0/linux-x64-64.gz',
- sha256: 'f54f059035e71a7ccb3fa201080e260c41d228d13a8247974b4bb157691b6757',
+ url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/linux-x64-64.gz',
+ sha256: 'e743587bc96314edf10c3e659c03168bc374a5cd9a6623ee99d989251e331f28',
},
win32: {
- url: 'https://github.com/uhop/node-re2/releases/download/1.14.0/win32-x64-64.gz',
- sha256: 'de708446a8b802f4634c2cfef097c2625a2811fdcd8133dfd7b7c485f966caa9',
+ url: 'https://github.com/uhop/node-re2/releases/download/1.15.4/win32-x64-64.gz',
+ sha256: 'b33de62cda24fb02dc80a19fb79977d686468ac746e97cd211059d2d4c75d529',
},
},
},
diff --git a/src/dev/ci_setup/setup.sh b/src/dev/ci_setup/setup.sh
index dc91d1cf23a37..25d2afb00cd87 100755
--- a/src/dev/ci_setup/setup.sh
+++ b/src/dev/ci_setup/setup.sh
@@ -65,3 +65,19 @@ if [ "$GIT_CHANGES" ]; then
echo -e "$GIT_CHANGES\n"
exit 1
fi
+
+###
+### rebuild plugin list to ensure it's not out of date
+###
+echo " -- building plugin list docs"
+node scripts/build_plugin_list_docs
+
+###
+### verify no git modifications
+###
+GIT_CHANGES="$(git ls-files --modified)"
+if [ "$GIT_CHANGES" ]; then
+ echo -e "\n${RED}ERROR: 'node scripts/build_plugin_list_docs' caused changes to the following files:${C_RESET}\n"
+ echo -e "$GIT_CHANGES\n"
+ exit 1
+fi
diff --git a/src/dev/ci_setup/setup_env.sh b/src/dev/ci_setup/setup_env.sh
index 343ff47199375..86927b694679a 100644
--- a/src/dev/ci_setup/setup_env.sh
+++ b/src/dev/ci_setup/setup_env.sh
@@ -126,6 +126,7 @@ export PATH="$PATH:$yarnGlobalDir"
# use a proxy to fetch chromedriver/geckodriver asset
export GECKODRIVER_CDNURL="https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache"
export CHROMEDRIVER_CDNURL="https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache"
+export RE2_DOWNLOAD_MIRROR="https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache"
export CYPRESS_DOWNLOAD_MIRROR="https://us-central1-elastic-kibana-184716.cloudfunctions.net/kibana-ci-proxy-cache/cypress"
export CHECKS_REPORTER_ACTIVE=false
diff --git a/src/dev/code_coverage/docs/team_assignment/README.md b/src/dev/code_coverage/docs/team_assignment/README.md
new file mode 100644
index 0000000000000..3509fb5c2a4fc
--- /dev/null
+++ b/src/dev/code_coverage/docs/team_assignment/README.md
@@ -0,0 +1,8 @@
+# Team Assignment Ingestion Pipeline
+
+Team assignment will occur once per ci run.
+Team assignment uses an ingest pipeline.
+
+The coverage user has the coverage admin role.
+
+This role must have the rights depicted below ![Cluster Rights](./security_privleges.png)
diff --git a/src/dev/code_coverage/docs/team_assignment/security_privleges.png b/src/dev/code_coverage/docs/team_assignment/security_privleges.png
new file mode 100644
index 0000000000000..002774f3ecefa
Binary files /dev/null and b/src/dev/code_coverage/docs/team_assignment/security_privleges.png differ
diff --git a/src/dev/code_coverage/ingest_coverage/constants.js b/src/dev/code_coverage/ingest_coverage/constants.js
index ddee7106f4490..ae3079afd911d 100644
--- a/src/dev/code_coverage/ingest_coverage/constants.js
+++ b/src/dev/code_coverage/ingest_coverage/constants.js
@@ -32,3 +32,4 @@ export const TEAM_ASSIGNMENT_PIPELINE_NAME = process.env.PIPELINE_NAME || 'team_
export const CODE_COVERAGE_CI_JOB_NAME = 'elastic+kibana+code-coverage';
export const RESEARCH_CI_JOB_NAME = 'elastic+kibana+qa-research';
export const CI_JOB_NAME = process.env.COVERAGE_JOB_NAME || RESEARCH_CI_JOB_NAME;
+export const RESEARCH_CLUSTER_ES_HOST = process.env.ES_HOST || 'http://localhost:9200';
diff --git a/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js b/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js
index 95056d9f0d8d7..ba73922ec508a 100644
--- a/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js
+++ b/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js
@@ -69,96 +69,4 @@ describe('Ingesting coverage', () => {
expect(folderStructure.test(actualUrl)).ok();
});
});
- describe(`vcsInfo`, () => {
- let stdOutWithVcsInfo = '';
- describe(`without a commit msg in the vcs info file`, () => {
- beforeAll(async () => {
- const args = [
- 'scripts/ingest_coverage.js',
- '--verbose',
- '--vcsInfoPath',
- 'src/dev/code_coverage/ingest_coverage/integration_tests/mocks/VCS_INFO_missing_commit_msg.txt',
- '--path',
- ];
- const opts = [...args, resolved];
- const { stdout } = await execa(process.execPath, opts, { cwd: ROOT_DIR, env });
- stdOutWithVcsInfo = stdout;
- });
-
- it(`should be an obj w/o a commit msg`, () => {
- const commitMsgRE = /"commitMsg"/;
- expect(commitMsgRE.test(stdOutWithVcsInfo)).to.not.be.ok();
- });
- });
- describe(`including previous sha`, () => {
- let stdOutWithPrevious = '';
- beforeAll(async () => {
- const opts = [...verboseArgs, resolved];
- const { stdout } = await execa(process.execPath, opts, { cwd: ROOT_DIR, env });
- stdOutWithPrevious = stdout;
- });
-
- it(`should have a vcsCompareUrl`, () => {
- const previousCompareUrlRe = /vcsCompareUrl.+\s*.*https.+compare\/FAKE_PREVIOUS_SHA\.\.\.f07b34f6206/;
- expect(previousCompareUrlRe.test(stdOutWithPrevious)).to.be.ok();
- });
- });
- describe(`with a commit msg in the vcs info file`, () => {
- beforeAll(async () => {
- const args = [
- 'scripts/ingest_coverage.js',
- '--verbose',
- '--vcsInfoPath',
- 'src/dev/code_coverage/ingest_coverage/integration_tests/mocks/VCS_INFO.txt',
- '--path',
- ];
- const opts = [...args, resolved];
- const { stdout } = await execa(process.execPath, opts, { cwd: ROOT_DIR, env });
- stdOutWithVcsInfo = stdout;
- });
-
- it(`should be an obj w/ a commit msg`, () => {
- const commitMsgRE = /commitMsg/;
- expect(commitMsgRE.test(stdOutWithVcsInfo)).to.be.ok();
- });
- });
- });
- describe(`team assignment`, () => {
- let shouldNotHavePipelineOut = '';
- let shouldIndeedHavePipelineOut = '';
-
- const args = [
- 'scripts/ingest_coverage.js',
- '--verbose',
- '--vcsInfoPath',
- 'src/dev/code_coverage/ingest_coverage/integration_tests/mocks/VCS_INFO.txt',
- '--path',
- ];
-
- const teamAssignRE = /pipeline:/;
-
- beforeAll(async () => {
- const summaryPath = 'jest-combined/coverage-summary-just-total.json';
- const resolved = resolve(MOCKS_DIR, summaryPath);
- const opts = [...args, resolved];
- const { stdout } = await execa(process.execPath, opts, { cwd: ROOT_DIR, env });
- shouldNotHavePipelineOut = stdout;
- });
- beforeAll(async () => {
- const summaryPath = 'jest-combined/coverage-summary-manual-mix.json';
- const resolved = resolve(MOCKS_DIR, summaryPath);
- const opts = [...args, resolved];
- const { stdout } = await execa(process.execPath, opts, { cwd: ROOT_DIR, env });
- shouldIndeedHavePipelineOut = stdout;
- });
-
- it(`should not occur when going to the totals index`, () => {
- const actual = teamAssignRE.test(shouldNotHavePipelineOut);
- expect(actual).to.not.be.ok();
- });
- it(`should indeed occur when going to the coverage index`, () => {
- const actual = /ingest-pipe=>team_assignment/.test(shouldIndeedHavePipelineOut);
- expect(actual).to.be.ok();
- });
- });
});
diff --git a/src/optimize/dynamic_dll_plugin/dll_entry_template.js b/src/dev/code_coverage/ingest_coverage/team_assignment/get_data.js
similarity index 61%
rename from src/optimize/dynamic_dll_plugin/dll_entry_template.js
rename to src/dev/code_coverage/ingest_coverage/team_assignment/get_data.js
index 351bed4e591ba..d9fbf5690d8a4 100644
--- a/src/optimize/dynamic_dll_plugin/dll_entry_template.js
+++ b/src/dev/code_coverage/ingest_coverage/team_assignment/get_data.js
@@ -17,21 +17,15 @@
* under the License.
*/
-export function dllEntryTemplate(requirePaths = []) {
- return requirePaths
- .map((path) => `require('${path}');`)
- .sort()
- .join('\n');
-}
+import { readFileSync } from 'fs';
+import { resolve } from 'path';
+import { fromNullable } from '../either';
-export function dllEntryFileContentStringToArray(content = '') {
- return content.split('\n');
-}
+const ROOT = resolve(__dirname, '../../../../..');
+const resolveFromRoot = resolve.bind(null, ROOT);
+const path = `
+src/dev/code_coverage/ingest_coverage/team_assignment/ingestion_pipeline_painless.json`;
+const resolved = resolveFromRoot(path.trimStart());
+const getContents = (scriptPath) => readFileSync(scriptPath, 'utf8');
-export function dllEntryFileContentArrayToString(content = []) {
- return content.join('\n');
-}
-
-export function dllMergeAllEntryFilesContent(content = []) {
- return content.join('\n').split('\n').sort().join('\n');
-}
+export const fetch = () => fromNullable(resolved).map(getContents);
diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/index.js b/src/dev/code_coverage/ingest_coverage/team_assignment/index.js
new file mode 100644
index 0000000000000..301f7fb2dee2f
--- /dev/null
+++ b/src/dev/code_coverage/ingest_coverage/team_assignment/index.js
@@ -0,0 +1,48 @@
+/*
+ * 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 { run } from '@kbn/dev-utils';
+import { TEAM_ASSIGNMENT_PIPELINE_NAME } from '../constants';
+import { fetch } from './get_data';
+import { noop } from '../utils';
+import { update } from './update_ingest_pipeline';
+
+export const uploadTeamAssignmentJson = () => run(execute, { description });
+
+const updatePipeline = update(TEAM_ASSIGNMENT_PIPELINE_NAME);
+
+function execute({ flags, log }) {
+ if (flags.verbose) log.verbose(`### Verbose logging enabled`);
+
+ fetch().fold(noop, updatePipeline(log));
+}
+
+function description() {
+ return `
+
+Upload the latest team assignment pipeline def from src,
+to the cluster.
+
+
+Examples:
+
+node scripts/load_team_assignment.js --verbose
+
+ `;
+}
diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/ingestion_pipeline_painless.json b/src/dev/code_coverage/ingest_coverage/team_assignment/ingestion_pipeline_painless.json
new file mode 100644
index 0000000000000..18e88b47ec887
--- /dev/null
+++ b/src/dev/code_coverage/ingest_coverage/team_assignment/ingestion_pipeline_painless.json
@@ -0,0 +1 @@
+{"description":"Kibana code coverage team assignments","processors":[{"script":{"lang":"painless","source":"\n String path = ctx.coveredFilePath; \n if (path.indexOf('src/legacy/core_plugins/kibana/') == 0) {\n\n if (path.indexOf('src/legacy/core_plugins/kibana/common/utils') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/migrations') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/dashboard/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/dev_tools/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/discover/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/home') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/home/np_ready/') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/local_application_service/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/management/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/lib') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/lib/management/saved_objects') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/routes/api/management/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/routes/api/import/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/routes/api/export/') == 0) ctx.team = 'kibana-platform';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('src/legacy/core_plugins/') == 0) {\n\n if (path.indexOf('src/legacy/core_plugins/apm_oss/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('src/legacy/core_plugins/console_legacy') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/elasticsearch') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/core_plugins/embeddable_api/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/input_control_vis') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/interpreter/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/kibana_react/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/newsfeed') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/legacy/core_plugins/region_map') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/legacy/core_plugins/status_page/public') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/testbed') == 0) ctx.team = 'kibana-platform';\n // else if (path.indexOf('src/legacy/core_plugins/tests_bundle/') == 0) ctx.team = 'kibana-platform';\n \n else if (path.indexOf('src/legacy/core_plugins/tile_map') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/legacy/core_plugins/timelion') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/ui_metric/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('src/legacy/core_plugins/vis_type_tagcloud') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/vis_type_vega') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/vis_type_vislib/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/visualizations/') == 0) ctx.team = 'kibana-app-arch';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('src/legacy/server/') == 0) {\n\n if (path.indexOf('src/legacy/server/config/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/http/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/i18n/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('src/legacy/server/index_patterns/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/server/keystore/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/legacy/server/logging/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/pid/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/legacy/server/sample_data/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/server/sass/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/legacy/server/saved_objects/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/status/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/url_shortening/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/server/utils/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/legacy/server/warnings/') == 0) ctx.team = 'kibana-operations';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('src/legacy/ui') == 0) {\n\n if (path.indexOf('src/legacy/ui/public/field_editor') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/ui/public/timefilter') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/ui/public/management') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/ui/public/state_management') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/ui/public/new_platform') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/plugin_discovery') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/chrome') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/notify') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/documentation_links') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/autoload') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/capabilities') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('src/legacy/ui/public/apm') == 0) ctx.team = 'apm-ui';\n\n } else if (path.indexOf('src/plugins/') == 0) {\n\n if (path.indexOf('src/plugins/advanced_settings/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/apm_oss/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('src/plugins/bfetch/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/charts/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/charts/public/static/color_maps') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/console/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('src/plugins/dashboard/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/data/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/dev_tools/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('src/plugins/discover/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/embeddable/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/es_ui_shared/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('src/plugins/expressions/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/home/public') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/plugins/home/server/tutorials') == 0) ctx.team = 'observability';\n else if (path.indexOf('src/plugins/home/server/services/') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/plugins/home/') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/plugins/index_pattern_management/public/service') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/index_pattern_management/public') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/input_control_vis/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/inspector/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/kibana_legacy/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/kibana_react/public/code_editor') == 0) ctx.team = 'kibana-canvas';\n else if (path.indexOf('src/plugins/kibana_react/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/kibana_utils/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/management/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/kibana_usage_collection/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('src/plugins/legacy_export/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/plugins/maps_legacy/') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/plugins/region_map/') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/plugins/tile_map/') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/plugins/timelion') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/navigation/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/newsfeed') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/plugins/saved_objects_management/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/plugins/saved_objects/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/share/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/status_page/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/plugins/telemetry') == 0) ctx.team = 'pulse';\n else if (path.indexOf('src/plugins/testbed/server/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/plugins/ui_actions/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/usage_collection/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('src/plugins/vis_default_editor') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/vis_type') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/visualizations/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/visualize/') == 0) ctx.team = 'kibana-app';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('x-pack/legacy/') == 0) {\n\n if (path.indexOf('x-pack/legacy/plugins/actions/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/legacy/plugins/alerting/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/legacy/plugins/apm/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('x-pack/legacy/plugins/beats_management/') == 0) ctx.team = 'beats';\n else if (path.indexOf('x-pack/legacy/plugins/canvas/') == 0) ctx.team = 'kibana-canvas';\n else if (path.indexOf('x-pack/legacy/plugins/cross_cluster_replication/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/dashboard_mode/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/legacy/plugins/encrypted_saved_objects/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/legacy/plugins/index_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/infra/') == 0) ctx.team = 'logs-metrics-ui';\n else if (path.indexOf('x-pack/legacy/plugins/ingest_manager/') == 0) ctx.team = 'ingest-management';\n else if (path.indexOf('x-pack/legacy/plugins/license_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/maps/') == 0) ctx.team = 'kibana-gis';\n else if (path.indexOf('x-pack/legacy/plugins/ml/') == 0) ctx.team = 'ml-ui';\n else if (path.indexOf('x-pack/legacy/plugins/monitoring/') == 0) ctx.team = 'stack-monitoring-ui';\n else if (path.indexOf('x-pack/legacy/plugins/reporting') == 0) ctx.team = 'kibana-reporting';\n else if (path.indexOf('x-pack/legacy/plugins/rollup/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/security/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/legacy/plugins/siem/') == 0) ctx.team = 'siem';\n else if (path.indexOf('x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules') == 0) ctx.team = 'security-intelligence-analytics';\n else if (path.indexOf('x-pack/legacy/plugins/snapshot_restore/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/spaces/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/legacy/plugins/task_manager') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/legacy/plugins/triggers_actions_ui/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/legacy/plugins/upgrade_assistant/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/uptime') == 0) ctx.team = 'uptime';\n else if (path.indexOf('x-pack/legacy/plugins/xpack_main/server/') == 0) ctx.team = 'kibana-platform';\n\n else if (path.indexOf('x-pack/legacy/server/lib/create_router/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/server/lib/check_license/') == 0) ctx.team = 'es-ui'; \n else if (path.indexOf('x-pack/legacy/server/lib/') == 0) ctx.team = 'kibana-platform'; \n else ctx.team = 'unknown';\n\n } else if (path.indexOf('x-pack/plugins/') == 0) {\n\n if (path.indexOf('x-pack/plugins/actions/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/advanced_ui_actions/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/alerts') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/alerting_builtins') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/apm/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('x-pack/plugins/beats_management/') == 0) ctx.team = 'beats';\n else if (path.indexOf('x-pack/plugins/canvas/') == 0) ctx.team = 'kibana-canvas';\n else if (path.indexOf('x-pack/plugins/case') == 0) ctx.team = 'siem';\n else if (path.indexOf('x-pack/plugins/cloud/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('x-pack/plugins/code/') == 0) ctx.team = 'code';\n else if (path.indexOf('x-pack/plugins/console_extensions/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/cross_cluster_replication/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/dashboard_enhanced') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/dashboard_mode') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/discover_enhanced') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/embeddable_enhanced') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/data_enhanced/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/drilldowns/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/encrypted_saved_objects/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/plugins/endpoint/') == 0) ctx.team = 'endpoint-app-team';\n else if (path.indexOf('x-pack/plugins/es_ui_shared/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/event_log/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/features/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('x-pack/plugins/file_upload') == 0) ctx.team = 'kibana-gis';\n else if (path.indexOf('x-pack/plugins/global_search') == 0) ctx.team = 'kibana-platform';\n \n else if (path.indexOf('x-pack/plugins/graph/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/grokdebugger/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/index_lifecycle_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/index_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/infra/') == 0) ctx.team = 'logs-metrics-ui';\n else if (path.indexOf('x-pack/plugins/ingest_manager/') == 0) ctx.team = 'ingest-management';\n else if (path.indexOf('x-pack/plugins/ingest_pipelines/') == 0) ctx.team = 'es-ui';\n \n else if (path.indexOf('x-pack/plugins/lens/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/license_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/licensing/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('x-pack/plugins/lists/') == 0) ctx.team = 'siem';\n else if (path.indexOf('x-pack/plugins/logstash') == 0) ctx.team = 'logstash';\n else if (path.indexOf('x-pack/plugins/maps/') == 0) ctx.team = 'kibana-gis';\n else if (path.indexOf('x-pack/plugins/maps_legacy_licensing') == 0) ctx.team = 'maps';\n else if (path.indexOf('x-pack/plugins/ml/') == 0) ctx.team = 'ml-ui';\n else if (path.indexOf('x-pack/plugins/monitoring') == 0) ctx.team = 'stack-monitoring-ui';\n else if (path.indexOf('x-pack/plugins/observability/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('x-pack/plugins/oss_telemetry/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('x-pack/plugins/painless_lab/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/remote_clusters/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/reporting') == 0) ctx.team = 'kibana-reporting';\n else if (path.indexOf('x-pack/plugins/rollup/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/searchprofiler/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/security/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/plugins/security_solution/') == 0) ctx.team = 'siem';\n \n else if (path.indexOf('x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules') == 0) ctx.team = 'security-intelligence-analytics';\n else if (path.indexOf('x-pack/plugins/siem/') == 0) ctx.team = 'siem';\n else if (path.indexOf('x-pack/plugins/snapshot_restore/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/spaces/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/plugins/task_manager/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/telemetry_collection_xpack/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('x-pack/plugins/transform/') == 0) ctx.team = 'ml-ui';\n else if (path.indexOf('x-pack/plugins/translations/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('x-pack/plugins/triggers_actions_ui/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/upgrade_assistant/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/ui_actions_enhanced') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/uptime') == 0) ctx.team = 'uptime';\n \n else if (path.indexOf('x-pack/plugins/watcher/') == 0) ctx.team = 'es-ui';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('packages') == 0) {\n\n if (path.indexOf('packages/kbn-analytics/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('packages/kbn-babel') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-config-schema/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('packages/elastic-datemath') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('packages/kbn-dev-utils') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-es/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-eslint') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-expect') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-i18n/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('packages/kbn-interpreter/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('packages/kbn-optimizer/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-pm/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-test/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-test-subj-selector/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-ui-framework/') == 0) ctx.team = 'kibana-design';\n else if (path.indexOf('packages/kbn-ui-shared-deps/') == 0) ctx.team = 'kibana-operations';\n else ctx.team = 'unknown';\n\n } else {\n\n if (path.indexOf('config/kibana.yml') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/apm.js') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('src/core/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/core/public/i18n/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('src/core/server/csp/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('src/dev/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/dev/i18n/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('src/dev/run_check_published_api_changes.ts') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/es_archiver/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/optimize/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/setup_node_env/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/test_utils/') == 0) ctx.team = 'kibana-operations'; \n else ctx.team = 'unknown';\n }"}}]}
diff --git a/packages/kbn-babel-code-parser/src/can_require.js b/src/dev/code_coverage/ingest_coverage/team_assignment/update_ingest_pipeline.js
similarity index 59%
rename from packages/kbn-babel-code-parser/src/can_require.js
rename to src/dev/code_coverage/ingest_coverage/team_assignment/update_ingest_pipeline.js
index b410611237766..03844b2a5dd32 100644
--- a/packages/kbn-babel-code-parser/src/can_require.js
+++ b/src/dev/code_coverage/ingest_coverage/team_assignment/update_ingest_pipeline.js
@@ -17,20 +17,21 @@
* under the License.
*/
-export function canRequire(entry, cwd = require.resolve.paths(entry) || []) {
+import { createFailError } from '@kbn/dev-utils';
+import { RESEARCH_CLUSTER_ES_HOST } from '../constants';
+import { pretty, green } from '../utils';
+
+const { Client } = require('@elastic/elasticsearch');
+
+const node = RESEARCH_CLUSTER_ES_HOST;
+const client = new Client({ node });
+
+export const update = (id) => (log) => async (body) => {
try {
- // We will try to test if we can resolve
- // this entry through the require.resolve
- // setting as the start looking path the
- // given cwd. That cwd variable could be
- // a path or an array of paths
- // from where Require.resolve will keep
- // looking recursively as normal starting
- // from those locations.
- return require.resolve(entry, {
- paths: [].concat(cwd),
- });
+ await client.ingest.putPipeline({ id, body });
+ log.verbose(`### Ingestion Pipeline ID: ${green(id)}`);
+ log.verbose(`### Payload Partial: \n${body.slice(0, 600)}...`);
} catch (e) {
- return false;
+ throw createFailError(`${pretty(e.meta)}`);
}
-}
+};
diff --git a/src/dev/code_coverage/shell_scripts/assign_teams.sh b/src/dev/code_coverage/shell_scripts/assign_teams.sh
new file mode 100644
index 0000000000000..186cbecb436e9
--- /dev/null
+++ b/src/dev/code_coverage/shell_scripts/assign_teams.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+echo "### Code Coverage Team Assignment"
+echo ""
+
+PIPELINE_NAME=$1
+export PIPELINE_NAME
+
+ES_HOST="https://${USER_FROM_VAULT}:${PASS_FROM_VAULT}@${HOST_FROM_VAULT}"
+export ES_HOST
+
+node scripts/load_team_assignment.js --verbose
+
+echo "### Code Coverage Team Assignment - Complete"
+echo ""
diff --git a/src/dev/notice/generate_notice_from_source.ts b/src/dev/notice/generate_notice_from_source.ts
index fb74bed0f26f4..37bbcce72e497 100644
--- a/src/dev/notice/generate_notice_from_source.ts
+++ b/src/dev/notice/generate_notice_from_source.ts
@@ -47,10 +47,11 @@ export async function generateNoticeFromSource({ productName, directory, log }:
cwd: directory,
nodir: true,
ignore: [
- '{node_modules,build,target,dist,data,built_assets}/**',
- 'packages/*/{node_modules,build,target,dist}/**',
- 'x-pack/{node_modules,build,target,dist,data}/**',
- 'x-pack/packages/*/{node_modules,build,target,dist}/**',
+ '{node_modules,build,dist,data,built_assets}/**',
+ 'packages/*/{node_modules,build,dist}/**',
+ 'x-pack/{node_modules,build,dist,data}/**',
+ 'x-pack/packages/*/{node_modules,build,dist}/**',
+ '**/target/**',
],
};
diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js
index 6b1f1dfaeabb4..929de8c6701d4 100644
--- a/src/dev/precommit_hook/casing_check_config.js
+++ b/src/dev/precommit_hook/casing_check_config.js
@@ -30,6 +30,7 @@ export const IGNORE_FILE_GLOBS = [
'docs/**/*',
'**/bin/**/*',
'**/+([A-Z_]).md',
+ '**/+([A-Z_]).asciidoc',
'**/LICENSE',
'**/*.txt',
'**/Gruntfile.js',
diff --git a/src/dev/renovate/config.ts b/src/dev/renovate/config.ts
index d868f0a89b98c..c9688fc0ae0bd 100644
--- a/src/dev/renovate/config.ts
+++ b/src/dev/renovate/config.ts
@@ -21,7 +21,7 @@ import { RENOVATE_PACKAGE_GROUPS } from './package_groups';
import { PACKAGE_GLOBS } from './package_globs';
import { wordRegExp, maybeFlatMap, maybeMap, getTypePackageName } from './utils';
-const DEFAULT_LABELS = ['release_note:skip', 'Team:Operations', 'renovate', 'v8.0.0', 'v7.9.0'];
+const DEFAULT_LABELS = ['release_note:skip', 'Team:Operations', 'renovate', 'v8.0.0', 'v7.10.0'];
export const RENOVATE_CONFIG = {
extends: ['config:base'],
diff --git a/src/es_archiver/lib/indices/create_index_stream.ts b/src/es_archiver/lib/indices/create_index_stream.ts
index df9d3bb623ad6..5629f95c7c9c6 100644
--- a/src/es_archiver/lib/indices/create_index_stream.ts
+++ b/src/es_archiver/lib/indices/create_index_stream.ts
@@ -18,6 +18,8 @@
*/
import { Transform, Readable } from 'stream';
+import { inspect } from 'util';
+
import { get, once } from 'lodash';
import { Client } from 'elasticsearch';
import { ToolingLog } from '@kbn/dev-utils';
@@ -84,6 +86,18 @@ export function createCreateIndexStream({
stats.createdIndex(index, { settings });
} catch (err) {
+ if (
+ err?.body?.error?.reason?.includes('index exists with the same name as the alias') &&
+ attemptNumber < 3
+ ) {
+ const aliasStr = inspect(aliases);
+ log.info(
+ `failed to create aliases [${aliasStr}] because ES indicated an index/alias already exists, trying again`
+ );
+ await attemptToCreate(attemptNumber + 1);
+ return;
+ }
+
if (
get(err, 'body.error.type') !== 'resource_already_exists_exception' ||
attemptNumber >= 3
diff --git a/src/legacy/core_plugins/tests_bundle/index.js b/src/legacy/core_plugins/tests_bundle/index.js
index 431c161585fe0..da7c1c4f4527e 100644
--- a/src/legacy/core_plugins/tests_bundle/index.js
+++ b/src/legacy/core_plugins/tests_bundle/index.js
@@ -108,6 +108,13 @@ export default (kibana) => {
resolve: {
extensions: ['.karma_mock.js', '.karma_mock.tsx', '.karma_mock.ts'],
},
+ node: {
+ fs: 'empty',
+ child_process: 'empty',
+ dns: 'empty',
+ net: 'empty',
+ tls: 'empty',
+ },
},
webpackConfig
);
diff --git a/src/legacy/server/logging/log_format_string.js b/src/legacy/server/logging/log_format_string.js
index 3c18aab2e3d09..cbbf71dd894ac 100644
--- a/src/legacy/server/logging/log_format_string.js
+++ b/src/legacy/server/logging/log_format_string.js
@@ -41,8 +41,6 @@ const typeColors = {
optmzr: 'white',
manager: 'green',
optimize: 'magentaBright',
- 'optimize:dynamic_dll_plugin': 'magentaBright',
- 'optimize:watch_cache': 'magentaBright',
listening: 'magentaBright',
scss: 'magentaBright',
};
diff --git a/src/legacy/server/sass/build.test.js b/src/legacy/server/sass/build.test.js
index 71c43ac010962..155c300bf3036 100644
--- a/src/legacy/server/sass/build.test.js
+++ b/src/legacy/server/sass/build.test.js
@@ -50,9 +50,6 @@ it('builds light themed SASS', async () => {
expect(readFileSync(targetPath, 'utf8').replace(/(\/\*# sourceMappingURL=).*( \*\/)/, '$1...$2'))
.toMatchInlineSnapshot(`
"foo bar {
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
display: flex;
background: #e6f0f8 url(./images/img.png) url(ui/assets/favicons/favicon.ico); }
/*# sourceMappingURL=... */"
@@ -75,9 +72,6 @@ it('builds dark themed SASS', async () => {
expect(readFileSync(targetPath, 'utf8').replace(/(\/\*# sourceMappingURL=).*( \*\/)/, '$1...$2'))
.toMatchInlineSnapshot(`
"foo bar {
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
display: flex;
background: #232635 url(./images/img.png) url(ui/assets/favicons/favicon.ico); }
/*# sourceMappingURL=... */"
@@ -104,9 +98,6 @@ it('rewrites url imports', async () => {
expect(readFileSync(targetPath, 'utf8').replace(/(\/\*# sourceMappingURL=).*( \*\/)/, '$1...$2'))
.toMatchInlineSnapshot(`
"foo bar {
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
display: flex;
background: #232635 url(__REPLACE_WITH_PUBLIC_PATH__foo/bar/images/img.png) url(__REPLACE_WITH_PUBLIC_PATH__ui/favicons/favicon.ico); }
/*# sourceMappingURL=... */"
diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js
index 168dddf0253d9..7788aeaee72e5 100644
--- a/src/legacy/ui/ui_render/ui_render_mixin.js
+++ b/src/legacy/ui/ui_render/ui_render_mixin.js
@@ -24,7 +24,6 @@ import { i18n } from '@kbn/i18n';
import * as UiSharedDeps from '@kbn/ui-shared-deps';
import { AppBootstrap } from './bootstrap';
import { getApmConfig } from '../apm';
-import { DllCompiler } from '../../../optimize/dynamic_dll_plugin';
/**
* @typedef {import('../../server/kbn_server').default} KbnServer
@@ -106,17 +105,8 @@ export function uiRenderMixin(kbnServer, server, config) {
const basePath = config.get('server.basePath');
const regularBundlePath = `${basePath}/${buildHash}/bundles`;
- const dllBundlePath = `${basePath}/${buildHash}/built_assets/dlls`;
-
- const dllStyleChunks = DllCompiler.getRawDllConfig().chunks.map(
- (chunk) => `${dllBundlePath}/vendors${chunk}.style.dll.css`
- );
- const dllJsChunks = DllCompiler.getRawDllConfig().chunks.map(
- (chunk) => `${dllBundlePath}/vendors${chunk}.bundle.dll.js`
- );
const styleSheetPaths = [
- ...(isCore ? [] : dllStyleChunks),
`${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`,
...(darkMode
? [
@@ -173,7 +163,6 @@ export function uiRenderMixin(kbnServer, server, config) {
(filename) => `${regularBundlePath}/kbn-ui-shared-deps/${filename}`
),
`${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.jsFilename}`,
- ...(isCore ? [] : [`${dllBundlePath}/vendors_runtime.bundle.dll.js`, ...dllJsChunks]),
`${regularBundlePath}/core/core.entry.js`,
...kpPluginBundlePaths,
diff --git a/src/optimize/base_optimizer.js b/src/optimize/base_optimizer.js
index 55752db55e28a..41628a2264193 100644
--- a/src/optimize/base_optimizer.js
+++ b/src/optimize/base_optimizer.js
@@ -30,8 +30,6 @@ import webpackMerge from 'webpack-merge';
import WrapperPlugin from 'wrapper-webpack-plugin';
import * as UiSharedDeps from '@kbn/ui-shared-deps';
-import { DynamicDllPlugin } from './dynamic_dll_plugin';
-
import { IS_KIBANA_DISTRIBUTABLE } from '../legacy/utils';
import { fromRoot } from '../core/server/utils';
import { PUBLIC_PATH_PLACEHOLDER } from './public_path_placeholder';
@@ -286,12 +284,6 @@ export default class BaseOptimizer {
},
plugins: [
- new DynamicDllPlugin({
- uiBundles: this.uiBundles,
- threadLoaderPoolConfig: this.getThreadLoaderPoolConfig(),
- logWithMetadata: this.logWithMetadata,
- }),
-
new MiniCssExtractPlugin({
filename: '[name].style.css',
}),
diff --git a/src/optimize/bundles_route/__tests__/bundles_route.js b/src/optimize/bundles_route/__tests__/bundles_route.js
index 902fa59b20569..5b42b658300fe 100644
--- a/src/optimize/bundles_route/__tests__/bundles_route.js
+++ b/src/optimize/bundles_route/__tests__/bundles_route.js
@@ -56,7 +56,6 @@ describe('optimizer/bundle route', () => {
function createServer(options = {}) {
const {
regularBundlesPath = outputFixture,
- dllBundlesPath = outputFixture,
basePublicPath = '',
builtCssPath = outputFixture,
npUiPluginPublicDirs = [],
@@ -70,7 +69,6 @@ describe('optimizer/bundle route', () => {
server.route(
createBundlesRoute({
regularBundlesPath,
- dllBundlesPath,
basePublicPath,
builtCssPath,
npUiPluginPublicDirs,
@@ -89,28 +87,24 @@ describe('optimizer/bundle route', () => {
expect(() => {
createBundlesRoute({
regularBundlesPath: null,
- dllBundlesPath: '/absolute/path',
basePublicPath: '',
});
}).to.throwError(/absolute path/);
expect(() => {
createBundlesRoute({
regularBundlesPath: './relative',
- dllBundlesPath: '/absolute/path',
basePublicPath: '',
});
}).to.throwError(/absolute path/);
expect(() => {
createBundlesRoute({
regularBundlesPath: 1234,
- dllBundlesPath: '/absolute/path',
basePublicPath: '',
});
}).to.throwError(/absolute path/);
expect(() => {
createBundlesRoute({
regularBundlesPath: '/absolute/path',
- dllBundlesPath: '/absolute/path',
basePublicPath: '',
});
}).to.not.throwError();
@@ -119,42 +113,36 @@ describe('optimizer/bundle route', () => {
expect(() => {
createBundlesRoute({
regularBundlesPath: '/bundles',
- dllBundlesPath: '/absolute/path',
basePublicPath: 123,
});
}).to.throwError(/string/);
expect(() => {
createBundlesRoute({
regularBundlesPath: '/bundles',
- dllBundlesPath: '/absolute/path',
basePublicPath: {},
});
}).to.throwError(/string/);
expect(() => {
createBundlesRoute({
regularBundlesPath: '/bundles',
- dllBundlesPath: '/absolute/path',
basePublicPath: '/a/',
});
}).to.throwError(/start and not end with a \//);
expect(() => {
createBundlesRoute({
regularBundlesPath: '/bundles',
- dllBundlesPath: '/absolute/path',
basePublicPath: 'a/',
});
}).to.throwError(/start and not end with a \//);
expect(() => {
createBundlesRoute({
regularBundlesPath: '/bundles',
- dllBundlesPath: '/absolute/path',
basePublicPath: '/a',
});
}).to.not.throwError();
expect(() => {
createBundlesRoute({
regularBundlesPath: '/bundles',
- dllBundlesPath: '/absolute/path',
basePublicPath: '',
});
}).to.not.throwError();
diff --git a/src/optimize/bundles_route/bundles_route.ts b/src/optimize/bundles_route/bundles_route.ts
index e9cfba0130d95..8c1092efed252 100644
--- a/src/optimize/bundles_route/bundles_route.ts
+++ b/src/optimize/bundles_route/bundles_route.ts
@@ -28,22 +28,19 @@ import { assertIsNpUiPluginPublicDirs, NpUiPluginPublicDirs } from '../np_ui_plu
import { fromRoot } from '../../core/server/utils';
/**
- * Creates the routes that serves files from `bundlesPath` or from
- * `dllBundlesPath` (if they are dll bundle's related files). If the
+ * Creates the routes that serves files from `bundlesPath`. If the
* file is js or css then it is searched for instances of
* PUBLIC_PATH_PLACEHOLDER and replaces them with `publicPath`.
*
* @param {Object} options
* @property {Array<{id,path}>} options.npUiPluginPublicDirs array of ids and paths that should be served for new platform plugins
* @property {string} options.regularBundlesPath
- * @property {string} options.dllBundlesPath
* @property {string} options.basePublicPath
*
* @return Array.of({Hapi.Route})
*/
export function createBundlesRoute({
regularBundlesPath,
- dllBundlesPath,
basePublicPath,
builtCssPath,
npUiPluginPublicDirs = [],
@@ -51,7 +48,6 @@ export function createBundlesRoute({
isDist = false,
}: {
regularBundlesPath: string;
- dllBundlesPath: string;
basePublicPath: string;
builtCssPath: string;
npUiPluginPublicDirs?: NpUiPluginPublicDirs;
@@ -70,12 +66,6 @@ export function createBundlesRoute({
);
}
- if (typeof dllBundlesPath !== 'string' || !isAbsolute(dllBundlesPath)) {
- throw new TypeError(
- 'dllBundlesPath must be an absolute path to the directory containing the dll bundles'
- );
- }
-
if (typeof basePublicPath !== 'string') {
throw new TypeError('basePublicPath must be a string');
}
@@ -118,13 +108,6 @@ export function createBundlesRoute({
fileHashCache,
isDist,
}),
- buildRouteForBundles({
- publicPath: `${basePublicPath}/${buildHash}/built_assets/dlls/`,
- routePath: `/${buildHash}/built_assets/dlls/`,
- bundlesPath: dllBundlesPath,
- fileHashCache,
- isDist,
- }),
buildRouteForBundles({
publicPath: `${basePublicPath}/`,
routePath: `/${buildHash}/built_assets/css/`,
diff --git a/src/optimize/bundles_route/proxy_bundles_route.ts b/src/optimize/bundles_route/proxy_bundles_route.ts
index 1d189054324a1..108d253d45202 100644
--- a/src/optimize/bundles_route/proxy_bundles_route.ts
+++ b/src/optimize/bundles_route/proxy_bundles_route.ts
@@ -28,7 +28,6 @@ export function createProxyBundlesRoute({
}) {
return [
buildProxyRouteForBundles(`/${buildHash}/bundles/`, host, port),
- buildProxyRouteForBundles(`/${buildHash}/built_assets/dlls/`, host, port),
buildProxyRouteForBundles(`/${buildHash}/built_assets/css/`, host, port),
];
}
diff --git a/src/optimize/dynamic_dll_plugin/dll_compiler.js b/src/optimize/dynamic_dll_plugin/dll_compiler.js
deleted file mode 100644
index 9ab21ee0e9076..0000000000000
--- a/src/optimize/dynamic_dll_plugin/dll_compiler.js
+++ /dev/null
@@ -1,366 +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.
- */
-
-import { configModel } from './dll_config_model';
-import {
- notInNodeModulesOrWebpackShims,
- notInNodeModules,
- inDllPluginPublic,
-} from './dll_allowed_modules';
-import {
- dllEntryFileContentArrayToString,
- dllEntryFileContentStringToArray,
- dllMergeAllEntryFilesContent,
-} from './dll_entry_template';
-import { fromRoot } from '../../core/server/utils';
-import { PUBLIC_PATH_PLACEHOLDER } from '../public_path_placeholder';
-import fs from 'fs';
-import webpack from 'webpack';
-import { promisify } from 'util';
-import path from 'path';
-import del from 'del';
-import { chunk } from 'lodash';
-import seedrandom from 'seedrandom';
-
-const readFileAsync = promisify(fs.readFile);
-const mkdirAsync = promisify(fs.mkdir);
-const accessAsync = promisify(fs.access);
-const writeFileAsync = promisify(fs.writeFile);
-
-export class DllCompiler {
- static getRawDllConfig(
- uiBundles = {},
- babelLoaderCacheDir = '',
- threadLoaderPoolConfig = {},
- chunks = Array.from(Array(4).keys()).map((chunkN) => `_${chunkN}`)
- ) {
- return {
- uiBundles,
- babelLoaderCacheDir,
- threadLoaderPoolConfig,
- chunks,
- context: fromRoot('.'),
- entryName: 'vendors',
- dllName: '[name]',
- manifestName: '[name]',
- styleName: '[name]',
- entryExt: '.entry.dll.js',
- dllExt: '.bundle.dll.js',
- manifestExt: '.manifest.dll.json',
- styleExt: '.style.dll.css',
- outputPath: fromRoot('built_assets/dlls'),
- publicPath: PUBLIC_PATH_PLACEHOLDER,
- };
- }
-
- constructor(uiBundles, threadLoaderPoolConfig, logWithMetadata) {
- this.rawDllConfig = DllCompiler.getRawDllConfig(
- uiBundles,
- uiBundles.getCacheDirectory('babel'),
- threadLoaderPoolConfig
- );
- this.logWithMetadata = logWithMetadata || (() => null);
- }
-
- async init() {
- await this.ensureEntryFilesExists();
- await this.ensureManifestFilesExists();
- await this.ensureOutputPathExists();
- }
-
- seededShuffle(array) {
- // Implementation based on https://github.com/TimothyGu/knuth-shuffle-seeded/blob/gh-pages/index.js#L46
- let currentIndex;
- let temporaryValue;
- let randomIndex;
- const rand = seedrandom('predictable', { global: false });
-
- if (array.constructor !== Array) throw new Error('Input is not an array');
- currentIndex = array.length;
-
- // While there remain elements to shuffle...
- while (0 !== currentIndex) {
- // Pick a remaining element...
- randomIndex = Math.floor(rand() * currentIndex--);
-
- // And swap it with the current element.
- temporaryValue = array[currentIndex];
- array[currentIndex] = array[randomIndex];
- array[randomIndex] = temporaryValue;
- }
-
- return array;
- }
-
- async upsertEntryFiles(content) {
- const arrayContent = this.seededShuffle(dllEntryFileContentStringToArray(content));
- const chunks = chunk(
- arrayContent,
- Math.ceil(arrayContent.length / this.rawDllConfig.chunks.length)
- );
- const entryPaths = this.getEntryPaths();
-
- await Promise.all(
- entryPaths.map(
- async (entryPath, idx) =>
- await this.upsertFile(entryPath, dllEntryFileContentArrayToString(chunks[idx]))
- )
- );
- }
-
- async upsertFile(filePath, content = '') {
- await this.ensurePathExists(filePath);
- await writeFileAsync(filePath, content, 'utf8');
- }
-
- getDllPaths() {
- return this.rawDllConfig.chunks.map((chunk) =>
- this.resolvePath(`${this.rawDllConfig.entryName}${chunk}${this.rawDllConfig.dllExt}`)
- );
- }
-
- getEntryPaths() {
- return this.rawDllConfig.chunks.map((chunk) =>
- this.resolvePath(`${this.rawDllConfig.entryName}${chunk}${this.rawDllConfig.entryExt}`)
- );
- }
-
- getManifestPaths() {
- return this.rawDllConfig.chunks.map((chunk) =>
- this.resolvePath(`${this.rawDllConfig.entryName}${chunk}${this.rawDllConfig.manifestExt}`)
- );
- }
-
- getStylePaths() {
- return this.rawDllConfig.chunks.map((chunk) =>
- this.resolvePath(`${this.rawDllConfig.entryName}${chunk}${this.rawDllConfig.styleExt}`)
- );
- }
-
- async ensureEntryFilesExists() {
- const entryPaths = this.getEntryPaths();
-
- await Promise.all(entryPaths.map(async (entryPath) => await this.ensureFileExists(entryPath)));
- }
-
- async ensureManifestFilesExists() {
- const manifestPaths = this.getManifestPaths();
-
- await Promise.all(
- manifestPaths.map(
- async (manifestPath, idx) =>
- await this.ensureFileExists(
- manifestPath,
- JSON.stringify({
- name: `${this.rawDllConfig.entryName}${this.rawDllConfig.chunks[idx]}`,
- content: {},
- })
- )
- )
- );
- }
-
- async ensureStyleFileExists() {
- const stylePaths = this.getStylePaths();
-
- await Promise.all(stylePaths.map(async (stylePath) => await this.ensureFileExists(stylePath)));
- }
-
- async ensureFileExists(filePath, content) {
- const exists = await this.ensurePathExists(filePath);
-
- if (!exists) {
- await this.upsertFile(filePath, content);
- }
- }
-
- async ensurePathExists(filePath) {
- try {
- await accessAsync(filePath);
- } catch (e) {
- await mkdirAsync(path.dirname(filePath), { recursive: true });
- return false;
- }
-
- return true;
- }
-
- async ensureOutputPathExists() {
- await this.ensurePathExists(this.rawDllConfig.outputPath);
- }
-
- dllsExistsSync() {
- const dllPaths = this.getDllPaths();
-
- return dllPaths.every((dllPath) => this.existsSync(dllPath));
- }
-
- existsSync(filePath) {
- return fs.existsSync(filePath);
- }
-
- resolvePath() {
- return path.resolve(this.rawDllConfig.outputPath, ...arguments);
- }
-
- async readEntryFiles() {
- const entryPaths = this.getEntryPaths();
-
- const entryFilesContent = await Promise.all(
- entryPaths.map(async (entryPath) => await this.readFile(entryPath))
- );
-
- // merge all the module contents from entry files again into
- // sorted single one
- return dllMergeAllEntryFilesContent(entryFilesContent);
- }
-
- async readFile(filePath, content) {
- await this.ensureFileExists(filePath, content);
- return await readFileAsync(filePath, 'utf8');
- }
-
- async run(dllEntries) {
- const dllConfig = this.dllConfigGenerator(this.rawDllConfig);
- await this.upsertEntryFiles(dllEntries);
-
- try {
- this.logWithMetadata(
- ['info', 'optimize:dynamic_dll_plugin'],
- 'Client vendors dll compilation started'
- );
-
- await this.runWebpack(dllConfig());
-
- this.logWithMetadata(
- ['info', 'optimize:dynamic_dll_plugin'],
- `Client vendors dll compilation finished with success`
- );
- } catch (e) {
- this.logWithMetadata(
- ['fatal', 'optimize:dynamic_dll_plugin'],
- `Client vendors dll compilation failed`
- );
-
- // Still throw the original error has here we just want
- // log the fail message
- throw e;
- }
-
- // Style dll file isn't always created but we are
- // expecting it to exist always as we are referencing
- // it from the bootstrap template
- //
- // NOTE: We should review the way we deal with the css extraction
- // in ours webpack builds. The industry standard is about to
- // only extract css for production but we are extracting it
- // in every single compilation.
- await this.ensureStyleFileExists();
- }
-
- dllConfigGenerator(dllConfig) {
- return configModel.bind(this, dllConfig);
- }
-
- async runWebpack(config) {
- return new Promise((resolve, reject) => {
- webpack(config, async (err, stats) => {
- // If a critical error occurs or we have
- // errors in the stats compilation,
- // reject the promise and logs the errors
- const webpackErrors =
- err ||
- (stats.hasErrors() &&
- stats.toString({
- all: false,
- colors: true,
- errors: true,
- errorDetails: true,
- moduleTrace: true,
- }));
-
- if (webpackErrors) {
- // Reject with webpack fatal errors
- return reject(webpackErrors);
- }
-
- // Identify if we have not allowed modules
- // bundled inside the dll bundle
- const notAllowedModules = [];
-
- stats.compilation.modules.forEach((module) => {
- // ignore if no module or userRequest are defined
- if (!module || !module.resource) {
- return;
- }
-
- // ignore if this module represents the
- // dll entry file
- if (this.getEntryPaths().includes(module.resource)) {
- return;
- }
-
- // ignore if this module is part of the
- // files inside dynamic dll plugin public folder
- if (inDllPluginPublic(module.resource)) {
- return;
- }
-
- // A module is not allowed if it's not a node_module, a webpackShim
- // or the reasons from being bundled into the dll are not node_modules
- if (notInNodeModulesOrWebpackShims(module.resource)) {
- const reasons = module.reasons || [];
-
- reasons.forEach((reason) => {
- // Skip if we can't read the reason info
- if (!reason || !reason.module || !reason.module.resource) {
- return;
- }
-
- // Is the reason for this module being bundle a
- // node_module or no?
- if (notInNodeModules(reason.module.resource)) {
- notAllowedModules.push(module.resource);
- }
- });
- }
- });
-
- if (notAllowedModules.length) {
- // Delete the built dll, as it contains invalid modules, and reject listing
- // all the not allowed modules
- try {
- await del(this.rawDllConfig.outputPath);
- } catch (e) {
- return reject(e);
- }
-
- return reject(
- `The following modules are not allowed to be bundled into the dll: \n${notAllowedModules.join(
- '\n'
- )}`
- );
- }
-
- // Otherwise it has succeed
- return resolve(stats);
- });
- });
- }
-}
diff --git a/src/optimize/dynamic_dll_plugin/dll_config_model.js b/src/optimize/dynamic_dll_plugin/dll_config_model.js
deleted file mode 100644
index eec369b194fef..0000000000000
--- a/src/optimize/dynamic_dll_plugin/dll_config_model.js
+++ /dev/null
@@ -1,278 +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.
- */
-
-import { IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
-import { fromRoot } from '../../core/server/utils';
-import webpack from 'webpack';
-import webpackMerge from 'webpack-merge';
-import MiniCssExtractPlugin from 'mini-css-extract-plugin';
-import TerserPlugin from 'terser-webpack-plugin';
-import * as UiSharedDeps from '@kbn/ui-shared-deps';
-
-function generateDLL(config) {
- const {
- dllAlias,
- dllValidateSyntax,
- dllNoParseRules,
- dllContext,
- dllEntry,
- dllOutputPath,
- dllPublicPath,
- dllBundleName,
- dllBundleFilename,
- dllStyleFilename,
- dllManifestPath,
- babelLoaderCacheDir,
- threadLoaderPoolConfig,
- } = config;
-
- const BABEL_PRESET_PATH = require.resolve('@kbn/babel-preset/webpack_preset');
- const BABEL_EXCLUDE_RE = [/[\/\\](webpackShims|node_modules|bower_components)[\/\\]/];
-
- /**
- * Wrap plugin loading in a function so that we can require
- * `@kbn/optimizer` only when absolutely necessary since we
- * don't ship this package in the distributable but this code
- * is still shipped, though it's not used.
- */
- const getValidateSyntaxPlugins = () => {
- if (!dllValidateSyntax) {
- return [];
- }
-
- // only require @kbn/optimizer
- const { DisallowedSyntaxPlugin } = require('@kbn/optimizer');
- return [new DisallowedSyntaxPlugin()];
- };
-
- return {
- entry: dllEntry,
- context: dllContext,
- output: {
- futureEmitAssets: true, // TODO: remove on webpack 5
- filename: dllBundleFilename,
- path: dllOutputPath,
- publicPath: dllPublicPath,
- library: dllBundleName,
- },
- node: { fs: 'empty', child_process: 'empty', dns: 'empty', net: 'empty', tls: 'empty' },
- resolve: {
- extensions: ['.js', '.json'],
- mainFields: ['browser', 'browserify', 'main'],
- alias: dllAlias,
- modules: ['webpackShims', fromRoot('webpackShims'), 'node_modules', fromRoot('node_modules')],
- },
- module: {
- rules: [
- {
- resource: [
- {
- test: /\.js$/,
- exclude: BABEL_EXCLUDE_RE.concat(dllNoParseRules),
- },
- {
- test: /\.js$/,
- include: /[\/\\]node_modules[\/\\]x-pack[\/\\]/,
- exclude: /[\/\\]node_modules[\/\\]x-pack[\/\\](.+?[\/\\])*node_modules[\/\\]/,
- },
- // TODO: remove when we drop support for IE11
- // We need because normalize-url is distributed without
- // any kind of transpilation
- // More info: https://github.com/elastic/kibana/pull/35804
- {
- test: /\.js$/,
- include: /[\/\\]node_modules[\/\\]normalize-url[\/\\]/,
- exclude: /[\/\\]node_modules[\/\\]normalize-url[\/\\](.+?[\/\\])*node_modules[\/\\]/,
- },
- ],
- // Self calling function with the equivalent logic
- // from maybeAddCacheLoader one from base optimizer
- use: ((babelLoaderCacheDirPath, loaders) => {
- return [
- {
- loader: 'cache-loader',
- options: {
- cacheContext: fromRoot('.'),
- cacheDirectory: babelLoaderCacheDirPath,
- readOnly: process.env.KBN_CACHE_LOADER_WRITABLE ? false : IS_KIBANA_DISTRIBUTABLE,
- },
- },
- ...loaders,
- ];
- })(babelLoaderCacheDir, [
- {
- loader: 'thread-loader',
- options: threadLoaderPoolConfig,
- },
- {
- loader: 'babel-loader',
- options: {
- babelrc: false,
- presets: [BABEL_PRESET_PATH],
- },
- },
- ]),
- },
- {
- test: /\.(html|tmpl)$/,
- loader: 'raw-loader',
- },
- {
- test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, 'css-loader'],
- },
- {
- test: /\.png$/,
- loader: 'url-loader',
- },
- {
- test: /\.(woff|woff2|ttf|eot|svg|ico)(\?|$)/,
- loader: 'file-loader',
- },
- ],
- noParse: dllNoParseRules,
- },
- plugins: [
- new webpack.DllPlugin({
- context: dllContext,
- name: dllBundleName,
- path: dllManifestPath,
- }),
- new MiniCssExtractPlugin({
- filename: dllStyleFilename,
- }),
- ...getValidateSyntaxPlugins(),
- ],
- // Single runtime for the dll bundles which assures that common transient dependencies won't be evaluated twice.
- // The module cache will be shared, even when module code may be duplicated across chunks.
- optimization: {
- runtimeChunk: {
- name: 'vendors_runtime',
- },
- },
- performance: {
- // NOTE: we are disabling this as those hints
- // are more tailored for the final bundles result
- // and not for the webpack compilations performance itself
- hints: false,
- },
- externals: {
- ...UiSharedDeps.externals,
- },
- };
-}
-
-function extendRawConfig(rawConfig) {
- // Build all extended configs from raw config
- const dllAlias = rawConfig.uiBundles.getAliases();
- const dllValidateSyntax = rawConfig.uiBundles.shouldValidateSyntaxOfNodeModules();
- const dllNoParseRules = rawConfig.uiBundles.getWebpackNoParseRules();
- const dllDevMode = rawConfig.uiBundles.isDevMode();
- const dllContext = rawConfig.context;
- const dllChunks = rawConfig.chunks;
- const dllEntry = {};
- const dllEntryName = rawConfig.entryName;
- const dllBundleName = rawConfig.dllName;
- const dllManifestName = rawConfig.dllName;
- const dllStyleName = rawConfig.styleName;
- const dllEntryExt = rawConfig.entryExt;
- const dllBundleExt = rawConfig.dllExt;
- const dllManifestExt = rawConfig.manifestExt;
- const dllStyleExt = rawConfig.styleExt;
- const dllOutputPath = rawConfig.outputPath;
- const dllPublicPath = rawConfig.publicPath;
- const dllBundleFilename = `${dllBundleName}${dllBundleExt}`;
- const dllManifestPath = `${dllOutputPath}/${dllManifestName}${dllManifestExt}`;
- const dllStyleFilename = `${dllStyleName}${dllStyleExt}`;
- const babelLoaderCacheDir = rawConfig.babelLoaderCacheDir;
- const threadLoaderPoolConfig = rawConfig.threadLoaderPoolConfig;
-
- // Create webpack entry object key with the provided dllEntryName
- dllChunks.reduce((dllEntryObj, chunk) => {
- dllEntryObj[`${dllEntryName}${chunk}`] = [
- `${dllOutputPath}/${dllEntryName}${chunk}${dllEntryExt}`,
- ];
- return dllEntryObj;
- }, dllEntry);
-
- // Export dll config map
- return {
- dllAlias,
- dllValidateSyntax,
- dllNoParseRules,
- dllDevMode,
- dllContext,
- dllEntry,
- dllOutputPath,
- dllPublicPath,
- dllBundleName,
- dllBundleFilename,
- dllStyleFilename,
- dllManifestPath,
- babelLoaderCacheDir,
- threadLoaderPoolConfig,
- };
-}
-
-function common(config) {
- return webpackMerge(generateDLL(config));
-}
-
-function optimized() {
- return webpackMerge({
- mode: 'production',
- optimization: {
- minimizer: [
- new TerserPlugin({
- // NOTE: we should not enable that option for now
- // Since 2.0.0 terser-webpack-plugin is using jest-worker
- // to run tasks in a pool of workers. Currently it looks like
- // is requiring too much memory and break on large entry points
- // compilations (like this) one. Also the gain we have enabling
- // that option was barely noticed.
- // https://github.com/webpack-contrib/terser-webpack-plugin/issues/143
- parallel: false,
- sourceMap: false,
- cache: false,
- extractComments: false,
- terserOptions: {
- compress: false,
- mangle: false,
- },
- }),
- ],
- },
- });
-}
-
-function unoptimized() {
- return webpackMerge({
- mode: 'development',
- });
-}
-
-export function configModel(rawConfig = {}) {
- const config = extendRawConfig(rawConfig);
-
- if (config.dllDevMode) {
- return webpackMerge(common(config), unoptimized());
- }
-
- return webpackMerge(common(config), optimized());
-}
diff --git a/src/optimize/dynamic_dll_plugin/dynamic_dll_plugin.js b/src/optimize/dynamic_dll_plugin/dynamic_dll_plugin.js
deleted file mode 100644
index fb6f6e097b5cd..0000000000000
--- a/src/optimize/dynamic_dll_plugin/dynamic_dll_plugin.js
+++ /dev/null
@@ -1,354 +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.
- */
-
-import { DllCompiler } from './dll_compiler';
-import { notInNodeModulesOrWebpackShims, inPluginNodeModules } from './dll_allowed_modules';
-import { IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
-import { dllEntryTemplate } from './dll_entry_template';
-import RawModule from 'webpack/lib/RawModule';
-import webpack from 'webpack';
-import path from 'path';
-import normalizePosixPath from 'normalize-path';
-import fs from 'fs';
-import { promisify } from 'util';
-
-const realPathAsync = promisify(fs.realpath);
-const DLL_ENTRY_STUB_MODULE_TYPE = 'javascript/dll-entry-stub';
-
-export class DynamicDllPlugin {
- constructor({ uiBundles, threadLoaderPoolConfig, logWithMetadata, maxCompilations = 1 }) {
- this.logWithMetadata = logWithMetadata || (() => null);
- this.dllCompiler = new DllCompiler(uiBundles, threadLoaderPoolConfig, logWithMetadata);
- this.entryPaths = dllEntryTemplate();
- this.afterCompilationEntryPaths = dllEntryTemplate();
- this.maxCompilations = maxCompilations;
- this.performedCompilations = 0;
- this.forceDLLCreationFlag = !!(process && process.env && process.env.FORCE_DLL_CREATION);
- }
-
- async init() {
- await this.dllCompiler.init();
- this.entryPaths = await this.dllCompiler.readEntryFiles();
- }
-
- apply(compiler) {
- // Just register the init basic hooks
- // in order to run the init function
- this.registerInitBasicHooks(compiler);
- // The dll reference should always be bind to the
- // main webpack config.
- this.bindDllReferencePlugin(compiler);
-
- // Verify if we must init and run the dynamic dll plugin tasks.
- // We must run it every time we are not under a distributable env
- if (!this.mustRunDynamicDllPluginTasks()) {
- return;
- }
-
- // This call init all the DynamicDllPlugin tasks
- // as it attaches the plugin to the main webpack
- // lifecycle hooks needed to perform the logic
- this.registerTasksHooks(compiler);
- }
-
- bindDllReferencePlugin(compiler) {
- const rawDllConfig = this.dllCompiler.rawDllConfig;
- const dllContext = rawDllConfig.context;
- const dllManifestPaths = this.dllCompiler.getManifestPaths();
-
- dllManifestPaths.forEach((dllChunkManifestPath) => {
- new webpack.DllReferencePlugin({
- context: dllContext,
- manifest: dllChunkManifestPath,
- }).apply(compiler);
- });
- }
-
- registerInitBasicHooks(compiler) {
- this.registerRunHook(compiler);
- this.registerWatchRunHook(compiler);
- }
-
- registerTasksHooks(compiler) {
- this.logWithMetadata(
- ['info', 'optimize:dynamic_dll_plugin'],
- 'Started dynamic dll plugin tasks'
- );
- this.registerBeforeCompileHook(compiler);
- this.registerCompilationHook(compiler);
- this.registerDoneHook(compiler);
- }
-
- registerRunHook(compiler) {
- compiler.hooks.run.tapPromise('DynamicDllPlugin', async () => {
- await this.init();
- });
- }
-
- registerWatchRunHook(compiler) {
- compiler.hooks.watchRun.tapPromise('DynamicDllPlugin', async () => {
- await this.init();
- });
- }
-
- registerBeforeCompileHook(compiler) {
- compiler.hooks.beforeCompile.tapPromise('DynamicDllPlugin', async ({ normalModuleFactory }) => {
- normalModuleFactory.hooks.factory.tap('DynamicDllPlugin', (actualFactory) => (params, cb) => {
- // This is used in order to avoid the cache for DLL modules
- // resolved from other dependencies
- normalModuleFactory.cachePredicate = (module) =>
- !(module.stubType === DLL_ENTRY_STUB_MODULE_TYPE);
-
- // Overrides the normalModuleFactory module creation behaviour
- // in order to understand the modules we need to add to the DLL
- actualFactory(params, (error, module) => {
- if (error || !module) {
- cb(error, module);
- } else {
- this.mapNormalModule(module).then(
- (m = module) => cb(undefined, m),
- (error) => cb(error)
- );
- }
- });
- });
- });
- }
-
- registerCompilationHook(compiler) {
- compiler.hooks.compilation.tap('DynamicDllPlugin', (compilation) => {
- compilation.hooks.needAdditionalPass.tap('DynamicDllPlugin', () => {
- // Run the procedures in order to execute our dll compilation
- // The process is very straightforward in it's conception:
- //
- // * 1 - loop through every compilation module in order to start building
- // the dll entry paths arrays and assume it is the new entry paths
- // * 1.1 - start from adding the modules already included into the dll, if any.
- // * 1.2 - adding the new discovered stub modules
- // * 1.3 - check if the module added to the entry path is from node_modules or
- // webpackShims, otherwise throw an error.
- // * 1.3.1 - for the entry path modules coming from webpackShims search for every
- // require statements inside of them
- // * 1.3.2 - discard the ones that are not js dependencies
- // * 1.3.3 - add those new discovered dependencies inside the webpackShims to the
- // entry paths array
- // * 2 - compare the built entry paths and compares it to the old one (if any)
- // * 3 - runs a new dll compilation in case there is none old entry paths or if the
- // new built one differs from the old one.
- //
- const rawDllConfig = this.dllCompiler.rawDllConfig;
- const dllContext = rawDllConfig.context;
- const dllOutputPath = rawDllConfig.outputPath;
- const requiresMap = {};
-
- for (const module of compilation.modules) {
- // re-include requires for modules already handled by the dll
- if (module.delegateData) {
- const absoluteResource = path.resolve(dllContext, module.userRequest);
- if (
- absoluteResource.includes('node_modules') ||
- absoluteResource.includes('webpackShims')
- ) {
- // NOTE: normalizePosixPath is been used as we only want to have posix
- // paths inside our final dll entry file
- requiresMap[
- normalizePosixPath(path.relative(dllOutputPath, absoluteResource))
- ] = true;
- }
- }
-
- // include requires for modules that need to be added to the dll
- if (module.stubType === DLL_ENTRY_STUB_MODULE_TYPE) {
- requiresMap[
- normalizePosixPath(path.relative(dllOutputPath, module.stubResource))
- ] = true;
- }
- }
-
- // Sort and join all the discovered require deps
- // in order to create a consistent entry file
- this.afterCompilationEntryPaths = dllEntryTemplate(Object.keys(requiresMap));
- // The dll compilation will run if on of the following conditions return true:
- // 1 - the new generated entry paths are different from the
- // old ones
- // 2 - if no dll bundle is yet created
- // 3 - if this.forceDLLCreationFlag were set from the node env var FORCE_DLL_CREATION and
- // we are not running over the distributable. If we are running under the watch optimizer,
- // this.forceDLLCreationFlag will only be applied in the very first execution,
- // then will be set to false
- compilation.needsDLLCompilation =
- this.afterCompilationEntryPaths !== this.entryPaths ||
- !this.dllCompiler.dllsExistsSync() ||
- (this.isToForceDLLCreation() && this.performedCompilations === 0);
- this.entryPaths = this.afterCompilationEntryPaths;
-
- // Only run this info log in the first performed dll compilation
- // per each execution run
- if (this.performedCompilations === 0) {
- this.logWithMetadata(
- ['info', 'optimize:dynamic_dll_plugin'],
- compilation.needsDLLCompilation
- ? 'Need to compile the client vendors dll'
- : 'No need to compile client vendors dll'
- );
- }
-
- return compilation.needsDLLCompilation;
- });
- });
- }
-
- registerDoneHook(compiler) {
- compiler.hooks.done.tapPromise('DynamicDllPlugin', async (stats) => {
- if (stats.compilation.needsDLLCompilation) {
- // Run the dlls compiler and increment
- // the performed compilations
- //
- // NOTE: check the need for this extra try/catch after upgrading
- // past webpack v4.29.3. For now it is needed so we can log the error
- // otherwise the error log we'll get will be something like: [fatal] [object Object]
- try {
- await this.runDLLCompiler(compiler);
- } catch (error) {
- this.logWithMetadata(['error', 'optimize:dynamic_dll_plugin'], error.message);
- throw error;
- }
-
- return;
- }
-
- this.performedCompilations = 0;
- // reset this flag var set from the node env FORCE_DLL_CREATION on init,
- // has the force_dll_creation is only valid for the very first run
- if (this.forceDLLCreationFlag) {
- this.forceDLLCreationFlag = false;
- }
- this.logWithMetadata(
- ['info', 'optimize:dynamic_dll_plugin'],
- 'Finished all dynamic dll plugin tasks'
- );
- });
- }
-
- isToForceDLLCreation() {
- return this.forceDLLCreationFlag;
- }
-
- mustRunDynamicDllPluginTasks() {
- return !IS_KIBANA_DISTRIBUTABLE || this.isToForceDLLCreation();
- }
-
- async mapNormalModule(module) {
- // ignore anything that doesn't have a resource (ignored) or is already delegating to the DLL
- if (!module.resource || module.delegateData) {
- return;
- }
-
- // ignore anything that needs special loaders or config
- if (module.request.includes('!') || module.request.includes('?')) {
- return;
- }
-
- // ignore files that are not in node_modules
- if (notInNodeModulesOrWebpackShims(module.resource)) {
- return;
- }
-
- // also ignore files that are symlinked into node_modules, but only
- // do the `realpath` call after checking the plain resource path
- if (notInNodeModulesOrWebpackShims(await realPathAsync(module.resource))) {
- return;
- }
-
- const dirs = module.resource.split(path.sep);
- const nodeModuleName = dirs[dirs.lastIndexOf('node_modules') + 1];
-
- // ignore webpack loader modules
- if (nodeModuleName.endsWith('-loader')) {
- return;
- }
-
- // ignore modules from plugins
- if (inPluginNodeModules(module.resource)) {
- return;
- }
-
- // also ignore files that are symlinked into plugins node_modules, but only
- // do the `realpath` call after checking the plain resource path
- if (inPluginNodeModules(await realPathAsync(module.resource))) {
- return;
- }
-
- // This is a StubModule (as a RawModule) in order
- // to mimic the missing modules from the DLL and
- // also hold useful metadata
- const stubModule = new RawModule(
- `/* pending dll entry */`,
- `dll pending:${module.resource}`,
- module.resource
- );
- stubModule.stubType = DLL_ENTRY_STUB_MODULE_TYPE;
- stubModule.stubResource = module.resource;
- stubModule.stubOriginalModule = module;
-
- return stubModule;
- }
-
- async assertMaxCompilations() {
- // Logic to run the max compilation requirements.
- // Only enable this for CI builds in order to ensure
- // we have an healthy dll ecosystem.
- if (this.performedCompilations === this.maxCompilations) {
- throw new Error(
- 'All the allowed dll compilations were already performed and one more is needed which is not possible'
- );
- }
- }
-
- async runDLLCompiler(mainCompiler) {
- const runCompilerErrors = [];
-
- try {
- await this.dllCompiler.run(this.entryPaths);
- } catch (e) {
- runCompilerErrors.push(e);
- }
-
- try {
- await this.assertMaxCompilations();
- } catch (e) {
- runCompilerErrors.push(e);
- }
-
- // We need to purge the cache into the inputFileSystem
- // for every single built in previous compilation
- // that we rely in next ones.
- this.dllCompiler
- .getManifestPaths()
- .forEach((chunkDllManifestPath) => mainCompiler.inputFileSystem.purge(chunkDllManifestPath));
-
- this.performedCompilations++;
-
- if (!runCompilerErrors.length) {
- return;
- }
-
- throw new Error(runCompilerErrors.join('\n-'));
- }
-}
diff --git a/src/optimize/optimize_mixin.ts b/src/optimize/optimize_mixin.ts
index 9a3f08e2f667e..947c918a567f5 100644
--- a/src/optimize/optimize_mixin.ts
+++ b/src/optimize/optimize_mixin.ts
@@ -22,8 +22,6 @@ import Hapi from 'hapi';
// @ts-ignore not TS yet
import FsOptimizer from './fs_optimizer';
import { createBundlesRoute } from './bundles_route';
-// @ts-ignore not TS yet
-import { DllCompiler } from './dynamic_dll_plugin';
import { fromRoot } from '../core/server/utils';
import { getNpUiPluginPublicDirs } from './np_ui_plugin_public_dirs';
import KbnServer, { KibanaConfig } from '../legacy/server/kbn_server';
@@ -40,7 +38,7 @@ export const optimizeMixin = async (
// bundles in a "middleware" style.
//
// the server listening on 5601 may be restarted a number of times, depending
- // on the watch setup managed by the cli. It proxies all bundles/* and built_assets/dlls/*
+ // on the watch setup managed by the cli. It proxies all bundles/*
// requests to the other server. The server on 5602 is long running, in order
// to prevent complete rebuilds of the optimize content.
const watch = config.get('optimize.watch');
@@ -53,7 +51,6 @@ export const optimizeMixin = async (
server.route(
createBundlesRoute({
regularBundlesPath: uiBundles.getWorkingDir(),
- dllBundlesPath: DllCompiler.getRawDllConfig().outputPath,
basePublicPath: config.get('server.basePath'),
builtCssPath: fromRoot('built_assets/css'),
npUiPluginPublicDirs: getNpUiPluginPublicDirs(kbnServer),
diff --git a/src/optimize/watch/optmzr_role.js b/src/optimize/watch/optmzr_role.js
index ba8007e1065b4..0057c04219ec6 100644
--- a/src/optimize/watch/optmzr_role.js
+++ b/src/optimize/watch/optmzr_role.js
@@ -17,12 +17,8 @@
* under the License.
*/
-import { resolve } from 'path';
-
import WatchServer from './watch_server';
import WatchOptimizer, { STATUS } from './watch_optimizer';
-import { DllCompiler } from '../dynamic_dll_plugin';
-import { WatchCache } from './watch_cache';
import { getNpUiPluginPublicDirs } from '../np_ui_plugin_public_dirs';
export default async (kbnServer, kibanaHapiServer, config) => {
@@ -36,12 +32,6 @@ export default async (kbnServer, kibanaHapiServer, config) => {
sourceMaps: config.get('optimize.sourceMaps'),
workers: config.get('optimize.workers'),
prebuild: config.get('optimize.watchPrebuild'),
- watchCache: new WatchCache({
- logWithMetadata,
- outputPath: config.get('path.data'),
- dllsPath: DllCompiler.getRawDllConfig().outputPath,
- cachePath: resolve(kbnServer.uiBundles.getCacheDirectory(), '../'),
- }),
});
const server = new WatchServer(
diff --git a/src/optimize/watch/watch.js b/src/optimize/watch/watch.js
index a284da11f294f..7774577fb7677 100644
--- a/src/optimize/watch/watch.js
+++ b/src/optimize/watch/watch.js
@@ -32,7 +32,7 @@ export default async (kbnServer) => {
* while the optimizer is running
*
* server: this process runs the entire kibana server and proxies
- * all requests for /bundles/* or /built_assets/dlls/* to the optmzr process
+ * all requests for /bundles/* to the optmzr process
*
* @param {string} process.env.kbnWorkerType
*/
diff --git a/src/optimize/watch/watch_cache.ts b/src/optimize/watch/watch_cache.ts
deleted file mode 100644
index 40bd1d6075f47..0000000000000
--- a/src/optimize/watch/watch_cache.ts
+++ /dev/null
@@ -1,189 +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.
- */
-
-import { createHash } from 'crypto';
-import { readFile, writeFile, readdir, unlink, rmdir } from 'fs';
-import { resolve } from 'path';
-import { promisify } from 'util';
-import path from 'path';
-import del from 'del';
-import normalizePosixPath from 'normalize-path';
-
-const readAsync = promisify(readFile);
-const writeAsync = promisify(writeFile);
-const readdirAsync = promisify(readdir);
-const unlinkAsync = promisify(unlink);
-const rmdirAsync = promisify(rmdir);
-
-interface Params {
- logWithMetadata: (tags: string[], message: string, metadata?: { [key: string]: any }) => void;
- outputPath: string;
- dllsPath: string;
- cachePath: string;
-}
-
-interface WatchCacheStateContent {
- optimizerConfigSha?: string;
- yarnLockSha?: string;
-}
-
-export class WatchCache {
- private readonly logWithMetadata: Params['logWithMetadata'];
- private readonly outputPath: Params['outputPath'];
- private readonly dllsPath: Params['dllsPath'];
- private readonly cachePath: Params['cachePath'];
- private readonly cacheState: WatchCacheStateContent;
- private statePath: string;
- private diskCacheState: WatchCacheStateContent;
- private isInitialized: boolean;
-
- constructor(params: Params) {
- this.logWithMetadata = params.logWithMetadata;
- this.outputPath = params.outputPath;
- this.dllsPath = params.dllsPath;
- this.cachePath = params.cachePath;
-
- this.isInitialized = false;
- this.statePath = '';
- this.cacheState = {};
- this.diskCacheState = {};
- this.cacheState.yarnLockSha = '';
- this.cacheState.optimizerConfigSha = '';
- }
-
- public async tryInit() {
- if (!this.isInitialized) {
- this.statePath = resolve(this.outputPath, 'watch_optimizer_cache_state.json');
- this.diskCacheState = await this.read();
- this.cacheState.yarnLockSha = await this.buildYarnLockSha();
- this.cacheState.optimizerConfigSha = await this.buildOptimizerConfigSha();
- this.isInitialized = true;
- }
- }
-
- public async tryReset() {
- await this.tryInit();
-
- if (!this.isResetNeeded()) {
- return;
- }
-
- await this.reset();
- }
-
- public async reset() {
- this.logWithMetadata(['info', 'optimize:watch_cache'], 'The optimizer watch cache will reset');
-
- // start by deleting the state file to lower the
- // amount of time that another process might be able to
- // successfully read it once we decide to delete it
- await del(this.statePath, { force: true });
-
- // delete everything in optimize/.cache directory
- await recursiveDelete(normalizePosixPath(this.cachePath));
-
- // delete dlls
- await del(this.dllsPath);
-
- // re-write new cache state file
- await this.write();
-
- this.logWithMetadata(['info', 'optimize:watch_cache'], 'The optimizer watch cache has reset');
- }
-
- private async buildShaWithMultipleFiles(filePaths: string[]) {
- const shaHash = createHash('sha1');
-
- for (const filePath of filePaths) {
- try {
- shaHash.update(await readAsync(filePath, 'utf8'), 'utf8');
- } catch (e) {
- /* no-op */
- }
- }
-
- return shaHash.digest('hex');
- }
-
- private async buildYarnLockSha() {
- const kibanaYarnLock = resolve(__dirname, '../../../yarn.lock');
-
- return await this.buildShaWithMultipleFiles([kibanaYarnLock]);
- }
-
- private async buildOptimizerConfigSha() {
- const baseOptimizer = resolve(__dirname, '../base_optimizer.js');
- const dynamicDllConfigModel = resolve(__dirname, '../dynamic_dll_plugin/dll_config_model.js');
- const dynamicDllPlugin = resolve(__dirname, '../dynamic_dll_plugin/dynamic_dll_plugin.js');
-
- return await this.buildShaWithMultipleFiles([
- baseOptimizer,
- dynamicDllConfigModel,
- dynamicDllPlugin,
- ]);
- }
-
- private isResetNeeded() {
- return this.hasYarnLockChanged() || this.hasOptimizerConfigChanged();
- }
-
- private hasYarnLockChanged() {
- return this.cacheState.yarnLockSha !== this.diskCacheState.yarnLockSha;
- }
-
- private hasOptimizerConfigChanged() {
- return this.cacheState.optimizerConfigSha !== this.diskCacheState.optimizerConfigSha;
- }
-
- private async write() {
- await writeAsync(this.statePath, JSON.stringify(this.cacheState, null, 2), 'utf8');
- this.diskCacheState = this.cacheState;
- }
-
- private async read(): Promise {
- try {
- return JSON.parse(await readAsync(this.statePath, 'utf8'));
- } catch (error) {
- return {};
- }
- }
-}
-
-/**
- * Recursively deletes a folder. This is a workaround for a bug in `del` where
- * very large folders (with 84K+ files) cause a stack overflow.
- */
-async function recursiveDelete(directory: string) {
- try {
- const entries = await readdirAsync(directory, { withFileTypes: true });
-
- await Promise.all(
- entries.map((entry) => {
- const absolutePath = path.join(directory, entry.name);
- return entry.isDirectory() ? recursiveDelete(absolutePath) : unlinkAsync(absolutePath);
- })
- );
-
- return rmdirAsync(directory);
- } catch (error) {
- if (error.code !== 'ENOENT') {
- throw error;
- }
- }
-}
diff --git a/src/optimize/watch/watch_optimizer.js b/src/optimize/watch/watch_optimizer.js
index 816185e544ab5..000c03ffb34fe 100644
--- a/src/optimize/watch/watch_optimizer.js
+++ b/src/optimize/watch/watch_optimizer.js
@@ -19,7 +19,6 @@
import BaseOptimizer from '../base_optimizer';
import { createBundlesRoute } from '../bundles_route';
-import { DllCompiler } from '../dynamic_dll_plugin';
import { fromRoot } from '../../core/server/utils';
import * as Rx from 'rxjs';
import { mergeMap, take } from 'rxjs/operators';
@@ -35,7 +34,6 @@ export default class WatchOptimizer extends BaseOptimizer {
constructor(opts) {
super(opts);
this.prebuild = opts.prebuild || false;
- this.watchCache = opts.watchCache;
this.status$ = new Rx.ReplaySubject(1);
}
@@ -43,9 +41,6 @@ export default class WatchOptimizer extends BaseOptimizer {
this.initializing = true;
this.initialBuildComplete = false;
- // try reset the watch optimizer cache
- await this.watchCache.tryReset();
-
// log status changes
this.status$.subscribe(this.onStatusChangeHandler);
await this.uiBundles.resetBundleDir();
@@ -120,7 +115,6 @@ export default class WatchOptimizer extends BaseOptimizer {
npUiPluginPublicDirs: npUiPluginPublicDirs,
buildHash,
regularBundlesPath: this.compiler.outputPath,
- dllBundlesPath: DllCompiler.getRawDllConfig().outputPath,
basePublicPath: basePath,
builtCssPath: fromRoot('built_assets/css'),
})
diff --git a/src/plugins/dashboard/README.asciidoc b/src/plugins/dashboard/README.asciidoc
new file mode 100644
index 0000000000000..78163b14c14d6
--- /dev/null
+++ b/src/plugins/dashboard/README.asciidoc
@@ -0,0 +1,5 @@
+[[kibana-dashboard-plugin]]
+== Dashboard plugin
+
+- Registers the dashboard application.
+- Adds a dashboard embeddable that can be used in other applications.
\ No newline at end of file
diff --git a/src/plugins/data/common/field_formats/converters/url.test.ts b/src/plugins/data/common/field_formats/converters/url.test.ts
index 5ee195f8c7752..771bde85626d0 100644
--- a/src/plugins/data/common/field_formats/converters/url.test.ts
+++ b/src/plugins/data/common/field_formats/converters/url.test.ts
@@ -167,8 +167,8 @@ describe('UrlFormat', () => {
});
});
- describe('whitelist', () => {
- test('should assume a relative url if the value is not in the whitelist without a base path', () => {
+ describe('allow-list', () => {
+ test('should assume a relative url if the value is not in the allow-list without a base path', () => {
const parsedUrl = {
origin: 'http://kibana',
basePath: '',
@@ -193,7 +193,7 @@ describe('UrlFormat', () => {
);
});
- test('should assume a relative url if the value is not in the whitelist with a basepath', () => {
+ test('should assume a relative url if the value is not in the allow-list with a basepath', () => {
const parsedUrl = {
origin: 'http://kibana',
basePath: '/xyz',
diff --git a/src/plugins/data/common/field_formats/converters/url.ts b/src/plugins/data/common/field_formats/converters/url.ts
index b797159b53486..2630c97b0821b 100644
--- a/src/plugins/data/common/field_formats/converters/url.ts
+++ b/src/plugins/data/common/field_formats/converters/url.ts
@@ -161,8 +161,8 @@ export class UrlFormat extends FieldFormat {
return this.generateImgHtml(url, imageLabel);
default:
- const inWhitelist = allowedUrlSchemes.some((scheme) => url.indexOf(scheme) === 0);
- if (!inWhitelist && !parsedUrl) {
+ const allowed = allowedUrlSchemes.some((scheme) => url.indexOf(scheme) === 0);
+ if (!allowed && !parsedUrl) {
return url;
}
@@ -178,7 +178,7 @@ export class UrlFormat extends FieldFormat {
* UNSUPPORTED
* - app/kibana
*/
- if (!inWhitelist) {
+ if (!allowed) {
// Handles urls like: `#/discover`
if (url[0] === '#') {
prefix = `${origin}${pathname}`;
diff --git a/src/plugins/data/public/actions/apply_filter_action.ts b/src/plugins/data/public/actions/apply_filter_action.ts
index 7e8ed5ec8fb22..a2621e6ce8802 100644
--- a/src/plugins/data/public/actions/apply_filter_action.ts
+++ b/src/plugins/data/public/actions/apply_filter_action.ts
@@ -22,6 +22,7 @@ import { toMountPoint } from '../../../kibana_react/public';
import { ActionByType, createAction, IncompatibleActionError } from '../../../ui_actions/public';
import { getOverlays, getIndexPatterns } from '../services';
import { applyFiltersPopover } from '../ui/apply_filters';
+import type { IEmbeddable } from '../../../embeddable/public';
import { Filter, FilterManager, TimefilterContract, esFilters } from '..';
export const ACTION_GLOBAL_APPLY_FILTER = 'ACTION_GLOBAL_APPLY_FILTER';
@@ -29,6 +30,7 @@ export const ACTION_GLOBAL_APPLY_FILTER = 'ACTION_GLOBAL_APPLY_FILTER';
export interface ApplyGlobalFilterActionContext {
filters: Filter[];
timeFieldName?: string;
+ embeddable?: IEmbeddable;
}
async function isCompatible(context: ApplyGlobalFilterActionContext) {
diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts
index a0eb49d773f3d..d9aa1b8ec8048 100644
--- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts
+++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts
@@ -22,7 +22,7 @@ import moment from 'moment';
import { esFilters, IFieldType, RangeFilterParams } from '../../../public';
import { getIndexPatterns } from '../../../public/services';
import { deserializeAggConfig } from '../../search/expressions/utils';
-import { RangeSelectContext } from '../../../../embeddable/public';
+import type { RangeSelectContext } from '../../../../embeddable/public';
export async function createFiltersFromRangeSelectAction(event: RangeSelectContext['data']) {
const column: Record = event.table.columns[event.column];
diff --git a/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts
index 1974b9f776748..9429df91f693c 100644
--- a/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts
+++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts
@@ -21,7 +21,7 @@ import { KibanaDatatable } from '../../../../../plugins/expressions/public';
import { deserializeAggConfig } from '../../search/expressions';
import { esFilters, Filter } from '../../../public';
import { getIndexPatterns } from '../../../public/services';
-import { ValueClickContext } from '../../../../embeddable/public';
+import type { ValueClickContext } from '../../../../embeddable/public';
/**
* For terms aggregations on `__other__` buckets, this assembles a list of applicable filter
diff --git a/src/plugins/data/public/actions/index.ts b/src/plugins/data/public/actions/index.ts
index ef9014aafe82d..692996cf6fd19 100644
--- a/src/plugins/data/public/actions/index.ts
+++ b/src/plugins/data/public/actions/index.ts
@@ -17,8 +17,12 @@
* under the License.
*/
-export { ACTION_GLOBAL_APPLY_FILTER, createFilterAction } from './apply_filter_action';
+export {
+ ACTION_GLOBAL_APPLY_FILTER,
+ createFilterAction,
+ ApplyGlobalFilterActionContext,
+} from './apply_filter_action';
export { createFiltersFromValueClickAction } from './filters/create_filters_from_value_click';
export { createFiltersFromRangeSelectAction } from './filters/create_filters_from_range_select';
-export { selectRangeAction } from './select_range_action';
-export { valueClickAction } from './value_click_action';
+export * from './select_range_action';
+export * from './value_click_action';
diff --git a/src/plugins/data/public/actions/select_range_action.ts b/src/plugins/data/public/actions/select_range_action.ts
index 49766143b5588..1781da980dc30 100644
--- a/src/plugins/data/public/actions/select_range_action.ts
+++ b/src/plugins/data/public/actions/select_range_action.ts
@@ -17,60 +17,39 @@
* under the License.
*/
-import { i18n } from '@kbn/i18n';
import {
- createAction,
- IncompatibleActionError,
ActionByType,
+ APPLY_FILTER_TRIGGER,
+ createAction,
+ UiActionsStart,
} from '../../../../plugins/ui_actions/public';
import { createFiltersFromRangeSelectAction } from './filters/create_filters_from_range_select';
-import { RangeSelectContext } from '../../../embeddable/public';
-import { FilterManager, TimefilterContract, esFilters } from '..';
-
-export const ACTION_SELECT_RANGE = 'ACTION_SELECT_RANGE';
+import type { RangeSelectContext } from '../../../embeddable/public';
export type SelectRangeActionContext = RangeSelectContext;
-async function isCompatible(context: SelectRangeActionContext) {
- try {
- return Boolean(await createFiltersFromRangeSelectAction(context.data));
- } catch {
- return false;
- }
-}
+export const ACTION_SELECT_RANGE = 'ACTION_SELECT_RANGE';
-export function selectRangeAction(
- filterManager: FilterManager,
- timeFilter: TimefilterContract
+export function createSelectRangeAction(
+ getStartServices: () => { uiActions: UiActionsStart }
): ActionByType {
return createAction({
type: ACTION_SELECT_RANGE,
id: ACTION_SELECT_RANGE,
- getIconType: () => 'filter',
- getDisplayName: () => {
- return i18n.translate('data.filter.applyFilterActionTitle', {
- defaultMessage: 'Apply filter to current view',
- });
- },
- isCompatible,
- execute: async ({ data }: SelectRangeActionContext) => {
- if (!(await isCompatible({ data }))) {
- throw new IncompatibleActionError();
- }
-
- const selectedFilters = await createFiltersFromRangeSelectAction(data);
-
- if (data.timeFieldName) {
- const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
- data.timeFieldName,
- selectedFilters
- );
- filterManager.addFilters(restOfFilters);
- if (timeRangeFilter) {
- esFilters.changeTimeFilter(timeFilter, timeRangeFilter);
+ shouldAutoExecute: async () => true,
+ execute: async (context: SelectRangeActionContext) => {
+ try {
+ const filters = await createFiltersFromRangeSelectAction(context.data);
+ if (filters.length > 0) {
+ await getStartServices().uiActions.getTrigger(APPLY_FILTER_TRIGGER).exec({
+ filters,
+ embeddable: context.embeddable,
+ timeFieldName: context.data.timeFieldName,
+ });
}
- } else {
- filterManager.addFilters(selectedFilters);
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.warn(`Error [ACTION_SELECT_RANGE]: can\'t extract filters from action context`);
}
},
});
diff --git a/src/plugins/data/public/actions/value_click_action.ts b/src/plugins/data/public/actions/value_click_action.ts
index dd74a7ee507f3..81e62380eacfb 100644
--- a/src/plugins/data/public/actions/value_click_action.ts
+++ b/src/plugins/data/public/actions/value_click_action.ts
@@ -17,98 +17,41 @@
* under the License.
*/
-import { i18n } from '@kbn/i18n';
-import { toMountPoint } from '../../../../plugins/kibana_react/public';
import {
ActionByType,
+ APPLY_FILTER_TRIGGER,
createAction,
- IncompatibleActionError,
+ UiActionsStart,
} from '../../../../plugins/ui_actions/public';
-import { getOverlays, getIndexPatterns } from '../services';
-import { applyFiltersPopover } from '../ui/apply_filters';
import { createFiltersFromValueClickAction } from './filters/create_filters_from_value_click';
-import { ValueClickContext } from '../../../embeddable/public';
-import { Filter, FilterManager, TimefilterContract, esFilters } from '..';
-
-export const ACTION_VALUE_CLICK = 'ACTION_VALUE_CLICK';
+import type { Filter } from '../../common/es_query/filters';
+import type { ValueClickContext } from '../../../embeddable/public';
export type ValueClickActionContext = ValueClickContext;
+export const ACTION_VALUE_CLICK = 'ACTION_VALUE_CLICK';
-async function isCompatible(context: ValueClickActionContext) {
- try {
- const filters: Filter[] = await createFiltersFromValueClickAction(context.data);
- return filters.length > 0;
- } catch {
- return false;
- }
-}
-
-export function valueClickAction(
- filterManager: FilterManager,
- timeFilter: TimefilterContract
+export function createValueClickAction(
+ getStartServices: () => { uiActions: UiActionsStart }
): ActionByType {
return createAction({
type: ACTION_VALUE_CLICK,
id: ACTION_VALUE_CLICK,
- getIconType: () => 'filter',
- getDisplayName: () => {
- return i18n.translate('data.filter.applyFilterActionTitle', {
- defaultMessage: 'Apply filter to current view',
- });
- },
- isCompatible,
- execute: async ({ data }: ValueClickActionContext) => {
- if (!(await isCompatible({ data }))) {
- throw new IncompatibleActionError();
- }
-
- const filters: Filter[] = await createFiltersFromValueClickAction(data);
-
- let selectedFilters = filters;
-
- if (filters.length > 1) {
- const indexPatterns = await Promise.all(
- filters.map((filter) => {
- return getIndexPatterns().get(filter.meta.index!);
- })
- );
-
- const filterSelectionPromise: Promise = new Promise((resolve) => {
- const overlay = getOverlays().openModal(
- toMountPoint(
- applyFiltersPopover(
- filters,
- indexPatterns,
- () => {
- overlay.close();
- resolve([]);
- },
- (filterSelection: Filter[]) => {
- overlay.close();
- resolve(filterSelection);
- }
- )
- ),
- {
- 'data-test-subj': 'selectFilterOverlay',
- }
- );
- });
-
- selectedFilters = await filterSelectionPromise;
- }
-
- if (data.timeFieldName) {
- const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
- data.timeFieldName,
- selectedFilters
- );
- filterManager.addFilters(restOfFilters);
- if (timeRangeFilter) {
- esFilters.changeTimeFilter(timeFilter, timeRangeFilter);
+ shouldAutoExecute: async () => true,
+ execute: async (context: ValueClickActionContext) => {
+ try {
+ const filters: Filter[] = await createFiltersFromValueClickAction(context.data);
+ if (filters.length > 0) {
+ await getStartServices().uiActions.getTrigger(APPLY_FILTER_TRIGGER).exec({
+ filters,
+ embeddable: context.embeddable,
+ timeFieldName: context.data.timeFieldName,
+ });
}
- } else {
- filterManager.addFilters(selectedFilters);
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ `Error [ACTION_EMIT_APPLY_FILTER_TRIGGER]: can\'t extract filters from action context`
+ );
}
},
});
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index 6328e694193c9..846471420327f 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -438,6 +438,8 @@ export {
export { isTimeRange, isQuery, isFilter, isFilters } from '../common';
+export { ApplyGlobalFilterActionContext } from './actions';
+
export * from '../common/field_mapping';
/*
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index 323a32ea362ac..68c0f506f121d 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -69,18 +69,15 @@ import {
createFilterAction,
createFiltersFromValueClickAction,
createFiltersFromRangeSelectAction,
-} from './actions';
-import { ApplyGlobalFilterActionContext } from './actions/apply_filter_action';
-import {
- selectRangeAction,
- SelectRangeActionContext,
+ ApplyGlobalFilterActionContext,
ACTION_SELECT_RANGE,
-} from './actions/select_range_action';
-import {
- valueClickAction,
ACTION_VALUE_CLICK,
+ SelectRangeActionContext,
ValueClickActionContext,
-} from './actions/value_click_action';
+ createValueClickAction,
+ createSelectRangeAction,
+} from './actions';
+
import { SavedObjectsClientPublicToCommon } from './index_patterns';
import { indexPatternLoad } from './index_patterns/expressions/load_index_pattern';
@@ -92,7 +89,14 @@ declare module '../../ui_actions/public' {
}
}
-export class DataPublicPlugin implements Plugin {
+export class DataPublicPlugin
+ implements
+ Plugin<
+ DataPublicPluginSetup,
+ DataPublicPluginStart,
+ DataSetupDependencies,
+ DataStartDependencies
+ > {
private readonly autocomplete: AutocompleteService;
private readonly searchService: SearchService;
private readonly fieldFormatsService: FieldFormatsService;
@@ -110,13 +114,13 @@ export class DataPublicPlugin implements Plugin,
{ expressions, uiActions, usageCollection }: DataSetupDependencies
): DataPublicPluginSetup {
const startServices = createStartServicesGetter(core.getStartServices);
const getInternalStartServices = (): InternalStartServices => {
- const { core: coreStart, self }: any = startServices();
+ const { core: coreStart, self } = startServices();
return {
fieldFormats: self.fieldFormats,
notifications: coreStart.notifications,
@@ -140,12 +144,16 @@ export class DataPublicPlugin implements Plugin ({
+ uiActions: startServices().plugins.uiActions,
+ }))
);
uiActions.addTriggerAction(
VALUE_CLICK_TRIGGER,
- valueClickAction(queryService.filterManager, queryService.timefilter.timefilter)
+ createValueClickAction(() => ({
+ uiActions: startServices().plugins.uiActions,
+ }))
);
return {
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index f8b8cb43b2297..38e0416233e25 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -250,6 +250,20 @@ export class AggParamType extends Ba
makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig;
}
+// Warning: (ae-missing-release-tag) "ApplyGlobalFilterActionContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export interface ApplyGlobalFilterActionContext {
+ // Warning: (ae-forgotten-export) The symbol "IEmbeddable" needs to be exported by the entry point index.d.ts
+ //
+ // (undocumented)
+ embeddable?: IEmbeddable;
+ // (undocumented)
+ filters: Filter[];
+ // (undocumented)
+ timeFieldName?: string;
+}
+
// Warning: (ae-forgotten-export) The symbol "DateNanosFormat" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "DateFormat" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "baseFormattersPublic" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -1443,18 +1457,16 @@ export type PhrasesFilter = Filter & {
meta: PhrasesFilterMeta;
};
+// Warning: (ae-forgotten-export) The symbol "DataSetupDependencies" needs to be exported by the entry point index.d.ts
+// Warning: (ae-forgotten-export) The symbol "DataStartDependencies" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "DataPublicPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export class Plugin implements Plugin_2 {
+export class Plugin implements Plugin_2 {
// Warning: (ae-forgotten-export) The symbol "ConfigSchema" needs to be exported by the entry point index.d.ts
constructor(initializerContext: PluginInitializerContext_2);
- // Warning: (ae-forgotten-export) The symbol "DataSetupDependencies" needs to be exported by the entry point index.d.ts
- //
// (undocumented)
- setup(core: CoreSetup, { expressions, uiActions, usageCollection }: DataSetupDependencies): DataPublicPluginSetup;
- // Warning: (ae-forgotten-export) The symbol "DataStartDependencies" needs to be exported by the entry point index.d.ts
- //
+ setup(core: CoreSetup, { expressions, uiActions, usageCollection }: DataSetupDependencies): DataPublicPluginSetup;
// (undocumented)
start(core: CoreStart_2, { uiActions }: DataStartDependencies): DataPublicPluginStart;
// (undocumented)
diff --git a/src/plugins/dev_tools/public/plugin.ts b/src/plugins/dev_tools/public/plugin.ts
index 3ee44aaa0816e..45fa3634bc87e 100644
--- a/src/plugins/dev_tools/public/plugin.ts
+++ b/src/plugins/dev_tools/public/plugin.ts
@@ -61,7 +61,7 @@ export class DevToolsPlugin implements Plugin {
}),
updater$: this.appStateUpdater,
euiIconType: 'devToolsApp',
- order: 9001,
+ order: 9010,
category: DEFAULT_APP_CATEGORIES.management,
mount: async (params: AppMountParameters) => {
const { element, history } = params;
diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.tsx
index 5f40c55e30e7e..724908281146d 100644
--- a/src/plugins/discover/public/application/components/sidebar/discover_field.tsx
+++ b/src/plugins/discover/public/application/components/sidebar/discover_field.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import React from 'react';
-import { EuiButton, EuiText } from '@elastic/eui';
+import { EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DiscoverFieldDetails } from './discover_field_details';
import { FieldIcon } from '../../../../../kibana_react/public';
@@ -108,6 +108,13 @@ export function DiscoverField({
}
};
+ function wrapOnDot(str?: string) {
+ // u200B is a non-width white-space character, which allows
+ // the browser to efficiently word-wrap right after the dot
+ // without us having to draw a lot of extra DOM elements, etc
+ return str ? str.replace(/\./g, '.\u200B') : '';
+ }
+
return (
<>
-
-
- {useShortDots ? shortenDottedString(field.name) : field.displayName}
-
+
+ {useShortDots ? wrapOnDot(shortenDottedString(field.name)) : wrapOnDot(field.displayName)}
{field.name !== '_source' && !selected && (
diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss
index ae7e915f09773..07efd64752c84 100644
--- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss
+++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss
@@ -23,13 +23,6 @@
margin-bottom: 0;
}
-.dscFieldList--selected,
-.dscFieldList--unpopular,
-.dscFieldList--popular {
- padding-left: $euiSizeS;
- padding-right: $euiSizeS;
-}
-
.dscFieldListHeader {
padding: $euiSizeS $euiSizeS 0 $euiSizeS;
background-color: lightOrDarkTheme(tint($euiColorPrimary, 90%), $euiColorLightShade);
@@ -40,8 +33,7 @@
}
.dscFieldChooser {
- padding-left: $euiSizeS !important;
- padding-right: $euiSizeS !important;
+ padding-left: $euiSize;
}
.dscFieldChooser__toggle {
@@ -55,12 +47,12 @@
display: flex;
align-items: center;
justify-content: space-between;
- padding: 0 2px;
cursor: pointer;
font-size: $euiFontSizeXS;
border-top: solid 1px transparent;
border-bottom: solid 1px transparent;
line-height: normal;
+ margin-bottom: $euiSizeXS * 0.5;
&:hover,
&:focus {
@@ -72,28 +64,25 @@
.dscSidebarItem--active {
border-top: 1px solid $euiColorLightShade;
- background: shade($euiColorLightestShade, 5%);
color: $euiColorFullShade;
- .euiText {
- font-weight: bold;
- }
}
.dscSidebarField {
- padding: $euiSizeXS 0;
+ padding: $euiSizeXS;
display: flex;
- align-items: flex-start;
+ align-items: center;
max-width: 100%;
- margin: 0;
width: 100%;
border: none;
- border-radius: 0;
+ border-radius: $euiBorderRadius - 1px;
text-align: left;
}
.dscSidebarField__name {
margin-left: $euiSizeS;
flex-grow: 1;
+ word-break: break-word;
+ padding-right: 1px;
}
.dscSidebarField__fieldIcon {
diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx
index 96e04c13d70e9..e8ed8b80da3bb 100644
--- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx
+++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx
@@ -19,7 +19,7 @@
import './discover_sidebar.scss';
import React, { useCallback, useEffect, useState, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
-import { EuiButtonIcon, EuiTitle } from '@elastic/eui';
+import { EuiButtonIcon, EuiTitle, EuiSpacer } from '@elastic/eui';
import { sortBy } from 'lodash';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { DiscoverField } from './discover_field';
@@ -199,6 +199,7 @@ export function DiscoverSidebar({
/>
+
{
const sortedActions = [...regularActions, ...extraActions].sort(sortByOrderField);
return await buildContextMenuForActions({
- actions: sortedActions,
- actionContext: { embeddable: this.props.embeddable },
+ actions: sortedActions.map((action) => [action, { embeddable: this.props.embeddable }]),
closeMenu: this.closeMyContextMenuPanel,
});
};
diff --git a/src/plugins/es_ui_shared/public/forms/multi_content/multi_content_context.tsx b/src/plugins/es_ui_shared/public/forms/multi_content/multi_content_context.tsx
index 210b0cedccd06..c5659745f229a 100644
--- a/src/plugins/es_ui_shared/public/forms/multi_content/multi_content_context.tsx
+++ b/src/plugins/es_ui_shared/public/forms/multi_content/multi_content_context.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import React, { useEffect, useCallback, createContext, useContext } from 'react';
+import React, { useEffect, useCallback, createContext, useContext, useRef } from 'react';
import { useMultiContent, HookProps, Content, MultiContent } from './use_multi_content';
@@ -55,7 +55,14 @@ export function useMultiContentContext(contentId: K) {
- const { updateContentAt, saveSnapshotAndRemoveContent, getData } = useMultiContentContext();
+ const isMounted = useRef(false);
+ const defaultValue = useRef(undefined);
+ const {
+ updateContentAt,
+ saveSnapshotAndRemoveContent,
+ getData,
+ getSingleContentData,
+ } = useMultiContentContext();
const updateContent = useCallback(
(content: Content) => {
@@ -71,12 +78,22 @@ export function useContent(contentId: K) {
};
}, [contentId, saveSnapshotAndRemoveContent]);
- const data = getData();
- const defaultValue = data[contentId];
+ useEffect(() => {
+ if (isMounted.current === false) {
+ isMounted.current = true;
+ }
+ }, []);
+
+ if (isMounted.current === false) {
+ // Only read the default value once, on component mount to avoid re-rendering the
+ // consumer each time the multi-content validity ("isValid") changes.
+ defaultValue.current = getSingleContentData(contentId);
+ }
return {
- defaultValue,
+ defaultValue: defaultValue.current!,
updateContent,
getData,
+ getSingleContentData,
};
}
diff --git a/src/plugins/es_ui_shared/public/forms/multi_content/use_multi_content.ts b/src/plugins/es_ui_shared/public/forms/multi_content/use_multi_content.ts
index adc68a39a4a5b..8d470f6454b0e 100644
--- a/src/plugins/es_ui_shared/public/forms/multi_content/use_multi_content.ts
+++ b/src/plugins/es_ui_shared/public/forms/multi_content/use_multi_content.ts
@@ -45,6 +45,7 @@ export interface MultiContent {
updateContentAt: (id: keyof T, content: Content) => void;
saveSnapshotAndRemoveContent: (id: keyof T) => void;
getData: () => T;
+ getSingleContentData: (contentId: K) => T[K];
validate: () => Promise;
validation: Validation;
}
@@ -109,9 +110,22 @@ export function useMultiContent({
};
}, [stateData, validation]);
+ /**
+ * Read a single content data.
+ */
+ const getSingleContentData = useCallback(
+ (contentId: K): T[K] => {
+ if (contents.current[contentId]) {
+ return contents.current[contentId].getData();
+ }
+ return stateData[contentId];
+ },
+ [stateData]
+ );
+
const updateContentValidity = useCallback(
(updatedData: { [key in keyof T]?: boolean | undefined }): boolean | undefined => {
- let allContentValidity: boolean | undefined;
+ let isAllContentValid: boolean | undefined = validation.isValid;
setValidation((prev) => {
if (
@@ -120,7 +134,7 @@ export function useMultiContent({
)
) {
// No change in validation, nothing to update
- allContentValidity = prev.isValid;
+ isAllContentValid = prev.isValid;
return prev;
}
@@ -129,21 +143,21 @@ export function useMultiContent({
...updatedData,
};
- allContentValidity = Object.values(nextContentsValidityState).some(
+ isAllContentValid = Object.values(nextContentsValidityState).some(
(_isValid) => _isValid === undefined
)
? undefined
: Object.values(nextContentsValidityState).every(Boolean);
return {
- isValid: allContentValidity,
+ isValid: isAllContentValid,
contents: nextContentsValidityState,
};
});
- return allContentValidity;
+ return isAllContentValid;
},
- []
+ [validation.isValid]
);
/**
@@ -163,7 +177,7 @@ export function useMultiContent({
}
return Boolean(updateContentValidity(updatedValidation));
- }, [updateContentValidity]);
+ }, [validation.isValid, updateContentValidity]);
/**
* Update a content. It replaces the content in our "contents" map and update
@@ -186,7 +200,7 @@ export function useMultiContent({
});
}
},
- [updateContentValidity, onChange]
+ [updateContentValidity, onChange, getData, validate]
);
/**
@@ -211,6 +225,7 @@ export function useMultiContent({
return {
getData,
+ getSingleContentData,
validate,
validation,
updateContentAt,
diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
index 4c4a7f0642022..4c8e91b13b1b7 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
@@ -29,6 +29,7 @@ interface Props {
export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) => {
const form = useFormContext();
+ const { subscribe } = form;
const previousRawData = useRef(form.__getFormData$().value);
const [formData, setFormData] = useState(previousRawData.current);
@@ -54,9 +55,9 @@ export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) =
);
useEffect(() => {
- const subscription = form.subscribe(onFormData);
+ const subscription = subscribe(onFormData);
return subscription.unsubscribe;
- }, [form.subscribe, onFormData]);
+ }, [subscribe, onFormData]);
return children(formData);
});
diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts
index 1605c09f575f6..3688421964d2e 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { useState, useEffect, useRef } from 'react';
+import { useState, useEffect, useRef, useCallback } from 'react';
import { useFormContext } from '../form_context';
@@ -83,14 +83,18 @@ export const UseArray = ({
const [items, setItems] = useState(initialState);
- const updatePaths = (_rows: ArrayItem[]) =>
- _rows.map(
- (row, index) =>
- ({
- ...row,
- path: `${path}[${index}]`,
- } as ArrayItem)
- );
+ const updatePaths = useCallback(
+ (_rows: ArrayItem[]) => {
+ return _rows.map(
+ (row, index) =>
+ ({
+ ...row,
+ path: `${path}[${index}]`,
+ } as ArrayItem)
+ );
+ },
+ [path]
+ );
const addItem = () => {
setItems((previousItems) => {
@@ -108,11 +112,13 @@ export const UseArray = ({
useEffect(() => {
if (didMountRef.current) {
- setItems(updatePaths(items));
+ setItems((prev) => {
+ return updatePaths(prev);
+ });
} else {
didMountRef.current = true;
}
- }, [path]);
+ }, [path, updatePaths]);
return children({ items, addItem, removeItem });
};
diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx
index 7ad32cb0bc3f0..f00beb470a9fc 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx
@@ -30,8 +30,9 @@ describe('', () => {
const TestComp = ({ onData }: { onData: OnUpdateHandler }) => {
const { form } = useForm();
+ const { subscribe } = form;
- useEffect(() => form.subscribe(onData).unsubscribe, [form]);
+ useEffect(() => subscribe(onData).unsubscribe, [subscribe, onData]);
return (