diff --git a/.eslintignore b/.eslintignore index 357d735e8044bd..1f22b6074e76e0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -9,14 +9,14 @@ bower_components /built_assets /html_docs /src/plugins/data/common/es_query/kuery/ast/_generated_/** -/src/legacy/core_plugins/vis_type_timelion/public/_generated_/** +/src/plugins/vis_type_timelion/public/_generated_/** src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data /src/legacy/ui/public/flot-charts /test/fixtures/scenarios /src/legacy/core_plugins/console/public/webpackShims /src/legacy/core_plugins/console/public/tests/webpackShims /src/legacy/ui/public/utils/decode_geo_hash.js -/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.* +/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.* /src/core/lib/kbn_internal_native_observable /packages/*/target /packages/eslint-config-kibana diff --git a/.eslintrc.js b/.eslintrc.js index 2ce6d279d93a9f..a2b8ae7622d0b0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -109,7 +109,7 @@ module.exports = { }, }, { - files: ['x-pack/legacy/plugins/lens/**/*.{js,ts,tsx}'], + files: ['x-pack/plugins/lens/**/*.{js,ts,tsx}'], rules: { 'react-hooks/exhaustive-deps': 'off', 'react-hooks/rules-of-hooks': 'off', @@ -536,9 +536,15 @@ module.exports = { * ML overrides */ { - files: ['x-pack/legacy/plugins/ml/**/*.js'], + files: ['x-pack/plugins/ml/**/*.js'], rules: { 'no-shadow': 'error', + 'import/no-extraneous-dependencies': [ + 'error', + { + packageDir: './x-pack', + }, + ], }, }, @@ -561,7 +567,7 @@ module.exports = { }, { // typescript only for front and back end - files: ['x-pack/legacy/plugins/siem/**/*.{ts,tsx}'], + files: ['x-pack/{,legacy/}plugins/siem/**/*.{ts,tsx}'], rules: { // This will be turned on after bug fixes are complete // '@typescript-eslint/explicit-member-accessibility': 'warn', @@ -607,7 +613,7 @@ module.exports = { // }, { // typescript and javascript for front and back end - files: ['x-pack/legacy/plugins/siem/**/*.{js,ts,tsx}'], + files: ['x-pack/{,legacy/}plugins/siem/**/*.{js,ts,tsx}'], plugins: ['eslint-plugin-node', 'react'], env: { mocha: true, @@ -728,7 +734,7 @@ module.exports = { * Lens overrides */ { - files: ['x-pack/legacy/plugins/lens/**/*.{ts,tsx}', 'x-pack/plugins/lens/**/*.{ts,tsx}'], + files: ['x-pack/plugins/lens/**/*.{ts,tsx}'], rules: { '@typescript-eslint/no-explicit-any': 'error', }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4994007dcaf274..790abde0bd8c89 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,22 +3,24 @@ # For more info, see https://help.github.com/articles/about-codeowners/ # App -/x-pack/legacy/plugins/lens/ @elastic/kibana-app -/x-pack/legacy/plugins/graph/ @elastic/kibana-app +/x-pack/plugins/lens/ @elastic/kibana-app +/x-pack/plugins/graph/ @elastic/kibana-app /src/legacy/server/url_shortening/ @elastic/kibana-app /src/legacy/server/sample_data/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/discover/ @elastic/kibana-app -/src/legacy/core_plugins/kibana/public/visualize/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app -/src/legacy/core_plugins/metrics/ @elastic/kibana-app /src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app -/src/legacy/core_plugins/vis_type_xy/ @elastic/kibana-app +/src/plugins/vis_type_xy/ @elastic/kibana-app /src/plugins/kibana_legacy/ @elastic/kibana-app -/src/plugins/timelion/ @elastic/kibana-app +/src/plugins/vis_type_timelion/ @elastic/kibana-app /src/plugins/dashboard/ @elastic/kibana-app /src/plugins/discover/ @elastic/kibana-app +/src/plugins/visualize/ @elastic/kibana-app +/src/plugins/vis_type_timeseries/ @elastic/kibana-app +/src/plugins/vis_type_metric/ @elastic/kibana-app +/src/plugins/vis_type_markdown/ @elastic/kibana-app # Core UI # Exclude tutorials folder for now because they are not owned by Kibana app and most will move out soon @@ -80,6 +82,8 @@ /x-pack/legacy/plugins/ingest_manager/ @elastic/ingest-management /x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest-management /x-pack/legacy/plugins/monitoring/ @elastic/stack-monitoring-ui +/x-pack/legacy/plugins/uptime @elastic/uptime +/x-pack/plugins/uptime @elastic/uptime # Machine Learning /x-pack/legacy/plugins/ml/ @elastic/ml-ui @@ -95,6 +99,7 @@ # Maps /x-pack/legacy/plugins/maps/ @elastic/kibana-gis +/x-pack/plugins/maps/ @elastic/kibana-gis /x-pack/test/api_integration/apis/maps/ @elastic/kibana-gis /x-pack/test/functional/apps/maps/ @elastic/kibana-gis /x-pack/test/functional/es_archives/maps/ @elastic/kibana-gis @@ -127,7 +132,6 @@ /packages/kbn-config-schema/ @elastic/kibana-platform /src/legacy/server/config/ @elastic/kibana-platform /src/legacy/server/http/ @elastic/kibana-platform -/src/legacy/server/i18n/ @elastic/kibana-platform /src/legacy/server/logging/ @elastic/kibana-platform /src/legacy/server/saved_objects/ @elastic/kibana-platform /src/legacy/server/status/ @elastic/kibana-platform @@ -146,7 +150,10 @@ /x-pack/test/api_integration/apis/security/ @elastic/kibana-security # Kibana Localization -/src/dev/i18n/ @elastic/kibana-localization +/src/dev/i18n/ @elastic/kibana-localization +/src/legacy/server/i18n/ @elastic/kibana-localization +/src/core/public/i18n/ @elastic/kibana-localization +/packages/kbn-i18n/ @elastic/kibana-localization # Pulse /packages/kbn-analytics/ @elastic/pulse @@ -203,9 +210,12 @@ # Endpoint /x-pack/plugins/endpoint/ @elastic/endpoint-app-team /x-pack/test/api_integration/apis/endpoint/ @elastic/endpoint-app-team +/x-pack/test/endpoint_api_integration_no_ingest/ @elastic/endpoint-app-team /x-pack/test/functional_endpoint/ @elastic/endpoint-app-team /x-pack/test/functional_endpoint_ingest_failure/ @elastic/endpoint-app-team /x-pack/test/functional/es_archives/endpoint/ @elastic/endpoint-app-team +/x-pack/test/plugin_functional/plugins/resolver_test/ @elastic/endpoint-app-team +/x-pack/test/plugin_functional/test_suites/resolver/ @elastic/endpoint-app-team # SIEM /x-pack/legacy/plugins/siem/ @elastic/siem diff --git a/.github/paths-labeller.yml b/.github/paths-labeller.yml index 3d35cd74e0718b..544dd577313df3 100644 --- a/.github/paths-labeller.yml +++ b/.github/paths-labeller.yml @@ -8,3 +8,6 @@ - "Feature:ExpressionLanguage": - "src/plugins/expressions/**/*.*" - "src/plugins/bfetch/**/*.*" + - "Team:uptime": + - "x-pack/plugins/uptime/**/*.*" + - "x-pack/legacy/plugins/uptime/**/*.*" diff --git a/.i18nrc.json b/.i18nrc.json index e18f529b92ac31..4a516f23ebf05d 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -43,18 +43,19 @@ "src/plugins/telemetry_management_section" ], "tileMap": "src/legacy/core_plugins/tile_map", - "timelion": ["src/legacy/core_plugins/timelion", "src/legacy/core_plugins/vis_type_timelion", "src/plugins/timelion"], + "timelion": ["src/legacy/core_plugins/timelion", "src/plugins/vis_type_timelion"], "uiActions": "src/plugins/ui_actions", "visDefaultEditor": "src/plugins/vis_default_editor", - "visTypeMarkdown": "src/legacy/core_plugins/vis_type_markdown", - "visTypeMetric": "src/legacy/core_plugins/vis_type_metric", + "visTypeMarkdown": "src/plugins/vis_type_markdown", + "visTypeMetric": "src/plugins/vis_type_metric", "visTypeTable": "src/legacy/core_plugins/vis_type_table", "visTypeTagCloud": "src/legacy/core_plugins/vis_type_tagcloud", "visTypeTimeseries": ["src/legacy/core_plugins/vis_type_timeseries", "src/plugins/vis_type_timeseries"], "visTypeVega": "src/legacy/core_plugins/vis_type_vega", "visTypeVislib": "src/legacy/core_plugins/vis_type_vislib", "visTypeXy": "src/legacy/core_plugins/vis_type_xy", - "visualizations": "src/plugins/visualizations" + "visualizations": "src/plugins/visualizations", + "visualize": "src/plugins/visualize" }, "exclude": [ "src/legacy/ui/ui_render/ui_render_mixin.js" diff --git a/.sass-lint.yml b/.sass-lint.yml index dd7bc0576692b5..5c2c88a1dad5dc 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -8,10 +8,11 @@ files: - 'x-pack/legacy/plugins/security/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/canvas/**/*.s+(a|c)ss' - 'x-pack/plugins/triggers_actions_ui/**/*.s+(a|c)ss' + - 'x-pack/plugins/lens/**/*.s+(a|c)ss' + - 'x-pack/legacy/plugins/maps/**/*.s+(a|c)ss' + - 'x-pack/plugins/maps/**/*.s+(a|c)ss' ignore: - 'x-pack/legacy/plugins/canvas/shareable_runtime/**/*.s+(a|c)ss' - - 'x-pack/legacy/plugins/lens/**/*.s+(a|c)ss' - - 'x-pack/legacy/plugins/maps/**/*.s+(a|c)ss' rules: quotes: - 2 diff --git a/Jenkinsfile b/Jenkinsfile index 79d3c93006cb6a..6646ee15ba1c29 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -41,7 +41,7 @@ kibanaPipeline(timeoutMinutes: 135, checkPrChanges: true) { 'xpack-ciGroup10': kibanaPipeline.xpackCiGroupProcess(10), 'xpack-accessibility': kibanaPipeline.functionalTestProcess('xpack-accessibility', './test/scripts/jenkins_xpack_accessibility.sh'), 'xpack-siemCypress': { processNumber -> - whenChanged(['x-pack/legacy/plugins/siem/', 'x-pack/test/siem_cypress/']) { + whenChanged(['x-pack/plugins/siem/', 'x-pack/legacy/plugins/siem/', 'x-pack/test/siem_cypress/']) { kibanaPipeline.functionalTestProcess('xpack-siemCypress', './test/scripts/jenkins_siem_cypress.sh')(processNumber) } }, diff --git a/docs/api/saved-objects/create.asciidoc b/docs/api/saved-objects/create.asciidoc index dc010c80fd0121..571b57a5ef9c28 100644 --- a/docs/api/saved-objects/create.asciidoc +++ b/docs/api/saved-objects/create.asciidoc @@ -57,12 +57,12 @@ any data that you send to the API is properly formed. [source,sh] -------------------------------------------------- -$ curl -X POST "localhost:5601/api/saved_objects/index-pattern/my-pattern" +$ curl -X POST "localhost:5601/api/saved_objects/index-pattern/my-pattern" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d ' { "attributes": { "title": "my-pattern-*" } -} +}' -------------------------------------------------- // KIBANA diff --git a/docs/api/saved-objects/export.asciidoc b/docs/api/saved-objects/export.asciidoc index e8c762b9543a18..a992d13ed9b9c8 100644 --- a/docs/api/saved-objects/export.asciidoc +++ b/docs/api/saved-objects/export.asciidoc @@ -68,10 +68,10 @@ Export all index pattern saved objects: [source,sh] -------------------------------------------------- -$ curl -X POST "localhost:5601/api/saved_objects/_export" +$ curl -X POST "localhost:5601/api/saved_objects/_export" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d ' { "type": "index-pattern" -} +}' -------------------------------------------------- // KIBANA @@ -79,11 +79,11 @@ Export all index pattern saved objects and exclude the export summary from the s [source,sh] -------------------------------------------------- -$ curl -X POST "localhost:5601/api/saved_objects/_export" +$ curl -X POST "localhost:5601/api/saved_objects/_export" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d ' { "type": "index-pattern", "excludeExportDetails": true -} +}' -------------------------------------------------- // KIBANA @@ -91,7 +91,7 @@ Export a specific saved object: [source,sh] -------------------------------------------------- -$ curl -X POST "localhost:5601/api/saved_objects/_export" +$ curl -X POST "localhost:5601/api/saved_objects/_export" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d ' { "objects": [ { @@ -99,7 +99,7 @@ $ curl -X POST "localhost:5601/api/saved_objects/_export" "id": "be3733a0-9efe-11e7-acb3-3dab96693fab" } ] -} +}' -------------------------------------------------- // KIBANA @@ -107,7 +107,7 @@ Export a specific saved object and it's related objects : [source,sh] -------------------------------------------------- -$ curl -X POST "localhost:5601/api/saved_objects/_export" +$ curl -X POST "localhost:5601/api/saved_objects/_export" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d ' { "objects": [ { @@ -116,6 +116,6 @@ $ curl -X POST "localhost:5601/api/saved_objects/_export" } ], "includeReferencesDeep": true -} +}' -------------------------------------------------- // KIBANA diff --git a/docs/apm/advanced-queries.asciidoc b/docs/apm/advanced-queries.asciidoc index add6f601489e1e..f89f994e59e574 100644 --- a/docs/apm/advanced-queries.asciidoc +++ b/docs/apm/advanced-queries.asciidoc @@ -1,14 +1,11 @@ +[role="xpack"] [[advanced-queries]] -=== Advanced queries +=== Query your data -When querying in the APM app, you're simply searching and selecting data from fields in Elasticsearch documents. -Queries entered into the query bar are also added as parameters to the URL, -so it's easy to share a specific query or view with others. - -You can begin to see some of the transaction fields available for filtering: - -[role="screenshot"] -image::apm/images/apm-query-bar.png[Example of the Kibana Query bar in APM app in Kibana] +Querying your APM data is a powerful tool that can make finding bottlenecks in your code even easier. +Imagine you have a user that complains about a slow response time in a specific service. +With the query bar, you can easily filter the APM app to only display trace data for that user, +or, to only show transactions that are slower than a specified time threshold. [float] ==== Example APM app queries @@ -17,15 +14,24 @@ image::apm/images/apm-query-bar.png[Example of the Kibana Query bar in APM app i * Filter by response status code: `context.response.status_code >= 400` * Filter by single user ID: `context.user.id : 12` +When querying in the APM app, you're merely searching and selecting data from fields in Elasticsearch documents. +Queries entered into the query bar are also added as parameters to the URL, +so it's easy to share a specific query or view with others. + +When you type, you can begin to see some of the transaction fields available for filtering: + +[role="screenshot"] +image::apm/images/apm-query-bar.png[Example of the Kibana Query bar in APM app in Kibana] + TIP: Read the {kibana-ref}/kuery-query.html[Kibana Query Language Enhancements] documentation to learn more about the capabilities of the {kib} query language. [float] [[discover-advanced-queries]] === Querying in Discover -It may also be helpful to view your APM data in {kibana-ref}/discover.html[*Discover*]. +Alternatively, you can query your APM documents in {kibana-ref}/discover.html[*Discover*]. Querying documents in *Discover* works the same way as querying in the APM app, -and all of the example APM app queries can also be used in *Discover*. +and *Discover* supports all of the example APM app queries shown on this page. [float] ==== Example Discover query @@ -33,7 +39,7 @@ and all of the example APM app queries can also be used in *Discover*. One example where you may want to make use of *Discover*, is for viewing _all_ transactions for an endpoint, instead of just a sample. -TIP: Starting in v7.6, you can view 10 samples per bucket in the APM app, instead of just one. +TIP: Starting in v7.6, you can view ten samples per bucket in the APM app, instead of just one. Use the APM app to find a transaction name and time bucket that you're interested in learning more about. Then, switch to *Discover* and make a search: diff --git a/docs/apm/agent-configuration.asciidoc b/docs/apm/agent-configuration.asciidoc index 0d2834c1a400ef..d911c2154ea4c5 100644 --- a/docs/apm/agent-configuration.asciidoc +++ b/docs/apm/agent-configuration.asciidoc @@ -1,12 +1,16 @@ [role="xpack"] [[agent-configuration]] -=== APM Agent configuration +=== APM Agent central configuration -APM Agent configuration allows you to fine-tune your agent configuration directly in Kibana. -Best of all, changes are automatically propagated to your APM agents so there's no need to redeploy. +++++ +Configure APM agents with central config +++++ -To get started, simply choose the services and environments you wish to configure. -The APM app will let you know when your configurations have been applied by your agents. +APM Agent configuration allows you to fine-tune your agent configuration from within the APM app. +Changes are automatically propagated to your APM agents, so there's no need to redeploy. + +To get started, choose the services and environments you wish to configure. +The APM app will let you know when your agents have applied your configurations. [role="screenshot"] image::apm/images/apm-agent-configuration.png[APM Agent configuration in Kibana] @@ -14,29 +18,28 @@ image::apm/images/apm-agent-configuration.png[APM Agent configuration in Kibana] [float] ==== Precedence -Configurations set with APM Agent configuration take precedence over configurations set locally in the Agent. +Configurations set from the APM app take precedence over configurations set locally in each Agent. However, if APM Server is slow to respond, is offline, reports an error, etc., APM agents will use local defaults until they're able to update the configuration. -For this reason, it is still important to set custom default configurations locally in each of your agents. +For this reason, it is still essential to set custom default configurations locally in each of your agents. [float] ==== APM Server setup This feature requires {apm-server-ref}/setup-kibana-endpoint.html[Kibana endpoint configuration] in APM Server. -Why is additional configuration needed in APM Server? -That's because APM Server acts as a proxy between the agents and Kibana. +APM Server acts as a proxy between the agents and Kibana. Kibana communicates any changed settings to APM Server so that your agents only need to poll APM Server to determine which settings have changed. [float] ==== Supported configurations -Each Agent has its own list of supported configurations. +Each Agent has a list of supported configurations. After selecting a Service name and environment in the APM app, -a list of all available configuration options, +a list of all supported configuration options, including descriptions and default values, will be displayed. -Supported configurations are also marked in each Agent's configuration documentation: +Supported configurations are also tagged with the image:./images/dynamic-config.svg[] badge in each Agent's configuration reference: [horizontal] Go Agent:: {apm-go-ref}/configuration.html[Configuration reference] diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index a8f4f4bf0baaab..93733f5990a46e 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -1,6 +1,10 @@ [role="xpack"] [[apm-api]] -== API +== APM app API + +++++ +REST API +++++ Some APM app features are provided via a REST API: diff --git a/docs/apm/apm-alerts.asciidoc b/docs/apm/apm-alerts.asciidoc index b8552c007b13df..75ce5f56c96c65 100644 --- a/docs/apm/apm-alerts.asciidoc +++ b/docs/apm/apm-alerts.asciidoc @@ -1,12 +1,16 @@ [role="xpack"] [[apm-alerts]] -=== Create an alert +=== Alerts + +++++ +Create an alert +++++ beta::[] -The APM app is integrated with Kibana's {kibana-ref}/alerting-getting-started.html[alerting and actions] feature. -It provides a set of built-in **actions** and APM specific threshold **alerts** for you to use, -and allows all alerts to be centrally managed from <>. +The APM app integrates with Kibana's {kibana-ref}/alerting-getting-started.html[alerting and actions] feature. +It provides a set of built-in **actions** and APM specific threshold **alerts** for you to use +and enables central management of all alerts from <>. [role="screenshot"] image::apm/images/apm-alert.png[Create an alert in the APM app] @@ -28,9 +32,9 @@ This guide creates an alert for the `opbeans-java` service based on the followin From the APM app, navigate to the `opbeans-java` service and select **Alerts** > **Create threshold alert** > **Transaction duration**. -The name of your alert will automatically be set as `Transaction duration | opbeans-java`, -and the alert will be tagged with `apm` and `service.name:opbeans-java`. -Feel free to edit either of these defaults. +`Transaction duration | opbeans-java` is automatically set as the name of the alert, +and `apm` and `service.name:opbeans-java` are added as tags. +It's fine to change the name of the alert, but do not edit the tags. Based on the alert criteria, define the following alert details: @@ -42,7 +46,7 @@ Based on the alert criteria, define the following alert details: * **FOR THE LAST** - `5 minutes` Select an action type. -Multiple action types can be selected, but in this example we want to post to a slack channel. +Multiple action types can be selected, but in this example, we want to post to a Slack channel. Select **Slack** > **Create a connector**. Enter a name for the connector, and paste the webhook URL. @@ -63,9 +67,9 @@ This guide creates an alert for the `opbeans-python` service based on the follow From the APM app, navigate to the `opbeans-python` service and select **Alerts** > **Create threshold alert** > **Error rate**. -The name of your alert will automatically be set as `Error rate | opbeans-python`, -and the alert will be tagged with `apm` and `service.name:opbeans-python`. -Feel free to edit either of these defaults. +`Error rate | opbeans-python` is automatically set as the name of the alert, +and `apm` and `service.name:opbeans-python` are added as tags. +It's fine to change the name of the alert, but do not edit the tags. Based on the alert criteria, define the following alert details: @@ -93,5 +97,5 @@ From this page, you can create, edit, disable, mute, and delete alerts, and crea See {kibana-ref}/alerting-getting-started.html[alerting and actions] for more information. NOTE: If you are using an **on-premise** Elastic Stack deployment with security, -TLS must be configured for communication between Elasticsearch and Kibana. +communication between Elasticsearch and Kibana must have TLS configured. More information is in the alerting {kibana-ref}/alerting-getting-started.html#alerting-setup-prerequisites[prerequisites]. \ No newline at end of file diff --git a/docs/apm/bottlenecks.asciidoc b/docs/apm/bottlenecks.asciidoc deleted file mode 100644 index fbde3e9ddcbd66..00000000000000 --- a/docs/apm/bottlenecks.asciidoc +++ /dev/null @@ -1,25 +0,0 @@ -[role="xpack"] -[[apm-bottlenecks]] -== Visualizing Application Bottlenecks - -Elastic APM captures different types of information from within instrumented applications: - -* {apm-overview-ref-v}/transaction-spans.html[*Spans*] contain information about a specific code path that has been executed. -They measure from the start to end of an activity, -and they can have a parent/child relationship with other spans. -* {apm-overview-ref-v}/transactions.html[*Transactions*] are a special kind of span that have extra metadata associated with them. -You can think of transactions as the highest level of work you’re measuring within a service. -As an example, a transaction could be a request to your server, a batch job, or a custom transaction type. -* {apm-overview-ref-v}/errors.html[*Errors*] contain information about the original exception that occurred or about a log created when the exception occurred. - -Each of these information types have a specific page associated with them in the APM app. -These various pages display the captured data in curated charts and tables that allow you to easily compare and debug your applications. - -For example, you can see information about response times, requests per minute, and status codes per endpoint. -You can even dive into a specific request sample and get a complete waterfall view of what your application is spending its time on. -You might see that your bottlenecks are in database queries, cache calls, or external requests. -For each incoming request and each application error, -you can also see contextual information such as the request header, user information, -system values, or custom data that you manually attached to the request. - -Having access to application-level insights with just a few clicks can drastically decrease the time you spend debugging errors, slow response times, and crashes. diff --git a/docs/apm/custom-links.asciidoc b/docs/apm/custom-links.asciidoc index 75c1c9d0009a2b..4fdf39b643f94b 100644 --- a/docs/apm/custom-links.asciidoc +++ b/docs/apm/custom-links.asciidoc @@ -1,6 +1,11 @@ +[role="xpack"] [[custom-links]] === Custom links +++++ +Create custom links +++++ + Elastic's custom link feature allows you to easily create up to 500 dynamic links based on your specific APM data. Custom links can be filtered to only appear in the APM app for relevant services, @@ -12,7 +17,7 @@ Ready to dive in? Jump straight to the <>. [[custom-links-create]] === Create a link -Each custom link consists of a label, url, and optional filter. +Each custom link consists of a label, URL, and optional filter. The easiest way to create a custom link is from within the actions dropdown in the transaction detail page. This method will automatically apply filters, scoping the link to that specific service, environment, transaction type, and transaction name. @@ -25,8 +30,7 @@ and selecting **Create custom link**. ==== Label The name of your custom link. -This text will be shown in the actions context menu, -so keep it as short as possible. +The actions context menu displays this text, so keep it as short as possible. TIP: Custom links are displayed alphabetically in the actions menu. @@ -39,8 +43,8 @@ URLs support dynamic field name variables, encapsulated in double curly brackets These variables will be replaced with transaction metadata when the link is clicked. Because everyone's data is different, -you'll need to examine your own traces to see what metadata is available for use. -The easiest way to do this is to select a trace in the APM app, and click **Metadata** in the **Trace Sample** table. +you'll need to examine your traces to see what metadata is available for use. +To do this, select a trace in the APM app, and click **Metadata** in the **Trace Sample** table. [role="screenshot"] image::apm/images/example-metadata.png[Example metadata] @@ -49,7 +53,7 @@ image::apm/images/example-metadata.png[Example metadata] [[custom-links-filters]] ==== Filters -Filter each link to only appear so it only appears for specific services or transactions. +Filter each link to only appear for specific services or transactions. You can filter on the following fields: * `service.name` @@ -57,7 +61,7 @@ You can filter on the following fields: * `transaction.type` * `transaction.name` -Multiple values are allowed when comma separated. +Multiple values are allowed when comma-separated. [float] [[custom-links-examples]] @@ -68,7 +72,7 @@ Multiple values are allowed when comma separated. :github-query-params: https://help.github.com/en/github/managing-your-work-on-github/about-automation-for-issues-and-pull-requests-with-query-parameters Not sure where to start with custom links? -Take a look at the examples below, and customize them to your liking! +Take a look at the examples below and customize them to your liking! [float] [[custom-links-examples-email]] diff --git a/docs/apm/deployment-annotations.asciidoc b/docs/apm/deployment-annotations.asciidoc new file mode 100644 index 00000000000000..6feadf8463226b --- /dev/null +++ b/docs/apm/deployment-annotations.asciidoc @@ -0,0 +1,17 @@ +[role="xpack"] +[[transactions-annotations]] +=== Track deployments with annotations + +++++ +Track deployments +++++ + +For enhanced visibility into your deployments, we offer deployment annotations on all transaction charts. +This feature automatically tags new deployments, so you can easily see if your deploy has increased response times +for an end-user, or if the memory/CPU footprint of your application has changed. +Being able to identify bad deployments quickly enables you to rollback and fix issues without causing costly outages. + +Deployment annotations are automatically enabled, and appear when the `service.version` of your app changes. + +[role="screenshot"] +image::apm/images/apm-transaction-annotation.png[Example view of transactions annotation in the APM app in Kibana] diff --git a/docs/apm/error-reports-watcher.asciidoc b/docs/apm/error-reports-watcher.asciidoc new file mode 100644 index 00000000000000..f41597932b751f --- /dev/null +++ b/docs/apm/error-reports-watcher.asciidoc @@ -0,0 +1,18 @@ +[role="xpack"] +[[errors-alerts-with-watcher]] +=== Error reports with Watcher + +++++ +Enable error reports +++++ + +You can use the power of the alerting features with Watcher to get reports on error occurrences. +The Watcher assistant, which is available on the errors overview, can help you set up a watch per service. + +Configure the watch with an occurrences threshold, time interval, and the desired actions, such as email or Slack notifications. +With Watcher, your team can set up reports within minutes. + +Watches are managed separately in the dedicated Watcher UI available in Advanced Settings. + +[role="screenshot"] +image::apm/images/apm-errors-watcher-assistant.png[Example view of the Watcher assistant for errors in APM app in Kibana] diff --git a/docs/apm/errors.asciidoc b/docs/apm/errors.asciidoc index 689fa1fffa89ee..49351ec2558588 100644 --- a/docs/apm/errors.asciidoc +++ b/docs/apm/errors.asciidoc @@ -1,7 +1,8 @@ +[role="xpack"] [[errors]] === Errors overview -TIP: {apm-overview-ref-v}/errors.html[Errors] are defined as groups of exceptions with matching exception or log messages. +TIP: {apm-overview-ref-v}/errors.html[Errors] are groups of exceptions with a similar exception or log message. The *Errors* overview provides a high-level view of the error message and culprit, the number of occurrences, and the most recent occurrence. @@ -20,7 +21,7 @@ image::apm/images/apm-error-group.png[Example view of the error group page in th Here, you'll see the error message, culprit, and the number of occurrences over time. Further down, you'll see the Error occurrence table. -This is where you can see the details of a sampled error within this group. +This table shows the details of a sampled error within this group. The error shown is always the most recent to occur. Each error occurrence features a breakdown of the exception, including the stack trace from when the error occurred, @@ -28,19 +29,4 @@ and additional contextual information to help debug the issue. In some cases, you might also see a Transaction sample ID. This feature allows you to make a connection between the errors and transactions, by linking you to the specific transaction where the error occurred. -This allows you to see the whole trace, including which services the request went through. - -[float] -[[errors-alerts-with-watcher]] -==== Error reports with Watcher - -You can use the power of the alerting features with Watcher to get reports on error occurrences. -The Watcher assistant, which is available on the errors overview, can help you set up a watch per service. - -Configure the watch with an occurrences threshold, time interval, and the desired actions, such as email or Slack notifications. -With Watcher, your team can set up reports within minutes. - -Watches are managed separately in the dedicated Watcher UI available in Advanced Settings. - -[role="screenshot"] -image::apm/images/apm-errors-watcher-assistant.png[Example view of the Watcher assistant for errors in APM app in Kibana] \ No newline at end of file +This allows you to see the whole trace, including which services the request went through. diff --git a/docs/apm/filters.asciidoc b/docs/apm/filters.asciidoc index 99ba827b0198d9..d53adb439f0c8e 100644 --- a/docs/apm/filters.asciidoc +++ b/docs/apm/filters.asciidoc @@ -1,6 +1,11 @@ +[role="xpack"] [[filters]] === Filters +++++ +Filter data +++++ + APM provides two different ways you can filter your data within the APM App: * <> @@ -42,7 +47,7 @@ It allows you to view only relevant data, and is especially useful for separatin By default, all environments are displayed. If there are no environment options, you'll see "not defined". Service environments are defined when configuring your APM agents. -It's very important to be consistent when naming environments in your agents. +It's vital to be consistent when naming environments in your agents. See the documentation for each agent you're using to learn how to configure service environments: * *Go:* {apm-go-ref}/configuration.html#config-environment[`ELASTIC_APM_ENVIRONMENT`] @@ -62,9 +67,9 @@ but only where they are applicable -- they are typically most useful in their or As an example, if you select a host on the Services overview, then select a transaction group, the host filter will still be applied. -These filters are very useful for quickly and easily removing noise from your data. +These filters are very useful for quickly and easily removing noise from your data. With just a click, you can filter your transactions by the transaction result, -host, container ID, and more. +host, container ID, and more. [role="screenshot"] image::apm/images/local-filter.png[Local filters available in the APM app in Kibana] \ No newline at end of file diff --git a/docs/apm/getting-started.asciidoc b/docs/apm/getting-started.asciidoc index 4a391f1a496721..89ce0be1499c5b 100644 --- a/docs/apm/getting-started.asciidoc +++ b/docs/apm/getting-started.asciidoc @@ -1,22 +1,45 @@ [role="xpack"] [[apm-getting-started]] -== Getting Started +== Get started with the APM app -If you have not already installed and configured Elastic APM, -the *Setup Instructions* will get you started. +++++ +Get started +++++ -[role="screenshot"] -image::apm/images/apm-setup.png[Installation instructions on the APM page in Kibana] +Elastic APM captures different types of information from within instrumented applications: +* *Spans* contain information about the execution of a specific code path. +They measure from the start to end of an activity, +and they can have a parent/child relationship with other spans. +* *Transactions* are a special kind of span; +they are the first span for a particular service and have extra metadata associated with them. +As an example, a transaction could be a request to your server, a batch job, or a custom transaction type. +*Traces* link together related transactions to show an end-to-end performance of how a request was served and which services were part of it. +* *Errors* contain information about the original exception that occurred or about a log created when the exception occurred. -Index patterns tell Kibana which Elasticsearch indices you want to explore. -An APM index pattern is necessary for certain features in the APM app, like the query bar. -To set up the correct index pattern, -simply click *Load Kibana objects* at the bottom of the Setup Instructions. +Curated charts and tables display the different types of APM data, which allows you to compare and debug your applications easily. -After you install an Elastic APM agent library in your application, -the application automatically appears in the APM app in {kib}. -No further configuration is required. +* <> +* <> +* <> +* <> +* <> +* <> +* <> -[role="screenshot"] -image::apm/images/apm-index-pattern.png[Setup index pattern for APM in Kibana] +TIP: Want to learn more about the Elastic APM ecosystem? +See the {apm-get-started-ref}/overview.html[APM Overview]. + +include::services.asciidoc[] + +include::traces.asciidoc[] + +include::transactions.asciidoc[] + +include::spans.asciidoc[] + +include::errors.asciidoc[] + +include::metrics.asciidoc[] + +include::service-maps.asciidoc[] diff --git a/docs/apm/how-to-guides.asciidoc b/docs/apm/how-to-guides.asciidoc new file mode 100644 index 00000000000000..9b0efb4f7a3599 --- /dev/null +++ b/docs/apm/how-to-guides.asciidoc @@ -0,0 +1,32 @@ +[role="xpack"] +[[apm-how-to]] +== How-to guides + +Learn how to perform common APM app tasks. + + +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> + + +include::agent-configuration.asciidoc[] + +include::apm-alerts.asciidoc[] + +include::custom-links.asciidoc[] + +include::error-reports-watcher.asciidoc[] + +include::filters.asciidoc[] + +include::machine-learning.asciidoc[] + +include::advanced-queries.asciidoc[] + +include::deployment-annotations.asciidoc[] \ No newline at end of file diff --git a/docs/apm/images/dynamic-config.svg b/docs/apm/images/dynamic-config.svg new file mode 100644 index 00000000000000..df62a3c84f4b45 --- /dev/null +++ b/docs/apm/images/dynamic-config.svg @@ -0,0 +1 @@ + DynamicDynamic \ No newline at end of file diff --git a/docs/apm/index.asciidoc b/docs/apm/index.asciidoc index d3f0dc5b7f11f2..79190efccdff29 100644 --- a/docs/apm/index.asciidoc +++ b/docs/apm/index.asciidoc @@ -4,25 +4,35 @@ [partintro] -- -Elastic Application Performance Monitoring (APM) automatically collects in-depth -performance metrics and errors from inside your applications. - -The **APM** app in {kib} is provided with the basic license. It -enables developers to drill down into the performance data for their applications -and quickly locate the performance bottlenecks. - -* <> -* <> -* <> - -NOTE: For more information about the components of Elastic APM, -see the {apm-get-started-ref}/overview.html[APM Overview]. +The APM app in {kib} is provided with the basic license. +It allows you to monitor your software services and applications in real-time; +visualize detailed performance information on your services, +identify and analyze errors, +and monitor host-level and agent-specific metrics like JVM and Go runtime metrics. + +[float] +[[apm-bottlenecks]] +== Visualizing application bottlenecks + +Having access to application-level insights with just a few clicks can drastically decrease the time you spend +debugging errors, slow response times, and crashes. + +For example, you can see information about response times, requests per minute, and status codes per endpoint. +You can even dive into a specific request sample and get a complete waterfall view of what your application is spending its time on. +You might see that your bottlenecks are in database queries, cache calls, or external requests. +For each incoming request and each application error, +you can also see contextual information such as the request header, user information, +system values, or custom data that you manually attached to the request. -- +include::set-up.asciidoc[] + include::getting-started.asciidoc[] -include::bottlenecks.asciidoc[] +include::how-to-guides.asciidoc[] -include::using-the-apm-ui.asciidoc[] +include::settings.asciidoc[] include::api.asciidoc[] + +include::troubleshooting.asciidoc[] diff --git a/docs/apm/machine-learning.asciidoc b/docs/apm/machine-learning.asciidoc new file mode 100644 index 00000000000000..9d347fc4f1111c --- /dev/null +++ b/docs/apm/machine-learning.asciidoc @@ -0,0 +1,27 @@ +[role="xpack"] +[[machine-learning-integration]] +=== Machine Learning integration + +++++ +Integrate with machine learning +++++ + +The Machine Learning integration will initiate a new job predefined to calculate anomaly scores on transaction response times. +The response time graph will show the expected bounds and add an annotation when the anomaly score is 75 or above. +Jobs can be created per transaction type, and based on the average response time. +Manage jobs in the *Machine Learning jobs management*. + +[role="screenshot"] +image::apm/images/apm-ml-integration.png[Example view of anomaly scores on response times in APM app in Kibana] + +[float] +[[create-ml-integration]] +=== Create a new machine learning job + +To enable machine learning anomaly detection, first choose a service to monitor. +Then, select **Integrations** > **Enable ML anomaly detection** and click **Create job**. +That's it! After a few minutes, the job will begin calculating results; +it might take additional time for results to appear on your graph. + +APM specific anomaly detection wizards are also available for certain Agents. +See the machine learning {ml-docs}/ootb-ml-jobs-apm.html[APM anomaly detection configurations] for more information. diff --git a/docs/apm/metrics.asciidoc b/docs/apm/metrics.asciidoc index ab394b785ef84a..e82a4fbd5c291b 100644 --- a/docs/apm/metrics.asciidoc +++ b/docs/apm/metrics.asciidoc @@ -1,3 +1,4 @@ +[role="xpack"] [[metrics]] === Metrics overview @@ -5,7 +6,7 @@ The *Metrics* overview provides agent-specific metrics, which lets you perform more in-depth root cause analysis investigations within the APM app. If you're experiencing a problem with your service, you can use this page to attempt to find the underlying cause. -For example, you might be able to correlate a high number of errors with a long transaction duration, high CPU usage, or a memory leak. +For example, you might be able to correlate a high number of errors with a long transaction duration, high CPU usage, or a memory leak. [role="screenshot"] image::apm/images/apm-metrics.png[Example view of the Metrics overview in APM app in Kibana] @@ -17,19 +18,3 @@ thread count, garbage collection rate, and garbage collection time spent per min [role="screenshot"] image::apm/images/jvm-metrics.png[Example view of the Metrics overview for the Java Agent] - -[[machine-learning-integration]] -=== Machine Learning integration - -The Machine Learning integration will initiate a new job predefined to calculate anomaly scores on transaction response times. -The response time graph will show the expected bounds and annotate the graph when the anomaly score is 75 or above. - -[role="screenshot"] -image::apm/images/apm-ml-integration.png[Example view of anomaly scores on response times in APM app in Kibana] - -Jobs can be created per transaction type and based on the average response time. -You can manage jobs in the *Machine Learning jobs management*. -It might take some time for results to appear on the graph. - -Machine learning is a platinum feature. For a comparison of the Elastic license levels, -see https://www.elastic.co/subscriptions[the subscription page]. \ No newline at end of file diff --git a/docs/apm/service-maps.asciidoc b/docs/apm/service-maps.asciidoc index e0d84f33b4dcb9..be86b9d522ac5e 100644 --- a/docs/apm/service-maps.asciidoc +++ b/docs/apm/service-maps.asciidoc @@ -1,37 +1,53 @@ +[role="xpack"] [[service-maps]] === Service maps beta::[] -A service map is a real-time diagram of the interactions occurring in your application’s architecture. -It allows you to easily visualize data flow and high-level statistics, like average transaction duration, -requests per minute, errors per minute, and metrics, allowing you to quickly assess the status of your services. +WARNING: Service map support for Internet Explorer 11 is extremely limited. +Please use Chrome or Firefox if available. -Our beta offering creates two types of service maps: +A service map is a real-time visual representation of the instrumented services in your application's architecture. +It shows you how these services are connected, along with high-level metrics like average transaction duration, +requests per minute, and errors per minute, that allow you to quickly assess the status of your services. -* Global: All services and connections are shown. -* Service-specific: Selecting a specific service will highlight it's connections. +We currently surface two types of service maps: + +* Global: All services instrumented with APM agents and the connections between them are shown. +* Service-specific: Highlight connections for a selected service. [role="screenshot"] image::apm/images/service-maps.png[Example view of service maps in the APM app in Kibana] +[float] +[[service-maps-how]] +=== How do service maps work? + +Service maps rely on distributed traces to draw connections between services. +As {apm-overview-ref-v}/distributed-tracing.html[distributed tracing] is enabled out-of-the-box for supported technologies, so are service maps. +However, if a service isn't instrumented, +or a `traceparent` header isn't being propagated to it, +distributed tracing will not work, and the connection will not be drawn on the map. + [float] [[visualize-your-architecture]] === Visualize your architecture Select the **Service Map** tab to get started. -By default, all services and connections are shown. -Whether your onboarding a new engineer, or just trying to grasp the big picture, +By default, all instrumented services and connections are shown. +Whether you're onboarding a new engineer, or just trying to grasp the big picture, click around, zoom in and out, and begin to visualize how your services are connected. If there's a specific service that interests you, select that service to highlight its connections. Clicking **Focus map** will refocus the map on that specific service and lock the connection highlighting. -From here, select **Service Details**, or click on the **Transaction** tab to jump to the Transaction overview. +From here, select **Service Details**, or click on the **Transaction** tab to jump to the Transaction overview +for the selected service. You can also use the tabs at the top of the page to easily jump to the **Errors** or **Metrics** overview. -While it's not possible to query in service maps, it is possible to filter by environment. +Filter out your maps by picking the environment from the environment drop-down filter. This can be useful if you have two or more services, in separate environments, but with the same name. -Use the environment drop down to only see the data you're interested in, like `dev` or `production`. +Use the environment drop-down to only see the data you're interested in, like `dev` or `production`. +Additional filters are not currently available for service maps. [role="screenshot"] image::apm/images/service-maps-java.png[Example view of service maps with Java highlighted in the APM app in Kibana] @@ -46,3 +62,18 @@ Nodes appear on the map in one of two shapes: * **Diamond**: Databases, external, and messaging. Interior icons represent the generic type, with specific icons for known entities, like Elasticsearch. Type and subtype are based on `span.type`, and `span.subtype`. + +[float] +[[service-maps-supported]] +=== Supported APM Agents + +Service maps are supported for the following Agent versions: + +[horizontal] +Go Agent:: >= v1.7.0 +Java Agent:: >= v1.13.0 +.NET Agent:: >= v1.3.0 +Node.js Agent:: >= v3.6.0 +Python Agent:: >= v5.5.0 +Ruby Agent:: >= v3.6.0 +Real User Monitoring (RUM) Agent:: >= v4.7.0 diff --git a/docs/apm/services.asciidoc b/docs/apm/services.asciidoc index 9af3e74562dab4..395e23c3793064 100644 --- a/docs/apm/services.asciidoc +++ b/docs/apm/services.asciidoc @@ -1,9 +1,9 @@ +[role="xpack"] [[services]] === Services overview -The *Services* overview gives you quick insights into the health and general performance of each service. - -You can add services by setting the `service.name` configuration in each of the {apm-agents-ref}[APM agents] you’re instrumenting. +The *Services* overview gives you quick insights into the health and general performance of all of your instrumented services. +Services are sorted by the `service.name` configured in each of the {apm-agents-ref}[APM agents] you’ve installed. [role="screenshot"] image::apm/images/apm-services-overview.png[Example view of services table the APM app in Kibana] \ No newline at end of file diff --git a/docs/apm/set-up.asciidoc b/docs/apm/set-up.asciidoc new file mode 100644 index 00000000000000..c5bf5e13b640b1 --- /dev/null +++ b/docs/apm/set-up.asciidoc @@ -0,0 +1,35 @@ +[role="xpack"] +[[apm-ui]] +== Set up the APM app + +++++ +Set up +++++ + +APM is available via the navigation sidebar in {Kib}. +If you have not already installed and configured Elastic APM, +the *Setup Instructions* in Kibana will get you started. + +[role="screenshot"] +image::apm/images/apm-setup.png[Installation instructions on the APM page in Kibana] + +[float] +[[apm-configure-index-pattern]] +=== Load the index pattern + +Index patterns tell Kibana which Elasticsearch indices you want to explore. +An APM index pattern is necessary for certain features in the APM app, like the query bar. +To set up the correct index pattern, +simply click *Load Kibana objects* at the bottom of the Setup Instructions. + +[role="screenshot"] +image::apm/images/apm-index-pattern.png[Setup index pattern for APM in Kibana] + +To use a custom index pattern, see <>. + +[float] +[[apm-getting-started-next]] +=== Next steps + +No further configuration in the APM app is required. +Install an APM Agent library in your service to begin visualizing and analyzing your data! diff --git a/docs/apm/settings.asciidoc b/docs/apm/settings.asciidoc index 37122fc9c635d0..44da63143f63f8 100644 --- a/docs/apm/settings.asciidoc +++ b/docs/apm/settings.asciidoc @@ -1,18 +1,23 @@ // Do not link directly to this page. // Link to the anchor in `/docs/settings/apm-settings.asciidoc` instead. +[role="xpack"] [[apm-settings-in-kibana]] -=== APM settings in Kibana +== APM app settings + +++++ +Settings +++++ You do not need to configure any settings to use the APM app. It is enabled by default. [float] [[apm-indices-settings]] -==== APM Indices +=== APM Indices include::./../settings/apm-settings.asciidoc[tag=apm-indices-settings] [float] [[general-apm-settings]] -==== General APM settings +=== General APM settings include::./../settings/apm-settings.asciidoc[tag=general-apm-settings] diff --git a/docs/apm/spans.asciidoc b/docs/apm/spans.asciidoc index ef21e1c5333e04..2eed339160fc4f 100644 --- a/docs/apm/spans.asciidoc +++ b/docs/apm/spans.asciidoc @@ -1,7 +1,8 @@ +[role="xpack"] [[spans]] === Span timeline -TIP: A {apm-overview-ref-v}/transaction-spans.html[span] is defined as the duration of a single event. +TIP: A {apm-overview-ref-v}/transaction-spans.html[span] is the duration of a single event. Spans are automatically captured by APM agents, and you can also define custom spans. Each span has a type and is defined by a different color in the timeline/waterfall visualization. @@ -28,7 +29,7 @@ Services in a distributed trace are separated by color and listed in the order t [role="screenshot"] image::apm/images/apm-services-trace.png[Example of distributed trace colors in the APM app in Kibana] -Don't forget, a distributed trace includes more than one transaction. +Don't forget; a distributed trace includes more than one transaction. When viewing these distributed traces in the timeline waterfall, you'll see this image:apm/images/transaction-icon.png[APM icon] icon, which indicates the next transaction in the trace. These transactions can be expanded and viewed in detail by clicking on them. diff --git a/docs/apm/traces.asciidoc b/docs/apm/traces.asciidoc index 09d8f52b92840c..8eef3d9bed4db1 100644 --- a/docs/apm/traces.asciidoc +++ b/docs/apm/traces.asciidoc @@ -1,12 +1,17 @@ +[role="xpack"] [[traces]] === Traces overview +TIP: Traces link together related transactions to show an end-to-end performance of how a request was served +and which services were part of it. +In addition to the Traces overview, you can view your application traces in the <>. + The *Traces* overview displays the entry transaction for all traces in your application. If you're using <>, this view is key to finding the critical paths within your application. Transactions with the same name are grouped together and only shown once in this table. By default, transactions are sorted by _Impact_. -Impact helps show the most used and slowest endpoints in your service - in other words, +Impact helps show the most used and slowest endpoints in your service--in other words, it's the collective amount of pain a specific endpoint is causing your users. If there's a particular endpoint you're worried about, you can click on it to view the <>. @@ -33,4 +38,4 @@ You can use the <> to view a waterfall displa [role="screenshot"] image::apm/images/apm-distributed-tracing.png[Example view of the distributed tracing in APM app in Kibana] -TIP: Distributed tracing is supported by all APM agents and there’s no additional configuration needed. \ No newline at end of file +TIP: Distributed tracing is supported by all APM agents, and there's no additional configuration needed. \ No newline at end of file diff --git a/docs/apm/transactions.asciidoc b/docs/apm/transactions.asciidoc index 1eb037009efffe..2e1022e6d684cf 100644 --- a/docs/apm/transactions.asciidoc +++ b/docs/apm/transactions.asciidoc @@ -1,3 +1,4 @@ +[role="xpack"] [[transactions]] === Transaction overview @@ -56,20 +57,6 @@ For further details, including troubleshooting and custom implementation instruc refer to the documentation for each {apm-agents-ref}[APM Agent] you've implemented. ==== -[[transactions-annotations]] -==== Transaction annotations - -For enhanced visibility into your deployments, we offer deployment annotations on all transaction charts. -This feature automatically tags new deployments, so you can easily see if your deploy has increased response times -for an end-user, or if the memory/CPU footprint of your application has increased. -Being able to quickly identify bad deployments enables you to rollback and fix issues without causing costly outages. - -Deployment annotations are automatically enabled, and appear when the `service.version` of your app changes. - -[role="screenshot"] -image::apm/images/apm-transaction-annotation.png[Example view of transactions annotation in the APM app in Kibana] - - [[rum-transaction-overview]] ==== RUM Transaction overview @@ -82,7 +69,7 @@ image::apm/images/apm-geo-ui.jpg[average page load duration distribution] This data is available due to the geo-ip and user agent pipelines being enabled by default, which allows for the capture of geo-location and user agent data. These visualizations make it easy for you to visualize performance information about your -end users' experience based on their location. +end-users' experience based on their location. [[transaction-details]] ==== Transaction details @@ -103,7 +90,7 @@ The number of requests per bucket is displayed when hovering over the graph, and [role="screenshot"] image::apm/images/apm-transaction-duration-dist.png[Example view of transactions duration distribution graph] -This graph shows a typical distribution, and indicates most of our requests were served quickly - awesome! +This graph shows a typical distribution, and indicates most of our requests were served quickly--awesome! It's the requests on the right, the ones taking longer than average, that we probably want to focus on. When you select one of these buckets, diff --git a/docs/apm/troubleshooting.asciidoc b/docs/apm/troubleshooting.asciidoc index c6174e1786c78f..eb4fb790afd7fd 100644 --- a/docs/apm/troubleshooting.asciidoc +++ b/docs/apm/troubleshooting.asciidoc @@ -1,19 +1,24 @@ -[[troubleshooting]] -=== Troubleshooting common problems +[[troubleshooting]] +== Troubleshoot common problems + +++++ +Troubleshooting +++++ If you have something to add to this section, please consider creating a pull request with your proposed changes at https://github.com/elastic/kibana. -Also check out the https://discuss.elastic.co/c/apm[APM discussion forum]. +Also, check out the https://discuss.elastic.co/c/apm[APM discussion forum]. +[float] [[no-apm-data-found]] -==== No APM data found +=== No APM data found This section can help with any of the following: * Data isn't displaying in the APM app -* You're seeing a message like "No Services Found", -* You're seeing errors like "Fielddata is disabled on text fields by default..." +* You see a message like "No Services Found", +* You see errors like "Fielddata is disabled on text fields by default..." There are a number of factors that could be at play here. One important thing to double-check first is your index template. @@ -52,12 +57,13 @@ you can customize the indices that the APM app uses to display data. Navigate to *APM* > *Settings* > *Indices*, and change all `apm_oss.*Pattern` values to include the new index pattern. For example: `customIndexName-*`. -==== Unknown route +[float] +=== Unknown route The {apm-app-ref}/transactions.html[transaction overview] will only display helpful information when the transactions in your services are named correctly. If you're seeing "GET unknown route" or "unknown route" in the APM app, -it could be a sign that something isn't working like it should. +it could be a sign that something isn't working as it should. Elastic APM Agents come with built-in support for popular frameworks out-of-the-box. This means, among other things, that the Agent will try to automatically name HTTP requests. @@ -71,7 +77,8 @@ To resolve this, you'll need to head over to the relevant {apm-agents-ref}[Agent Specifically, view the Agent's supported technologies page. You can also use the Agent's public API to manually set a name for the transaction. -==== Fields are not searchable +[float] +=== Fields are not searchable In Elasticsearch, index templates are used to define settings and mappings that determine how fields should be analyzed. The recommended index template file for APM Server is installed by the APM Server packages. @@ -92,7 +99,7 @@ Selecting the `apm-*` index pattern shows a listing of every field defined in th *Ensure a field is searchable* There are two things you can do to if you'd like to ensure a field is searchable: -1. Index your additional data as {apm-overview-ref}/metadata.html[labels] instead. +1. Index your additional data as {apm-overview-ref-v}/metadata.html[labels] instead. These are dynamic by default, which means they will be indexed and become searchable and aggregatable. 2. Use the {apm-server-ref}/configuration-template.html[`append_fields`] feature. As an example, diff --git a/docs/apm/using-the-apm-ui.asciidoc b/docs/apm/using-the-apm-ui.asciidoc deleted file mode 100644 index 904718999069d5..00000000000000 --- a/docs/apm/using-the-apm-ui.asciidoc +++ /dev/null @@ -1,51 +0,0 @@ -[role="xpack"] -[[apm-ui]] -== Using APM - -APM is designed to be as intuitive as possible, -but you might come across certain terms or concepts that don’t feel native to you. -Not to worry, we've created this guide to help you get the most out of Elastic APM. - -APM is available via the navigation sidebar in {Kib}. - -* <> -* <> -* <> -* <> -* <> -* <> -* <> -* <> -* <> -* <> -* <> -* <> -* <> - -include::filters.asciidoc[] - -include::services.asciidoc[] - -include::traces.asciidoc[] - -include::transactions.asciidoc[] - -include::spans.asciidoc[] - -include::service-maps.asciidoc[] - -include::errors.asciidoc[] - -include::metrics.asciidoc[] - -include::apm-alerts.asciidoc[] - -include::agent-configuration.asciidoc[] - -include::custom-links.asciidoc[] - -include::advanced-queries.asciidoc[] - -include::settings.asciidoc[] - -include::troubleshooting.asciidoc[] diff --git a/docs/development/core/server/kibana-plugin-core-server.coresetup.http.md b/docs/development/core/server/kibana-plugin-core-server.coresetup.http.md index dcc1d754feb7cd..a8028827cc0a46 100644 --- a/docs/development/core/server/kibana-plugin-core-server.coresetup.http.md +++ b/docs/development/core/server/kibana-plugin-core-server.coresetup.http.md @@ -9,5 +9,7 @@ Signature: ```typescript -http: HttpServiceSetup; +http: HttpServiceSetup & { + resources: HttpResources; + }; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.coresetup.md b/docs/development/core/server/kibana-plugin-core-server.coresetup.md index c10b460da8b4f7..30c054345928bf 100644 --- a/docs/development/core/server/kibana-plugin-core-server.coresetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.coresetup.md @@ -20,7 +20,7 @@ export interface CoreSetupContextSetup | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) | | [elasticsearch](./kibana-plugin-core-server.coresetup.elasticsearch.md) | ElasticsearchServiceSetup | [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) | | [getStartServices](./kibana-plugin-core-server.coresetup.getstartservices.md) | StartServicesAccessor<TPluginsStart, TStart> | [StartServicesAccessor](./kibana-plugin-core-server.startservicesaccessor.md) | -| [http](./kibana-plugin-core-server.coresetup.http.md) | HttpServiceSetup | [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) | +| [http](./kibana-plugin-core-server.coresetup.http.md) | HttpServiceSetup & {
resources: HttpResources;
} | [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) | | [metrics](./kibana-plugin-core-server.coresetup.metrics.md) | MetricsServiceSetup | [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) | | [savedObjects](./kibana-plugin-core-server.coresetup.savedobjects.md) | SavedObjectsServiceSetup | [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) | | [status](./kibana-plugin-core-server.coresetup.status.md) | StatusServiceSetup | [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) | diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresources.md b/docs/development/core/server/kibana-plugin-core-server.httpresources.md new file mode 100644 index 00000000000000..cb3170e989e172 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresources.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResources](./kibana-plugin-core-server.httpresources.md) + +## HttpResources interface + +HttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP. Provides API allowing plug-ins to respond with: - a pre-configured HTML page bootstrapping Kibana client app - custom HTML page - custom JS script file. + +Signature: + +```typescript +export interface HttpResources +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [register](./kibana-plugin-core-server.httpresources.register.md) | <P, Q, B>(route: RouteConfig<P, Q, B, 'get'>, handler: HttpResourcesRequestHandler<P, Q, B>) => void | To register a route handler executing passed function to form response. | + diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md b/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md new file mode 100644 index 00000000000000..fe3803a6ffe52a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResources](./kibana-plugin-core-server.httpresources.md) > [register](./kibana-plugin-core-server.httpresources.register.md) + +## HttpResources.register property + +To register a route handler executing passed function to form response. + +Signature: + +```typescript +register: (route: RouteConfig, handler: HttpResourcesRequestHandler) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.headers.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.headers.md new file mode 100644 index 00000000000000..bb6dec504ff425 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.headers.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesRenderOptions](./kibana-plugin-core-server.httpresourcesrenderoptions.md) > [headers](./kibana-plugin-core-server.httpresourcesrenderoptions.headers.md) + +## HttpResourcesRenderOptions.headers property + +HTTP Headers with additional information about response. + +Signature: + +```typescript +headers?: ResponseHeaders; +``` + +## Remarks + +All HTML pages are already pre-configured with `content-security-policy` header that cannot be overridden. + diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.md new file mode 100644 index 00000000000000..6563e3c636a99e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrenderoptions.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesRenderOptions](./kibana-plugin-core-server.httpresourcesrenderoptions.md) + +## HttpResourcesRenderOptions interface + +Allows to configure HTTP response parameters + +Signature: + +```typescript +export interface HttpResourcesRenderOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [headers](./kibana-plugin-core-server.httpresourcesrenderoptions.headers.md) | ResponseHeaders | HTTP Headers with additional information about response. | + diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md new file mode 100644 index 00000000000000..20f930382955e4 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesRequestHandler](./kibana-plugin-core-server.httpresourcesrequesthandler.md) + +## HttpResourcesRequestHandler type + +Extended version of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) having access to [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) to respond with HTML or JS resources. + +Signature: + +```typescript +export declare type HttpResourcesRequestHandler

= RequestHandler; +``` + +## Example + +\`\`\`typescript httpResources.register({ path: '/login', validate: { params: schema.object({ id: schema.string() }), }, }, async (context, request, response) => { //.. return response.renderCoreApp(); }); + diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesresponseoptions.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesresponseoptions.md new file mode 100644 index 00000000000000..2ea3ea7e58c786 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesresponseoptions.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesResponseOptions](./kibana-plugin-core-server.httpresourcesresponseoptions.md) + +## HttpResourcesResponseOptions type + +HTTP Resources response parameters + +Signature: + +```typescript +export declare type HttpResourcesResponseOptions = HttpResponseOptions; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.md new file mode 100644 index 00000000000000..1c221e13f534fd --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) + +## HttpResourcesServiceToolkit interface + +Extended set of [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) helpers used to respond with HTML or JS resource. + +Signature: + +```typescript +export interface HttpResourcesServiceToolkit +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [renderAnonymousCoreApp](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md) | (options?: HttpResourcesRenderOptions) => Promise<IKibanaResponse> | To respond with HTML page bootstrapping Kibana application without retrieving user-specific information. | +| [renderCoreApp](./kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md) | (options?: HttpResourcesRenderOptions) => Promise<IKibanaResponse> | To respond with HTML page bootstrapping Kibana application. | +| [renderHtml](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md) | (options: HttpResourcesResponseOptions) => IKibanaResponse | To respond with a custom HTML page. | +| [renderJs](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md) | (options: HttpResourcesResponseOptions) => IKibanaResponse | To respond with a custom JS script file. | + diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md new file mode 100644 index 00000000000000..3dce9d88c80363 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) > [renderAnonymousCoreApp](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md) + +## HttpResourcesServiceToolkit.renderAnonymousCoreApp property + +To respond with HTML page bootstrapping Kibana application without retrieving user-specific information. + +Signature: + +```typescript +renderAnonymousCoreApp: (options?: HttpResourcesRenderOptions) => Promise; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md new file mode 100644 index 00000000000000..eb4f095bc19bed --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) > [renderCoreApp](./kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md) + +## HttpResourcesServiceToolkit.renderCoreApp property + +To respond with HTML page bootstrapping Kibana application. + +Signature: + +```typescript +renderCoreApp: (options?: HttpResourcesRenderOptions) => Promise; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md new file mode 100644 index 00000000000000..325d19625df449 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) > [renderHtml](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderhtml.md) + +## HttpResourcesServiceToolkit.renderHtml property + +To respond with a custom HTML page. + +Signature: + +```typescript +renderHtml: (options: HttpResourcesResponseOptions) => IKibanaResponse; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md new file mode 100644 index 00000000000000..f8d4418fc6cba0 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) > [renderJs](./kibana-plugin-core-server.httpresourcesservicetoolkit.renderjs.md) + +## HttpResourcesServiceToolkit.renderJs property + +To respond with a custom JS script file. + +Signature: + +```typescript +renderJs: (options: HttpResourcesResponseOptions) => IKibanaResponse; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.handlelegacyerrors.md b/docs/development/core/server/kibana-plugin-core-server.irouter.handlelegacyerrors.md index 94cf3c94187b0d..35d109975c83a6 100644 --- a/docs/development/core/server/kibana-plugin-core-server.irouter.handlelegacyerrors.md +++ b/docs/development/core/server/kibana-plugin-core-server.irouter.handlelegacyerrors.md @@ -9,5 +9,5 @@ Wrap a router handler to catch and converts legacy boom errors to proper custom Signature: ```typescript -handleLegacyErrors: (handler: RequestHandler) => RequestHandler; +handleLegacyErrors: RequestHandlerWrapper; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.md b/docs/development/core/server/kibana-plugin-core-server.irouter.md index 073f02f1a4191b..4bade638a65a57 100644 --- a/docs/development/core/server/kibana-plugin-core-server.irouter.md +++ b/docs/development/core/server/kibana-plugin-core-server.irouter.md @@ -18,7 +18,7 @@ export interface IRouter | --- | --- | --- | | [delete](./kibana-plugin-core-server.irouter.delete.md) | RouteRegistrar<'delete'> | Register a route handler for DELETE request. | | [get](./kibana-plugin-core-server.irouter.get.md) | RouteRegistrar<'get'> | Register a route handler for GET request. | -| [handleLegacyErrors](./kibana-plugin-core-server.irouter.handlelegacyerrors.md) | <P, Q, B>(handler: RequestHandler<P, Q, B>) => RequestHandler<P, Q, B> | Wrap a router handler to catch and converts legacy boom errors to proper custom errors. | +| [handleLegacyErrors](./kibana-plugin-core-server.irouter.handlelegacyerrors.md) | RequestHandlerWrapper | Wrap a router handler to catch and converts legacy boom errors to proper custom errors. | | [patch](./kibana-plugin-core-server.irouter.patch.md) | RouteRegistrar<'patch'> | Register a route handler for PATCH request. | | [post](./kibana-plugin-core-server.irouter.post.md) | RouteRegistrar<'post'> | Register a route handler for POST request. | | [put](./kibana-plugin-core-server.irouter.put.md) | RouteRegistrar<'put'> | Register a route handler for PUT request. | diff --git a/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.md b/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.md deleted file mode 100644 index 0632b5e5e2297a..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IScopedRenderingClient](./kibana-plugin-core-server.iscopedrenderingclient.md) - -## IScopedRenderingClient interface - - -Signature: - -```typescript -export interface IScopedRenderingClient -``` - -## Methods - -| Method | Description | -| --- | --- | -| [render(options)](./kibana-plugin-core-server.iscopedrenderingclient.render.md) | Generate a KibanaResponse which renders an HTML page bootstrapped with the core bundle. Intended as a response body for HTTP route handlers. | - diff --git a/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.render.md b/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.render.md deleted file mode 100644 index ca114bed211491..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.render.md +++ /dev/null @@ -1,41 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IScopedRenderingClient](./kibana-plugin-core-server.iscopedrenderingclient.md) > [render](./kibana-plugin-core-server.iscopedrenderingclient.render.md) - -## IScopedRenderingClient.render() method - -Generate a `KibanaResponse` which renders an HTML page bootstrapped with the `core` bundle. Intended as a response body for HTTP route handlers. - -Signature: - -```typescript -render(options?: Pick): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| options | Pick<IRenderOptions, 'includeUserSettings'> | | - -Returns: - -`Promise` - -## Example - - -```ts -router.get( - { path: '/', validate: false }, - (context, request, response) => - response.ok({ - body: await context.core.rendering.render(), - headers: { - 'content-security-policy': context.core.http.csp.header, - }, - }) -); - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.md b/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.md index f037b7b3e7cb29..a5c1d59be06d35 100644 --- a/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.md +++ b/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.md @@ -20,4 +20,5 @@ export interface LegacyServiceSetupDeps | --- | --- | --- | | [core](./kibana-plugin-core-server.legacyservicesetupdeps.core.md) | LegacyCoreSetup | | | [plugins](./kibana-plugin-core-server.legacyservicesetupdeps.plugins.md) | Record<string, unknown> | | +| [uiPlugins](./kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md) | UiPlugins | | diff --git a/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md b/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md new file mode 100644 index 00000000000000..d19a7dfcbfcfad --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md) > [uiPlugins](./kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md) + +## LegacyServiceSetupDeps.uiPlugins property + +Signature: + +```typescript +uiPlugins: UiPlugins; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index 5c0f10cac51793..5450e84417f89b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -80,6 +80,9 @@ The plugin integrates with the core system via lifecycle events: `setup` | [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) | | | [ErrorHttpResponseOptions](./kibana-plugin-core-server.errorhttpresponseoptions.md) | HTTP response parameters | | [FakeRequest](./kibana-plugin-core-server.fakerequest.md) | Fake request object created manually by Kibana plugins. | +| [HttpResources](./kibana-plugin-core-server.httpresources.md) | HttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP. Provides API allowing plug-ins to respond with: - a pre-configured HTML page bootstrapping Kibana client app - custom HTML page - custom JS script file. | +| [HttpResourcesRenderOptions](./kibana-plugin-core-server.httpresourcesrenderoptions.md) | Allows to configure HTTP response parameters | +| [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) | Extended set of [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) helpers used to respond with HTML or JS resource. | | [HttpResponseOptions](./kibana-plugin-core-server.httpresponseoptions.md) | HTTP response parameters | | [HttpServerInfo](./kibana-plugin-core-server.httpserverinfo.md) | | | [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) | Kibana HTTP Service provides own abstraction for work with HTTP stack. Plugins don't have direct access to hapi server and its primitives anymore. Moreover, plugins shouldn't rely on the fact that HTTP Service uses one or another library under the hood. This gives the platform flexibility to upgrade or changing our internal HTTP stack without breaking plugins. If the HTTP Service lacks functionality you need, we are happy to discuss and support your needs. | @@ -92,7 +95,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [IndexSettingsDeprecationInfo](./kibana-plugin-core-server.indexsettingsdeprecationinfo.md) | | | [IRenderOptions](./kibana-plugin-core-server.irenderoptions.md) | | | [IRouter](./kibana-plugin-core-server.irouter.md) | Registers route handlers for specified resource path and method. See [RouteConfig](./kibana-plugin-core-server.routeconfig.md) and [RequestHandler](./kibana-plugin-core-server.requesthandler.md) for more information about arguments to route registrations. | -| [IScopedRenderingClient](./kibana-plugin-core-server.iscopedrenderingclient.md) | | | [IUiSettingsClient](./kibana-plugin-core-server.iuisettingsclient.md) | Server-side client that provides access to the advanced settings stored in elasticsearch. The settings provide control over the behavior of the Kibana application. For example, a user can specify how to display numeric or date fields. Users can adjust the settings via Management UI. | | [KibanaRequestEvents](./kibana-plugin-core-server.kibanarequestevents.md) | Request events. | | [KibanaRequestRoute](./kibana-plugin-core-server.kibanarequestroute.md) | Request specific route information exposed to a handler. | @@ -118,7 +120,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [PluginConfigDescriptor](./kibana-plugin-core-server.pluginconfigdescriptor.md) | Describes a plugin configuration properties. | | [PluginInitializerContext](./kibana-plugin-core-server.plugininitializercontext.md) | Context that's available to plugins during initialization stage. | | [PluginManifest](./kibana-plugin-core-server.pluginmanifest.md) | Describes the set of required and optional properties plugin can define in its mandatory JSON manifest file. | -| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients and services: - [rendering](./kibana-plugin-core-server.iscopedrenderingclient.md) - Rendering client which uses the data of the incoming request - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.dataClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request | +| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.dataClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request | | [RouteConfig](./kibana-plugin-core-server.routeconfig.md) | Route specific configuration. | | [RouteConfigOptions](./kibana-plugin-core-server.routeconfigoptions.md) | Additional route options. | | [RouteConfigOptionsBody](./kibana-plugin-core-server.routeconfigoptionsbody.md) | Additional body options for a route | @@ -216,6 +218,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | [HandlerFunction](./kibana-plugin-core-server.handlerfunction.md) | A function that accepts a context object and an optional number of additional arguments. Used for the generic types in [IContextContainer](./kibana-plugin-core-server.icontextcontainer.md) | | [HandlerParameters](./kibana-plugin-core-server.handlerparameters.md) | Extracts the types of the additional arguments of a [HandlerFunction](./kibana-plugin-core-server.handlerfunction.md), excluding the [HandlerContextType](./kibana-plugin-core-server.handlercontexttype.md). | | [Headers](./kibana-plugin-core-server.headers.md) | Http request headers to read. | +| [HttpResourcesRequestHandler](./kibana-plugin-core-server.httpresourcesrequesthandler.md) | Extended version of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) having access to [HttpResourcesServiceToolkit](./kibana-plugin-core-server.httpresourcesservicetoolkit.md) to respond with HTML or JS resources. | +| [HttpResourcesResponseOptions](./kibana-plugin-core-server.httpresourcesresponseoptions.md) | HTTP Resources response parameters | | [HttpResponsePayload](./kibana-plugin-core-server.httpresponsepayload.md) | Data send to the client as a response payload. | | [IBasePath](./kibana-plugin-core-server.ibasepath.md) | Access or manipulate the Kibana base path[BasePath](./kibana-plugin-core-server.basepath.md) | | [IClusterClient](./kibana-plugin-core-server.iclusterclient.md) | Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-core-server.clusterclient.md). | @@ -245,6 +249,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [RequestHandler](./kibana-plugin-core-server.requesthandler.md) | A function executed when route path matched requested resource path. Request handler is expected to return a result of one of [KibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) functions. | | [RequestHandlerContextContainer](./kibana-plugin-core-server.requesthandlercontextcontainer.md) | An object that handles registration of http request context providers. | | [RequestHandlerContextProvider](./kibana-plugin-core-server.requesthandlercontextprovider.md) | Context provider for request handler. Extends request context object with provided functionality or data. | +| [RequestHandlerWrapper](./kibana-plugin-core-server.requesthandlerwrapper.md) | Type-safe wrapper for [RequestHandler](./kibana-plugin-core-server.requesthandler.md) function. | | [ResponseError](./kibana-plugin-core-server.responseerror.md) | Error message and optional data send to the client in case of error. | | [ResponseErrorAttributes](./kibana-plugin-core-server.responseerrorattributes.md) | Additional data to provide error details. | | [ResponseHeaders](./kibana-plugin-core-server.responseheaders.md) | Http response headers to set. | diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandler.md b/docs/development/core/server/kibana-plugin-core-server.requesthandler.md index 156f38fab0983b..cecef7c923568e 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandler.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandler.md @@ -9,7 +9,7 @@ A function executed when route path matched requested resource path. Request han Signature: ```typescript -export declare type RequestHandler

= (context: RequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory) => IKibanaResponse | Promise>; +export declare type RequestHandler

= (context: RequestHandlerContext, request: KibanaRequest, response: ResponseFactory) => IKibanaResponse | Promise>; ``` ## Example diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md index 3c6bee114b6ab3..0d640e52c3a03f 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md @@ -8,7 +8,6 @@ ```typescript core: { - rendering: IScopedRenderingClient; savedObjects: { client: SavedObjectsClientContract; typeRegistry: ISavedObjectTypeRegistry; diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md index b65ae47f0e0c11..0966b91a4ebf21 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md @@ -6,7 +6,7 @@ Plugin specific context passed to a route handler. -Provides the following clients and services: - [rendering](./kibana-plugin-core-server.iscopedrenderingclient.md) - Rendering client which uses the data of the incoming request - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.dataClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request +Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.dataClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request Signature: @@ -18,5 +18,5 @@ export interface RequestHandlerContext | Property | Type | Description | | --- | --- | --- | -| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | {
rendering: IScopedRenderingClient;
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
};
elasticsearch: {
dataClient: IScopedClusterClient;
adminClient: IScopedClusterClient;
};
uiSettings: {
client: IUiSettingsClient;
};
} | | +| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | {
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
};
elasticsearch: {
dataClient: IScopedClusterClient;
adminClient: IScopedClusterClient;
};
uiSettings: {
client: IUiSettingsClient;
};
} | | diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md new file mode 100644 index 00000000000000..a9fe188ee2bffd --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [RequestHandlerWrapper](./kibana-plugin-core-server.requesthandlerwrapper.md) + +## RequestHandlerWrapper type + +Type-safe wrapper for [RequestHandler](./kibana-plugin-core-server.requesthandler.md) function. + +Signature: + +```typescript +export declare type RequestHandlerWrapper = (handler: RequestHandler) => RequestHandler; +``` + +## Example + + +```typescript +export const wrapper: RequestHandlerWrapper = handler => { + return async (context, request, response) => { + // do some logic + ... + }; +} + +``` + diff --git a/docs/development/core/server/kibana-plugin-core-server.responseheaders.md b/docs/development/core/server/kibana-plugin-core-server.responseheaders.md index 4551d1cab86322..fb7d6a10c6b6c5 100644 --- a/docs/development/core/server/kibana-plugin-core-server.responseheaders.md +++ b/docs/development/core/server/kibana-plugin-core-server.responseheaders.md @@ -9,9 +9,5 @@ Http response headers to set. Signature: ```typescript -export declare type ResponseHeaders = { - [header in KnownHeaders]?: string | string[]; -} & { - [header: string]: string | string[]; -}; +export declare type ResponseHeaders = Record | Record; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md index d8202545f0eae6..a8894286de9100 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md @@ -25,6 +25,5 @@ This is only internal for now, and will only be public when we expose the regist | [mappings](./kibana-plugin-core-server.savedobjectstype.mappings.md) | SavedObjectsTypeMappingDefinition | The [mapping definition](./kibana-plugin-core-server.savedobjectstypemappingdefinition.md) for the type. | | [migrations](./kibana-plugin-core-server.savedobjectstype.migrations.md) | SavedObjectMigrationMap | An optional map of [migrations](./kibana-plugin-core-server.savedobjectmigrationfn.md) to be used to migrate the type. | | [name](./kibana-plugin-core-server.savedobjectstype.name.md) | string | The name of the type, which is also used as the internal id. | -| [namespaceAgnostic](./kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md) | boolean | Is the type global (true), or not (false). | | [namespaceType](./kibana-plugin-core-server.savedobjectstype.namespacetype.md) | SavedObjectsNamespaceType | The [namespace type](./kibana-plugin-core-server.savedobjectsnamespacetype.md) for the type. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md deleted file mode 100644 index e3474215904823..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md +++ /dev/null @@ -1,18 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsType](./kibana-plugin-core-server.savedobjectstype.md) > [namespaceAgnostic](./kibana-plugin-core-server.savedobjectstype.namespaceagnostic.md) - -## SavedObjectsType.namespaceAgnostic property - -> Warning: This API is now obsolete. -> -> Use `namespaceType` instead. -> - -Is the type global (true), or not (false). - -Signature: - -```typescript -namespaceAgnostic?: boolean; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespacetype.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespacetype.md index 69912f9144980d..3a3b0f7f3a9a59 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespacetype.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.namespacetype.md @@ -9,5 +9,5 @@ The [namespace type](./kibana-plugin-core-server.savedobjectsnamespacetype.md) f Signature: ```typescript -namespaceType?: SavedObjectsNamespaceType; +namespaceType: SavedObjectsNamespaceType; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md new file mode 100644 index 00000000000000..d1a1ee0905c6e3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md) + +## IndexPatternField.indexPattern property + +Signature: + +```typescript +indexPattern?: IndexPattern; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md index 121ae80734dfd3..df0de6ce0e5419 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md @@ -27,6 +27,7 @@ export declare class Field implements IFieldType | [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | string[] | | | [filterable](./kibana-plugin-plugins-data-public.indexpatternfield.filterable.md) | | boolean | | | [format](./kibana-plugin-plugins-data-public.indexpatternfield.format.md) | | any | | +| [indexPattern](./kibana-plugin-plugins-data-public.indexpatternfield.indexpattern.md) | | IndexPattern | | | [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) | | string | | | [name](./kibana-plugin-plugins-data-public.indexpatternfield.name.md) | | string | | | [script](./kibana-plugin-plugins-data-public.indexpatternfield.script.md) | | 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 fc0dab94a0f656..bf29c883e4eb96 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 @@ -88,7 +88,6 @@ | [SavedQuery](./kibana-plugin-plugins-data-public.savedquery.md) | | | [SavedQueryService](./kibana-plugin-plugins-data-public.savedqueryservice.md) | | | [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | | -| [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md) | | | [TabbedAggColumn](./kibana-plugin-plugins-data-public.tabbedaggcolumn.md) | \* | | [TabbedTable](./kibana-plugin-plugins-data-public.tabbedtable.md) | \* | | [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.id.md deleted file mode 100644 index d60ffba6a05ca8..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md) > [id](./kibana-plugin-plugins-data-public.searchstrategyprovider.id.md) - -## SearchStrategyProvider.id property - -Signature: - -```typescript -id: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md deleted file mode 100644 index aa8ed49051ee9a..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md) > [isViable](./kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md) - -## SearchStrategyProvider.isViable property - -Signature: - -```typescript -isViable: (indexPattern: IndexPattern) => boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.md deleted file mode 100644 index b271a921906a71..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md) - -## SearchStrategyProvider interface - -Signature: - -```typescript -export interface SearchStrategyProvider -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [id](./kibana-plugin-plugins-data-public.searchstrategyprovider.id.md) | string | | -| [isViable](./kibana-plugin-plugins-data-public.searchstrategyprovider.isviable.md) | (indexPattern: IndexPattern) => boolean | | -| [search](./kibana-plugin-plugins-data-public.searchstrategyprovider.search.md) | (params: SearchStrategySearchParams) => SearchStrategyResponse | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.search.md deleted file mode 100644 index 6e2561c3b0ad00..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchstrategyprovider.search.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchStrategyProvider](./kibana-plugin-plugins-data-public.searchstrategyprovider.md) > [search](./kibana-plugin-plugins-data-public.searchstrategyprovider.search.md) - -## SearchStrategyProvider.search property - -Signature: - -```typescript -search: (params: SearchStrategySearchParams) => SearchStrategyResponse; -``` diff --git a/docs/images/add_remote_cluster.png b/docs/images/add_remote_cluster.png index 376b1d83923664..160d29b741c629 100755 Binary files a/docs/images/add_remote_cluster.png and b/docs/images/add_remote_cluster.png differ diff --git a/docs/images/auto_follow_pattern.png b/docs/images/auto_follow_pattern.png index 3bf86458eddd7f..f80de9352280fb 100755 Binary files a/docs/images/auto_follow_pattern.png and b/docs/images/auto_follow_pattern.png differ diff --git a/docs/images/cross-cluster-replication-list-view.png b/docs/images/cross-cluster-replication-list-view.png new file mode 100755 index 00000000000000..4c45174cff7f12 Binary files /dev/null and b/docs/images/cross-cluster-replication-list-view.png differ diff --git a/docs/images/follower_indices.png b/docs/images/follower_indices.png old mode 100644 new mode 100755 index f103bb3cf2acfe..505adeb45ae239 Binary files a/docs/images/follower_indices.png and b/docs/images/follower_indices.png differ diff --git a/docs/images/remote-clusters-list-view.png b/docs/images/remote-clusters-list-view.png new file mode 100755 index 00000000000000..c28379863b74bf Binary files /dev/null and b/docs/images/remote-clusters-list-view.png differ diff --git a/docs/ingest_manager/index-templates.asciidoc b/docs/ingest_manager/index-templates.asciidoc new file mode 100644 index 00000000000000..e19af63c3116f1 --- /dev/null +++ b/docs/ingest_manager/index-templates.asciidoc @@ -0,0 +1,7 @@ +# Elasticsearch Index Templates + +## Generation + +* Index templates are generated from `YAML` files contained in the package. +* There is one index template per dataset. +* For the generation of an index template, all `yml` files contained in the package subdirectory `dataset/DATASET_NAME/fields/` are used. diff --git a/docs/ingest_manager/index.asciidoc b/docs/ingest_manager/index.asciidoc index 1254f412e14c52..866935d1fa5801 100644 --- a/docs/ingest_manager/index.asciidoc +++ b/docs/ingest_manager/index.asciidoc @@ -113,7 +113,7 @@ Ingest Management enforces an indexing strategy to allow the system to automatic {type}-{dataset}-{namespace} ``` -The `{type}` can be `logs` or `metrics`. The `{namespace}` is the part where the user can use free form. The only two requirement are that it has only characters allowed in an Elasticsearch index name and does NOT contain a `-`. The `dataset` is defined by the data that is indexed. The same requirements as for the namespace apply. It is expected that the fields for type, namespace and dataset are part of each event and are constant keywords. +The `{type}` can be `logs` or `metrics`. The `{namespace}` is the part where the user can use free form. The only two requirement are that it has only characters allowed in an Elasticsearch index name and does NOT contain a `-`. The `dataset` is defined by the data that is indexed. The same requirements as for the namespace apply. It is expected that the fields for type, namespace and dataset are part of each event and are constant keywords. If there is a dataset or a namespace with a `-` inside, it is recommended to replace it either by a `.` or a `_`. Note: More `{type}`s might be added in the future like `apm` and `endpoint`. @@ -126,6 +126,8 @@ This indexing strategy has a few advantages: * Having a global metrics and logs template, allows to create new indices on demand which still follow the convention. This is common in the case of k8s as an example. * Constant keywords allow to narrow down the indices we need to access for querying very efficiently. This is especially relevant in environments which a large number of indices or with indices on slower nodes. +Overall it creates smaller indices in size, makes querying more efficient and allows users to define their own naming parts in namespace and still benefiting from all features that can be built on top of the indexing startegy. + === Ingest Pipeline The ingest pipelines for a specific dataset will have the following naming scheme: @@ -197,3 +199,10 @@ The new ingest pipeline is expected to still work with the data coming from olde In case of a breaking change in the data structure, the new ingest pipeline is also expected to deal with this change. In case there are breaking changes which cannot be dealt with in an ingest pipeline, a new package has to be created. Each package lists its minimal required agent version. In case there are agents enrolled with an older version, the user is notified to upgrade these agents as otherwise the new configs cannot be rolled out. + +=== Generated assets + +When a package is installed or upgraded, certain Kibana and Elasticsearch assets are generated from . These follow the naming conventions explained above (see "indexing strategy") and contain configuration for the elastic stack that makes ingesting and displaying data work with as little user interaction as possible. + +* link:index-templates.asciidoc[Elasticsearch Index Templates] +* Kibana Index Patterns diff --git a/docs/management/managing-ccr.asciidoc b/docs/management/managing-ccr.asciidoc new file mode 100644 index 00000000000000..b2db5a80cfe7ec --- /dev/null +++ b/docs/management/managing-ccr.asciidoc @@ -0,0 +1,73 @@ +[role="xpack"] +[[managing-cross-cluster-replication]] +== Cross-Cluster Replication + +Use *Cross-Cluster Replication* to reproduce indices in +remote clusters on a local cluster. {ref}/xpack-ccr.html[Cross-cluster replication] +is commonly used to provide remote backups for disaster recovery and for +geo-proximite copies of data. + +To get started, go to *Management > Cross-Cluster Replication*. + +[role="screenshot"] +image::images/cross-cluster-replication-list-view.png[][Cross-cluster replication list view] + +[float] +=== Prerequisites + +* You must have a {ref}/modules-remote-clusters.html[remote cluster]. +* Leader indices must meet {ref}/ccr-requirements.html[these requirements]. +* The Elasticsearch version of the local cluster must be the same as or newer than the remote cluster. +Refer to {ref}/ccr-overview.html[this document] for more information. + +[float] +[[configure-replication]] +=== Configure replication + +Replication requires a leader index, the index being replicated, and a +follower index, which will contain the leader index's replicated data. +The follower index is passive in that it can read requests and searches, +but cannot accept direct writes. Only the leader index is active for direct writes. + +You can configure follower indices in two ways: + +* Create specific follower indices +* Create follower indices from an auto-follow pattern + +[float] +==== Create specific follower indices + +To replicate data from existing indices, or set up local followers on a case-by-case basis, +go to *Follower indices*. When you create the follower index, you must reference the +remote cluster and the leader index that you created in the remote cluster. + +[role="screenshot"] +image::images/follower_indices.png[][UI for adding follower indices] + +[float] +==== Create follower indices from an auto-follow pattern + +To automatically detect and follow new indices when they are created on a remote cluster, +go to *Auto-follow patterns*. Creating an auto-follow pattern is useful when you have +time series data, like event logs, on the remote cluster that is created or rolled over on a daily basis. + +When creating the pattern, you must reference the remote cluster that you +connected to your local cluster. You must also specify a collection of index patterns +that match the indices you want to automatically follow. + +Once you configure an +auto-follow pattern, any time a new index with a name that matches the pattern is +created in the remote cluster, a follower index is automatically configured in the local cluster. + +[role="screenshot"] +image::images/auto_follow_pattern.png[UI for adding an auto-follow pattern] + +[float] +[[manage-replication]] +=== Manage replication + +Use the list views in *Cross-Cluster Replication* to monitor whether the replication is active and +pause and resume replication. You can also edit and remove the follower indices and auto-follow patterns. + +For an example of cross-cluster replication, +refer to https://www.elastic.co/blog/bi-directional-replication-with-elasticsearch-cross-cluster-replication-ccr[Bi-directional replication with Elasticsearch cross-cluster replication]. diff --git a/docs/management/managing-remote-clusters.asciidoc b/docs/management/managing-remote-clusters.asciidoc index 6b69cfef5b7689..00ec5c7d2ddeae 100644 --- a/docs/management/managing-remote-clusters.asciidoc +++ b/docs/management/managing-remote-clusters.asciidoc @@ -1,67 +1,39 @@ [[working-remote-clusters]] == Remote Clusters -{kib} *Management* provides user interfaces for working with data from remote -clusters and managing the {ccr} process. You can replicate indices from a -leader remote cluster to a follower index in a local cluster. The local follower indices -can be used to provide remote backups for disaster recovery or for geo-proximite copies of data. +Use *Remote Clusters* to establish a unidirectional +connection from your cluster to other clusters. This functionality is +required for {ref}/xpack-ccr.html[cross-cluster replication] and +{ref}/modules-cross-cluster-search.html[cross-cluster search]. -Before using these features, you should be familiar with the following concepts: +To get started, go to *Management > Remote Clusters*. -* {ref}/xpack-ccr.html[{ccr-cap}] -* {ref}/modules-cross-cluster-search.html[{ccs-cap}] -* {ref}/cross-cluster-configuring.html[Cross-cluster security requirements] +[role="screenshot"] +image::images/remote-clusters-list-view.png[Remote Clusters list view, including Add a remote cluster button] [float] [[managing-remote-clusters]] -== Managing remote clusters - -*Remote clusters* helps you manage remote clusters for use with -{ccs} and {ccr}. You can add and remove remote clusters and check their connectivity. +=== Add a remote cluster -Before you use this feature, you should be familiar with the concept of -{ref}/modules-remote-clusters.html[remote clusters]. +A {ref}/modules-remote-clusters.html[remote cluster] connection works by configuring a remote cluster and +connecting to a limited number of nodes, called {ref}/modules-remote-clusters.html#sniff-mode[seed nodes], +in that cluster. +Alternatively, you can define a single proxy address for the remote cluster. -Go to *Management > Elasticsearch > Remote clusters* to create or manage your remotes. +By default, a cross-cluster request, such as a cross-cluster search or +replication request, fails if any cluster in the request is unavailable. +To skip a cluster when its unavailable, +set *Skip if unavailable* to true. -To set up a new remote, click *Add a remote cluster*. Give the cluster a unique name -and define the seed nodes for cluster discovery. You can edit or remove your remote clusters -from the *Remote clusters* list view. +Once you add a remote cluster, you can configure <> +to reproduce indices in the remote cluster on a local cluster. [role="screenshot"] image::images/add_remote_cluster.png[][UI for adding a remote cluster] -Once a remote cluster is registered, you can use the tools under *{ccr-cap}* -to add and manage follower indices on the local cluster, and replicate data from -indices on the remote cluster based on an auto-follow index pattern. - [float] -[[managing-cross-cluster-replication]] -== [xpack]#Managing {ccr}# - -*{ccr-cap}* helps you create and manage the {ccr} process. -If you want to replicate data from existing indices, or set up -local followers on a case-by-case basis, go to *Follower indices*. -If you want to automatically detect and follow new indices when they are created -on a remote cluster, you can do so from *Auto-follow patterns*. - -Creating an auto-follow pattern is useful when you have time-series data, like a logs index, on the -remote cluster that is created or rolled over on a daily basis. Once you have configured an -auto-follow pattern, any time a new index with a name that matches the pattern is -created in the remote cluster, a follower index is automatically configured in the local cluster. - -From the same view, you can also see a list of your saved auto-follow patterns for -a given remote cluster, and monitor whether the replication is active. +[[manage-remote-clusters]] +=== Manage remote clusters -Before you use these features, you should be familiar with the following concepts: - -* {ref}/ccr-requirements.html[Requirements for leader indices] -* {ref}/ccr-auto-follow.html[Automatically following indices] - -To get started, go to *Management > Elasticsearch > {ccr-cap}*. - -[role="screenshot"] -image::images/auto_follow_pattern.png[][UI for adding an auto-follow pattern] - -[role="screenshot"] -image::images/follower_indices.png[][UI for adding follower indices] +From the *Remote Clusters* list view, you can drill down into each cluster and +view its status. You can also edit and delete a cluster. diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index fd835bde83322b..a5503969a3ec18 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -23,7 +23,7 @@ For more {kib} configuration settings, see <>. [role="exclude",id="uptime-security"] == Uptime security -This page has moved. Please see the new section in the {uptime-guide}/uptime-security.html[Uptime Monitoring Guide]. +This page has moved. Please see the new section in the {heartbeat-ref}/securing-heartbeat.html[Uptime Monitoring Guide]. [role="exclude",id="infra-read-only-access"] == Configure source read-only access diff --git a/docs/settings/apm-settings.asciidoc b/docs/settings/apm-settings.asciidoc index 91bbef5690fd5d..fd53c3aeb3605c 100644 --- a/docs/settings/apm-settings.asciidoc +++ b/docs/settings/apm-settings.asciidoc @@ -5,7 +5,10 @@ APM settings ++++ -You do not need to configure any settings to use the APM app. It is enabled by default. +These settings allow the APM app to function, and specify the data that it surfaces. +Unless you've customized your setup, +you do not need to configure any settings to use the APM app. +It is enabled by default. [float] [[apm-indices-settings-kb]] @@ -33,29 +36,29 @@ image::settings/images/apm-settings.png[APM app settings in Kibana] If you'd like to change any of the default values, copy and paste the relevant settings into your `kibana.yml` configuration file. +Changing these settings may disable features of the APM App. -xpack.apm.enabled:: Set to `false` to disabled the APM plugin {kib}. Defaults to -`true`. +xpack.apm.enabled:: Set to `false` to disable the APM app. Defaults to `true`. -xpack.apm.ui.enabled:: Set to `false` to hide the APM plugin {kib} from the menu. Defaults to -`true`. +xpack.apm.ui.enabled:: Set to `false` to hide the APM app from the menu. Defaults to `true`. -xpack.apm.ui.transactionGroupBucketSize:: Number of top transaction groups displayed in APM plugin in Kibana. Defaults to `100`. +xpack.apm.ui.transactionGroupBucketSize:: Number of top transaction groups displayed in the APM app. Defaults to `100`. -xpack.apm.ui.maxTraceItems:: Max number of child items displayed when viewing trace details. Defaults to `1000`. +xpack.apm.ui.maxTraceItems:: Maximum number of child items displayed when viewing trace details. Defaults to `1000`. -apm_oss.indexPattern:: Index pattern is used for integrations with Machine Learning and Kuery Bar. It must match all apm indices. Defaults to `apm-*`. +apm_oss.indexPattern:: The index pattern used for integrations with Machine Learning and Query Bar. +It must match all apm indices. Defaults to `apm-*`. -apm_oss.errorIndices:: Matcher for indices containing error documents. Defaults to `apm-*`. +apm_oss.errorIndices:: Matcher for all {apm-server-ref}/error-indices.html[error indices]. Defaults to `apm-*`. -apm_oss.onboardingIndices:: Matcher for indices containing onboarding documents. Defaults to `apm-*`. +apm_oss.onboardingIndices:: Matcher for all onboarding indices. Defaults to `apm-*`. -apm_oss.spanIndices:: Matcher for indices containing span documents. Defaults to `apm-*`. +apm_oss.spanIndices:: Matcher for all {apm-server-ref}/span-indices.html[span indices]. Defaults to `apm-*`. -apm_oss.transactionIndices:: Matcher for indices containing transaction documents. Defaults to `apm-*`. +apm_oss.transactionIndices:: Matcher for all {apm-server-ref}/transaction-indices.html[transaction indices]. Defaults to `apm-*`. -apm_oss.metricsIndices:: Matcher for indices containing metric documents. Defaults to `apm-*`. +apm_oss.metricsIndices:: Matcher for all {apm-server-ref}/metricset-indices.html[metrics indices]. Defaults to `apm-*`. -apm_oss.sourcemapIndices:: Matcher for indices containing sourcemap documents. Defaults to `apm-*`. +apm_oss.sourcemapIndices:: Matcher for all {apm-server-ref}/sourcemap-indices.html[source map indices]. Defaults to `apm-*`. // end::general-apm-settings[] diff --git a/docs/uptime-guide/index.asciidoc b/docs/uptime-guide/index.asciidoc index 7bbc01bb303f1f..09763182fa88fc 100644 --- a/docs/uptime-guide/index.asciidoc +++ b/docs/uptime-guide/index.asciidoc @@ -12,4 +12,3 @@ include::install.asciidoc[] include::deployment-arch.asciidoc[] -include::security.asciidoc[] diff --git a/docs/uptime-guide/install.asciidoc b/docs/uptime-guide/install.asciidoc index e7c50bb7604ce9..0ed1270ca92cea 100644 --- a/docs/uptime-guide/install.asciidoc +++ b/docs/uptime-guide/install.asciidoc @@ -56,6 +56,11 @@ Additional information is available in {heartbeat-ref}/heartbeat-configuration.h [role="screenshot"] image::images/uptime-setup.png[Installation instructions on the Uptime page in Kibana] +[[setup-security]] +=== Step 4: Setup Security + +Secure your installation by following the {heartbeat-ref}/securing-heartbeat.html[Secure Heartbeat] documentation. + [float] ==== Important considerations diff --git a/docs/uptime-guide/security.asciidoc b/docs/uptime-guide/security.asciidoc deleted file mode 100644 index 0c6fa4c6c4f56f..00000000000000 --- a/docs/uptime-guide/security.asciidoc +++ /dev/null @@ -1,60 +0,0 @@ -[[uptime-security]] -== Elasticsearch Security - -If you use Elasticsearch security, you'll need to enable certain privileges for users -that would like to access the Uptime app. For example, create user and support roles to implement the privileges: - -[float] -=== Create a role - -You'll need a role that lets you access the Heartbeat indices, which by default are `heartbeat-*`. -You can create this with the following request: - -["source","sh",subs="attributes,callouts"] ---------------------------------------------------------------- -PUT /_security/role/uptime -{ "indices" : [ - { - "names" : [ - "heartbeat-*" - ], - "privileges" : [ - "read", - "view_index_metadata" - ], - "field_security" : { - "grant" : [ - "*" - ] - }, - "allow_restricted_indices" : false - } - ], - "transient_metadata" : { - "enabled" : true - } -} ---------------------------------------------------------------- -// CONSOLE - -[float] -=== Assign the role to a user - -Next, you'll need to create a user with both the `uptime` role, and another role with sufficient {kibana-ref}/kibana-privileges.html[Kibana privileges], -such as the `kibana_admin` role. -You can do this with the following request: - -["source","sh",subs="attributes,callouts"] ---------------------------------------------------------------- -PUT /_security/user/jacknich -{ - "password" : "j@rV1s", - "roles" : [ "uptime", "kibana_admin" ], - "full_name" : "Jack Nicholson", - "email" : "jacknich@example.com", - "metadata" : { - "intelligence" : 7 - } -} ---------------------------------------------------------------- -// CONSOLE diff --git a/docs/uptime/images/alert-flyout.png b/docs/uptime/images/alert-flyout.png new file mode 100644 index 00000000000000..7fc1e3d9aefe27 Binary files /dev/null and b/docs/uptime/images/alert-flyout.png differ diff --git a/docs/uptime/images/check-history.png b/docs/uptime/images/check-history.png index 6418495eee9edd..91565bf59aa7f2 100644 Binary files a/docs/uptime/images/check-history.png and b/docs/uptime/images/check-history.png differ diff --git a/docs/uptime/images/error-list.png b/docs/uptime/images/error-list.png deleted file mode 100644 index 99f017f2945a5b..00000000000000 Binary files a/docs/uptime/images/error-list.png and /dev/null differ diff --git a/docs/uptime/images/monitor-charts.png b/docs/uptime/images/monitor-charts.png index dbfa43f47656e0..522f34662657ee 100644 Binary files a/docs/uptime/images/monitor-charts.png and b/docs/uptime/images/monitor-charts.png differ diff --git a/docs/uptime/images/observability_integrations.png b/docs/uptime/images/observability_integrations.png index d5c612c7589ca3..6589c0c5565dd6 100644 Binary files a/docs/uptime/images/observability_integrations.png and b/docs/uptime/images/observability_integrations.png differ diff --git a/docs/uptime/images/settings.png b/docs/uptime/images/settings.png new file mode 100644 index 00000000000000..dd36f0a6d702b3 Binary files /dev/null and b/docs/uptime/images/settings.png differ diff --git a/docs/uptime/images/snapshot-view.png b/docs/uptime/images/snapshot-view.png index 020396d0f3e4cf..1fce2e9592c14c 100644 Binary files a/docs/uptime/images/snapshot-view.png and b/docs/uptime/images/snapshot-view.png differ diff --git a/docs/uptime/images/status-bar.png b/docs/uptime/images/status-bar.png index e0e9b27555900a..8d242789cdccd7 100644 Binary files a/docs/uptime/images/status-bar.png and b/docs/uptime/images/status-bar.png differ diff --git a/docs/uptime/index.asciidoc b/docs/uptime/index.asciidoc index 785b9f818f5bfc..a355f8ecf48436 100644 --- a/docs/uptime/index.asciidoc +++ b/docs/uptime/index.asciidoc @@ -12,8 +12,10 @@ To get started with Elastic Uptime, refer to {uptime-guide}/install-uptime.html[ * <> * <> +* <> -- include::overview.asciidoc[] include::monitor.asciidoc[] +include::settings.asciidoc[] diff --git a/docs/uptime/monitor.asciidoc b/docs/uptime/monitor.asciidoc index d54fd02c7c0691..8a4be1f11a7219 100644 --- a/docs/uptime/monitor.asciidoc +++ b/docs/uptime/monitor.asciidoc @@ -5,21 +5,24 @@ The Monitor page will help you get further insight into the performance of a specific network endpoint. You'll see a detailed visualization of the monitor's request duration over time, as well as the `up`/`down` -status over time. +status over time. You can also also detect anomalies in response time data +by configuring Machine Learning jobs on this page. [float] -=== Status bar +=== Status panel [role="screenshot"] image::uptime/images/status-bar.png[Status bar] -The Status bar displays a quick summary of the latest information +The Status panel displays a quick summary of the latest information regarding your monitor. You can view its latest status, click a link to visit the targeted URL, see its most recent request duration, and determine the amount of time that has elapsed since the last check. -You can use the Status bar to get a quick summary of current performance, -beyond simply knowing if the monitor is `up` or `down`. +When two Heartbeat instances are configured in different geographic locations +the map will show each location as a pinpoint on the map, along with the +amount of time elapsed since data was last received from that location. + [float] === Monitor charts @@ -32,12 +35,14 @@ date range. These charts can help you gain insight into how quickly requests are by the targeted endpoint, and give you a sense of how frequently a host or endpoint was down in your selected timespan. -The first chart displays request duration information for your monitor. +The Monitor duration chart displays request duration information for your monitor. The area surrounding the line is the range of request time for the corresponding -bucket. The line is the average time. +bucket. The line is the average time. Anomaly detection using Machine Learning +can be configured in the upper right hand of this panel. When response times change +in an unexpected way the time range in which they occurred will be given filled with a color. -Next, is a graphical representation of the check statuses over time. Hover over -the charts to display crosshairs with more specific numeric data. +The pings over time chart is a graphical representation of the check statuses over time. +Hover over the charts to display crosshairs with more specific numeric data. [role="screenshot"] image::uptime/images/crosshair-example.png[Chart crosshair] @@ -49,6 +54,6 @@ image::uptime/images/crosshair-example.png[Chart crosshair] image::uptime/images/check-history.png[Check history view] The Check history displays the total count of this monitor's checks for the selected -date range. You can additionally filter the checks by `status` to help find recent problems +date range. You can additionally filter the checks by status and location to help find recent problems on a per-check basis. This table can help you gain some insight into more granular details about recent individual data points Heartbeat is logging about your host or endpoint. diff --git a/docs/uptime/overview.asciidoc b/docs/uptime/overview.asciidoc index 098ce12a569911..71c09c968e5124 100644 --- a/docs/uptime/overview.asciidoc +++ b/docs/uptime/overview.asciidoc @@ -21,12 +21,12 @@ This control allows you to use automated filter options, as well as input custom text to select specific monitors by field, URL, ID, and other attributes. [float] -=== Snapshot view +=== Snapshot panel [role="screenshot"] image::uptime/images/snapshot-view.png[Snapshot view] -This view is intended to quickly give you a sense of the overall +This panel is intended to quickly give you a sense of the overall status of the environment you're monitoring, or a subset of those monitors. Here, you can see the total number of detected monitors within the selected Uptime date range. In addition to the total, the counts for the number of monitors @@ -49,6 +49,17 @@ way to navigate to a more in-depth visualization for interesting hosts or endpoi This table includes information like the most recent status, when the monitor was last checked, its ID and URL, its IP address, and a dedicated sparkline showing its check status over time. +[float] +=== Creating and managing alerts + +[role="screenshot"] +image::uptime/images/alert-flyout.png[Create alert flyout] + +To receive alerts when a monitor goes down, use the alerting menu at the top of the +overview page. Use a query in the alert flyout to determine which monitors to check +with your alert. If you already have a query in the overview page search bar it will +be carried over into this box. + [float] === Observability integrations @@ -60,14 +71,3 @@ Docker related host information, it will provide links to open the Metrics app o for this host. Additionally, this feature supplies links to simply filter the other views on the host's IP address, to help you quickly determine if these other solutions contain data relevant to your current interest. - -[float] -=== Error list - -[role="screenshot"] -image::uptime/images/error-list.png[Error list] - -The Error list displays aggregations of errors that Heartbeat has logged. Errors are -displayed by Error type, monitor ID, and message. Clicking a monitor's ID will take you -to the corresponding Monitor view, which can provide you richer information about the individual -data points that are resulting in the displayed errors. diff --git a/docs/uptime/settings.asciidoc b/docs/uptime/settings.asciidoc new file mode 100644 index 00000000000000..55da6e802bec60 --- /dev/null +++ b/docs/uptime/settings.asciidoc @@ -0,0 +1,27 @@ +[role="xpack"] +[[uptime-settings]] + +== Settings + +[role="screenshot"] +image::uptime/images/settings.png[Filter bar] + +The Uptime settings page lets you change which Heartbeat indices are displayed +by the uptime app. Users must have the 'all' permission to modify items on this page. +Uptime settings apply to the current space only. Use different settings in different +spaces to segment different uptime use cases and domains. + +As an example, imagine your organization has one team for internal IT services, and another +for public services. Each team operates independently and is only responsible for its +own services. In this scenario, you might set up separate Heartbeat instances for each team, +writing out to index patterns named `it-heartbeat-\*`, and `external-heartbeat-\*`. You would +create separate roles and users for each in Elasticsearch, each with access to their own spaces, +named `it` and `external` respectively. Within each space you would navigate to the settings page +and set the correct index pattern to match only the indices that space is allowed to access. + +Note that the pattern set here only restricts what the Uptime app shows. Users may still be able +to manually query Elasticsearch for data outside this pattern! + +See the <> +and {heartbeat-ref}/securing-heartbeat.html[Heartbeat security] +docs for more information. diff --git a/docs/user/alerting/action-types.asciidoc b/docs/user/alerting/action-types.asciidoc index 02c09736e1fa08..49e7bd1d777432 100644 --- a/docs/user/alerting/action-types.asciidoc +++ b/docs/user/alerting/action-types.asciidoc @@ -2,181 +2,55 @@ [[action-types]] == Action and connector types -{kib} provides the following types of actions: +Actions are Kibana services or integrations with third-party systems that run as background tasks on the Kibana server when alert conditions are met. {kib} provides the following types of actions: -* <> -* <> -* <> -* <> -* <> -* <> +[cols="2"] +|=== -This section describes how to configure connectors and actions for each type. +a| <> -[NOTE] -============================================== -Some action types are paid commercial features, while others are free. -For a comparison of the Elastic license levels, -see https://www.elastic.co/subscriptions[the subscription page]. -============================================== - -[float] -[[email-action-type]] -=== Email - -The email action type uses the SMTP protocol to send mail message, using an integration of https://nodemailer.com/[Nodemailer]. Email message text is sent as both plain text and html text. - -[float] -[[email-connector-configuration]] -==== Connector configuration - -Email 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. -Sender:: The from address for all emails sent with this connector, specified in `user@host-name` format. -Host:: Host name of the service provider. If you are using the <> setting, make sure this hostname is whitelisted. -Port:: The port to connect to on the service provider. -Secure:: If true the connection will use TLS when connecting to the service provider. See https://nodemailer.com/smtp/#tls-options[nodemailer TLS documentation] for more information. -Username:: username for 'login' type authentication. -Password:: password for 'login' type authentication. - -[float] -[[email-action-configuration]] -==== Action configuration - -Email actions have the following configuration properties: - -To, CC, BCC:: Each is a list of addresses. Addresses can be specified in `user@host-name` format, or in `name ` format. One of To, CC, or BCC must contain an entry. -Subject:: The subject line of the email. -Message:: The message text of the email. Markdown format is supported. - -[float] -[[index-action-type]] -=== Index - -The index action type will index a document into {es}. - -[float] -[[index-connector-configuration]] -==== Connector configuration - -Index 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. -Index:: The {es} index to be written to. -Refresh:: Setting for the {ref}/docs-refresh.html[refresh] policy for the write request. -Execution time field:: This field will be automatically set to the time the alert condition was detected. - -[float] -[[index-action-configuration]] -==== Action configuration - -Index actions have the following properties: - -Document:: The document to index in json format. - -[float] -[[pagerduty-action-type]] -=== PagerDuty - -The PagerDuty action type uses the https://v2.developer.pagerduty.com/docs/events-api-v2[v2 Events API] to trigger, acknowledge, and resolve PagerDuty alerts. - -[float] -[[pagerduty-connector-configuration]] -==== Connector configuration - -PagerDuty 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. -API URL:: An optional PagerDuty event URL. Defaults to `https://events.pagerduty.com/v2/enqueue`. If you are using the <> setting, make sure the hostname is whitelisted. -Routing Key:: A 32 character PagerDuty Integration Key for an integration on a service or on a global ruleset. - -[float] -[[pagerduty-action-configuration]] -==== Action configuration +| Send email from your server. -PagerDuty actions have the following properties: +a| <> -Severity:: The perceived severity of on the affected system. This can be one of `Critical`, `Error`, `Warning` or `Info`(default). -Event action:: One of `Trigger` (default), `Resolve`, or `Acknowledge`. See https://v2.developer.pagerduty.com/docs/events-api-v2#event-action[event action] for more details. -Dedup Key:: All actions sharing this key will be associated with the same PagerDuty alert. This value is used to correlate trigger and resolution. This value is *optional*, and if unset defaults to `action:`. The maximum length is *255* characters. See https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication[alert deduplication] for details. -Timestamp:: An *optional* https://v2.developer.pagerduty.com/v2/docs/types#datetime[ISO-8601 format date-time], indicating the time the event was detected or generated. -Component:: An *optional* value indicating the component of the source machine that is responsible for the event, for example `mysql` or `eth0`. -Group:: An *optional* value indicating the logical grouping of components of a service, for example `app-stack`. -Source:: An *optional* value indicating the affected system, preferably a hostname or fully qualified domain name. Defaults to the {kib} saved object id of the action. -Summary:: An *optional* text summary of the event, defaults to `No summary provided`. The maximum length is 1024 characters. -Class:: An *optional* value indicating the class/type of the event, for example `ping failure` or `cpu load`. +| Index data into Elasticsearch. -For more details on these properties, see https://v2.developer.pagerduty.com/v2/docs/send-an-event-events-api-v2[PagerDuty v2 event parameters]. +a| <> -[float] -[[server-log-action-type]] -=== Server log +| Send an event in PagerDuty. -This action type writes and entry to the {kib} server log. +a| <> -[float] -[[server-log-connector-configuration]] -==== Connector configuration +| Add a message to a Kibana log. -Server log connectors have the following configuration properties: +a| <> -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. +| Send a message to a Slack channel or user. -[float] -[[server-log-action-configuration]] -==== Action configuration - -Server log actions have the following properties: - -Message:: The message to log. - -[float] -[[slack-action-type]] -=== Slack - -The Slack action type uses https://api.slack.com/incoming-webhooks[Slack Incoming Webhooks]. - -[float] -[[slack-connector-configuration]] -==== Connector configuration +a| <> -Slack connectors have the following configuration properties: +| Send a request to a web service. +|=== -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. -Webhook URL:: The URL of the incoming webhook. See https://api.slack.com/messaging/webhooks#getting_started[Slack Incoming Webhooks] for instructions on generating this URL. If you are using the <> setting, make sure the hostname is whitelisted. - -[float] -[[slack-action-configuration]] -==== Action configuration - -Slack actions have the following properties: - -Message:: The message text, converted to the `text` field in the Webhook JSON payload. Currently only the text field is supported. Markdown, images, and other advanced formatting are not yet supported. - -[float] -[[webhook-action-type]] -=== Webhook - -The Webhook action type uses https://github.com/axios/axios[axios] to send a POST or PUT request to a web service. - -[float] -[[webhook-connector-configuration]] -==== Connector configuration - -Webhook 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:: The request URL. If you are using the <> setting, make sure the hostname is whitelisted. -Method:: HTTP request method, either `post`(default) or `put`. -Headers:: A set of key-value pairs sent as headers with the request -User:: An optional username. If set, HTTP basic authentication is used. Currently only basic authentication is supported. -Password:: An optional password. If set, HTTP basic authentication is used. Currently only basic authentication is supported. +[NOTE] +============================================== +Some action types are paid commercial features, while others are free. +For a comparison of the Elastic subscription levels, +see https://www.elastic.co/subscriptions[the subscription page]. +============================================== [float] -[[webhook-action-configuration]] -==== Action configuration +[[create-connectors]] +=== Connectors -Webhook actions have the following properties: +You can create connectors for actions in <> or via the action API. +For out-of-the-box and standardized connectors, you can <> +before {kib} starts. -Body:: A json payload sent to the request URL. \ No newline at end of file +include::action-types/email.asciidoc[] +include::action-types/index.asciidoc[] +include::action-types/pagerduty.asciidoc[] +include::action-types/server-log.asciidoc[] +include::action-types/slack.asciidoc[] +include::action-types/webhook.asciidoc[] +include::pre-configured-connectors.asciidoc[] diff --git a/docs/user/alerting/action-types/email.asciidoc b/docs/user/alerting/action-types/email.asciidoc new file mode 100644 index 00000000000000..be3623dd9e59c6 --- /dev/null +++ b/docs/user/alerting/action-types/email.asciidoc @@ -0,0 +1,29 @@ +[role="xpack"] +[[email-action-type]] +== Email action type + +The email action type uses the SMTP protocol to send mail message, using an integration of https://nodemailer.com/[Nodemailer]. Email message text is sent as both plain text and html text. + +[float] +[[email-connector-configuration]] +==== Connector configuration + +Email 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. +Sender:: The from address for all emails sent with this connector, specified in `user@host-name` format. +Host:: Host name of the service provider. If you are using the <> setting, make sure this hostname is whitelisted. +Port:: The port to connect to on the service provider. +Secure:: If true the connection will use TLS when connecting to the service provider. See https://nodemailer.com/smtp/#tls-options[nodemailer TLS documentation] for more information. +Username:: username for 'login' type authentication. +Password:: password for 'login' type authentication. + +[float] +[[email-action-configuration]] +==== Action configuration + +Email actions have the following configuration properties: + +To, CC, BCC:: Each is a list of addresses. Addresses can be specified in `user@host-name` format, or in `name ` format. One of To, CC, or BCC must contain an entry. +Subject:: The subject line of the email. +Message:: The message text of the email. Markdown format is supported. \ No newline at end of file diff --git a/docs/user/alerting/action-types/index.asciidoc b/docs/user/alerting/action-types/index.asciidoc new file mode 100644 index 00000000000000..75d9e57b1f212f --- /dev/null +++ b/docs/user/alerting/action-types/index.asciidoc @@ -0,0 +1,24 @@ +[role="xpack"] +[[index-action-type]] +== Index action type + +The index action type will index a document into {es}. + +[float] +[[index-connector-configuration]] +==== Connector configuration + +Index 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. +Index:: The {es} index to be written to. +Refresh:: Setting for the {ref}/docs-refresh.html[refresh] policy for the write request. +Execution time field:: This field will be automatically set to the time the alert condition was detected. + +[float] +[[index-action-configuration]] +==== Action configuration + +Index actions have the following properties: + +Document:: The document to index in json format. \ No newline at end of file diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc new file mode 100644 index 00000000000000..50a1f31e4a9ae1 --- /dev/null +++ b/docs/user/alerting/action-types/pagerduty.asciidoc @@ -0,0 +1,33 @@ +[role="xpack"] +[[pagerduty-action-type]] +== PagerDuty action type + +The PagerDuty action type uses the https://v2.developer.pagerduty.com/docs/events-api-v2[v2 Events API] to trigger, acknowledge, and resolve PagerDuty alerts. + +[float] +[[pagerduty-connector-configuration]] +==== Connector configuration + +PagerDuty 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. +API URL:: An optional PagerDuty event URL. Defaults to `https://events.pagerduty.com/v2/enqueue`. If you are using the <> setting, make sure the hostname is whitelisted. +Routing Key:: A 32 character PagerDuty Integration Key for an integration on a service or on a global ruleset. + +[float] +[[pagerduty-action-configuration]] +==== Action configuration + +PagerDuty actions have the following properties: + +Severity:: The perceived severity of on the affected system. This can be one of `Critical`, `Error`, `Warning` or `Info`(default). +Event action:: One of `Trigger` (default), `Resolve`, or `Acknowledge`. See https://v2.developer.pagerduty.com/docs/events-api-v2#event-action[event action] for more details. +Dedup Key:: All actions sharing this key will be associated with the same PagerDuty alert. This value is used to correlate trigger and resolution. This value is *optional*, and if unset defaults to `action:`. The maximum length is *255* characters. See https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication[alert deduplication] for details. +Timestamp:: An *optional* https://v2.developer.pagerduty.com/v2/docs/types#datetime[ISO-8601 format date-time], indicating the time the event was detected or generated. +Component:: An *optional* value indicating the component of the source machine that is responsible for the event, for example `mysql` or `eth0`. +Group:: An *optional* value indicating the logical grouping of components of a service, for example `app-stack`. +Source:: An *optional* value indicating the affected system, preferably a hostname or fully qualified domain name. Defaults to the {kib} saved object id of the action. +Summary:: An *optional* text summary of the event, defaults to `No summary provided`. The maximum length is 1024 characters. +Class:: An *optional* value indicating the class/type of the event, for example `ping failure` or `cpu load`. + +For more details on these properties, see https://v2.developer.pagerduty.com/v2/docs/send-an-event-events-api-v2[PagerDuty v2 event parameters]. \ No newline at end of file diff --git a/docs/user/alerting/action-types/server-log.asciidoc b/docs/user/alerting/action-types/server-log.asciidoc new file mode 100644 index 00000000000000..4efbdf3bea099d --- /dev/null +++ b/docs/user/alerting/action-types/server-log.asciidoc @@ -0,0 +1,21 @@ +[role="xpack"] +[[server-log-action-type]] +== Server log action type + +This action type writes and entry to the {kib} server log. + +[float] +[[server-log-connector-configuration]] +==== Connector configuration + +Server log 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. + +[float] +[[server-log-action-configuration]] +==== Action configuration + +Server log actions have the following properties: + +Message:: The message to log. \ No newline at end of file diff --git a/docs/user/alerting/action-types/slack.asciidoc b/docs/user/alerting/action-types/slack.asciidoc new file mode 100644 index 00000000000000..a4bacbf162e461 --- /dev/null +++ b/docs/user/alerting/action-types/slack.asciidoc @@ -0,0 +1,22 @@ +[role="xpack"] +[[slack-action-type]] +== Slack action type + +The Slack action type uses https://api.slack.com/incoming-webhooks[Slack Incoming Webhooks]. + +[float] +[[slack-connector-configuration]] +==== Connector configuration + +Slack 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. +Webhook URL:: The URL of the incoming webhook. See https://api.slack.com/messaging/webhooks#getting_started[Slack Incoming Webhooks] for instructions on generating this URL. If you are using the <> setting, make sure the hostname is whitelisted. + +[float] +[[slack-action-configuration]] +==== Action configuration + +Slack actions have the following properties: + +Message:: The message text, converted to the `text` field in the Webhook JSON payload. Currently only the text field is supported. Markdown, images, and other advanced formatting are not yet supported. \ No newline at end of file diff --git a/docs/user/alerting/action-types/webhook.asciidoc b/docs/user/alerting/action-types/webhook.asciidoc new file mode 100644 index 00000000000000..8c211aa83af89d --- /dev/null +++ b/docs/user/alerting/action-types/webhook.asciidoc @@ -0,0 +1,26 @@ +[role="xpack"] +[[webhook-action-type]] +== Webhook action type + +The Webhook action type uses https://github.com/axios/axios[axios] to send a POST or PUT request to a web service. + +[float] +[[webhook-connector-configuration]] +==== Connector configuration + +Webhook 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:: The request URL. If you are using the <> setting, make sure the hostname is whitelisted. +Method:: HTTP request method, either `post`(default) or `put`. +Headers:: A set of key-value pairs sent as headers with the request +User:: An optional username. If set, HTTP basic authentication is used. Currently only basic authentication is supported. +Password:: An optional password. If set, HTTP basic authentication is used. Currently only basic authentication is supported. + +[float] +[[webhook-action-configuration]] +==== Action configuration + +Webhook actions have the following properties: + +Body:: A json payload sent to the request URL. \ No newline at end of file diff --git a/docs/user/alerting/images/alert-pre-configured-connectors-dropdown.png b/docs/user/alerting/images/alert-pre-configured-connectors-dropdown.png new file mode 100644 index 00000000000000..4e6c713298626f Binary files /dev/null and b/docs/user/alerting/images/alert-pre-configured-connectors-dropdown.png differ diff --git a/docs/user/alerting/images/alert-pre-configured-slack-connector.png b/docs/user/alerting/images/alert-pre-configured-slack-connector.png new file mode 100644 index 00000000000000..de05e2074dddeb Binary files /dev/null and b/docs/user/alerting/images/alert-pre-configured-slack-connector.png differ diff --git a/docs/user/alerting/images/pre-configured-connectors-managing.png b/docs/user/alerting/images/pre-configured-connectors-managing.png new file mode 100644 index 00000000000000..f97e93175fa368 Binary files /dev/null and b/docs/user/alerting/images/pre-configured-connectors-managing.png differ diff --git a/docs/user/alerting/images/pre-configured-connectors-view-screen.png b/docs/user/alerting/images/pre-configured-connectors-view-screen.png new file mode 100644 index 00000000000000..43ac44e7536d8f Binary files /dev/null and b/docs/user/alerting/images/pre-configured-connectors-view-screen.png differ diff --git a/docs/user/alerting/index.asciidoc b/docs/user/alerting/index.asciidoc index f556cf71bf06cd..df11f5f03a7de0 100644 --- a/docs/user/alerting/index.asciidoc +++ b/docs/user/alerting/index.asciidoc @@ -154,10 +154,13 @@ Pre-packaged *alert types* simplify setup, hide the details complex domain-speci [[alerting-setup-prerequisites]] == Setup and prerequisites +If you are using an *on-premises* Elastic Stack deployment: + +* In the kibana.yml configuration file, add the <> setting. + If you are using an *on-premises* Elastic Stack deployment with <>: -* TLS must be configured for communication <>. {kib} alerting uses <> to secure background alert checks and actions, and API keys require {ref}/configuring-tls.html#tls-http[TLS on the HTTP interface]. -* In the kibana.yml configuration file, add the <> +* Transport Layer Security (TLS) must be configured for communication <>. {kib} alerting uses <> to secure background alert checks and actions, and API keys require {ref}/configuring-tls.html#tls-http[TLS on the HTTP interface]. [float] [[alerting-security]] diff --git a/docs/user/alerting/pre-configured-connectors.asciidoc b/docs/user/alerting/pre-configured-connectors.asciidoc new file mode 100644 index 00000000000000..3db13acfb423e0 --- /dev/null +++ b/docs/user/alerting/pre-configured-connectors.asciidoc @@ -0,0 +1,88 @@ +[role="xpack"] +[[pre-configured-connectors]] + +== Preconfigured connectors + +You can preconfigure an action connector to have all the information it needs prior to startup +by adding it to the `kibana.yml` file. +Sensitive configuration information, such as credentials, can use the {kib} keystore. + +Preconfigured connectors offer the following capabilities: + +- Require no setup. Configuration and credentials needed to execute an +action are predefined, including the connector name and ID. +- Appear in all spaces because they are not saved objects. +- Cannot be edited or deleted. + +[float] +[[preconfigured-connector-example]] +=== Example of a preconfigured connector + +The following example shows a valid configuration 2 out-of-the box connector. + +[source,console] +------------------------ + xpack.actions.preconfigured: + - id: 'my-slack1' <1> + actionTypeId: .slack <2> + name: 'Slack #xyz' <3> + config: <4> + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz' + - id: 'webhook-service' + actionTypeId: .webhook + name: 'Email service' + config: + url: 'https://email-alert-service.elastic.co' + method: post + headers: + header1: value1 + header2: value2 + secrets: <5> + user: elastic + password: changeme +------------------------ + +<1> `id` is the action connector identifier. +<2> `actionTypeId` is the action type identifier. +<3> `name` is the name of the preconfigured connector. +<4> `config` is the action type specific to the configuration. +<5> `secrets` is sensitive configuration, such as username, password, and keys. + +[NOTE] +============================================== +Sensitive properties, such as passwords, can also be stored in the {kib} keystore. +============================================== + +[float] +[[pre-configured-connector-alert-form]] +=== Creating an alert with a preconfigured connector + +When attaching an action to an alert, +select from a list of available action types, and +then select the Slack or Webhook type. Those action types were configured previously. +The preconfigured connector is installed and is automatically selected. + +[role="screenshot"] +image::images/alert-pre-configured-slack-connector.png[Create alert with selected Slack action type] + +The dropdown is populated with additional preconfigured Slack connectors. +The `preconfigured` label distinguishes them from space-aware connectors that use saved objects. + +[role="screenshot"] +image::images/alert-pre-configured-connectors-dropdown.png[Dropdown list with pre-cofigured connectors] + +[float] +[[managing-pre-configured-connectors]] +=== Managing preconfigured connectors + +Preconfigured connectors appear in the connector list, regardless of which space the user is in. +They are tagged as “preconfigured” and cannot be deleted. + +[role="screenshot"] +image::images/pre-configured-connectors-managing.png[Connectors managing tab with pre-cofigured] + +Clicking on a preconfigured connector shows the description, but not any of the configuration. +A message indicates that this is a preconfigured connector. + +[role="screenshot"] +image::images/pre-configured-connectors-view-screen.png[Pre-configured connector view details] diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc index fa34802abe2a91..a4ba320e826b17 100644 --- a/docs/user/management.asciidoc +++ b/docs/user/management.asciidoc @@ -13,7 +13,7 @@ indices, clusters, licenses, UI settings, index patterns, spaces, and more. [cols="50, 50"] |=== -a| <> +a| <> Replicate indices on a remote cluster and copy them to a follower index on a local cluster. This is important for @@ -85,7 +85,8 @@ set the timespan for notification messages, and much more. | <> -Centrally manage your alerts from across {kib}. Create and manage re-usable connectors for triggering actions. +Centrally manage your alerts across {kib}. Create and manage reusable +connectors for triggering actions. | <> @@ -125,6 +126,8 @@ include::{kib-repo-dir}/management/alerting/connector-management.asciidoc[] include::{kib-repo-dir}/management/managing-beats.asciidoc[] +include::{kib-repo-dir}/management/managing-ccr.asciidoc[] + include::{kib-repo-dir}/management/index-lifecycle-policies/intro-to-lifecycle-policies.asciidoc[] include::{kib-repo-dir}/management/index-lifecycle-policies/create-policy.asciidoc[] diff --git a/docs/user/reporting/chromium-sandbox.asciidoc b/docs/user/reporting/chromium-sandbox.asciidoc index 5d4fbfb153a0bb..bfef5b8b86c6b1 100644 --- a/docs/user/reporting/chromium-sandbox.asciidoc +++ b/docs/user/reporting/chromium-sandbox.asciidoc @@ -11,12 +11,12 @@ sandboxing techniques differ for each operating system. The Linux sandbox depends on user namespaces, which were introduced with the 3.8 Linux kernel. However, many distributions don't have user namespaces enabled by default, or they require the CAP_SYS_ADMIN capability. {reporting} will automatically disable the sandbox when it is running on Debian and CentOS as additional steps are required to enable -unprivileged usernamespaces. In these situations, you'll see the following message in your {kib} logs: -`Enabling the Chromium sandbox provides an additional layer of protection`. +unprivileged usernamespaces. In these situations, you'll see the following message in your {kib} startup logs: +`Chromium sandbox provides an additional layer of protection, but is not supported for your OS. +Automatically setting 'xpack.reporting.capture.browser.chromium.disableSandbox: true'.` -If your kernel is 3.8 or newer, it's -recommended to enable usernamespaces and set `xpack.reporting.capture.browser.chromium.disableSandbox: false` in your -`kibana.yml` to enable the sandbox. +Reporting will automatically enable the Chromium sandbox at startup when a supported OS is detected. However, if your kernel is 3.8 or newer, it's +recommended to set `xpack.reporting.capture.browser.chromium.disableSandbox: false` in your `kibana.yml` to explicitly enable usernamespaces. ==== Docker When running {kib} in a Docker container, all container processes are run within a usernamespace with seccomp-bpf and diff --git a/src/legacy/core_plugins/vis_type_timelion/public/legacy_imports.ts b/examples/embeddable_examples/common/index.ts similarity index 87% rename from src/legacy/core_plugins/vis_type_timelion/public/legacy_imports.ts rename to examples/embeddable_examples/common/index.ts index e7612b288fb246..726420fb9bdc36 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/legacy_imports.ts +++ b/examples/embeddable_examples/common/index.ts @@ -17,5 +17,4 @@ * under the License. */ -export { npSetup, npStart } from 'ui/new_platform'; -export { PluginsStart } from 'ui/new_platform/new_platform'; +export { TodoSavedObjectAttributes } from './todo_saved_object_attributes'; diff --git a/examples/embeddable_examples/common/todo_saved_object_attributes.ts b/examples/embeddable_examples/common/todo_saved_object_attributes.ts new file mode 100644 index 00000000000000..21b6df20fea90d --- /dev/null +++ b/examples/embeddable_examples/common/todo_saved_object_attributes.ts @@ -0,0 +1,26 @@ +/* + * 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 { SavedObjectAttributes } from '../../../src/core/types'; + +export interface TodoSavedObjectAttributes extends SavedObjectAttributes { + task: string; + icon?: string; + title?: string; +} diff --git a/examples/embeddable_examples/kibana.json b/examples/embeddable_examples/kibana.json index c70bc7009ff519..f446e7f31ac8ef 100644 --- a/examples/embeddable_examples/kibana.json +++ b/examples/embeddable_examples/kibana.json @@ -3,7 +3,7 @@ "version": "0.0.1", "kibanaVersion": "kibana", "configPath": ["embeddable_examples"], - "server": false, + "server": true, "ui": true, "requiredPlugins": ["embeddable"], "optionalPlugins": [] diff --git a/examples/embeddable_examples/public/create_sample_data.ts b/examples/embeddable_examples/public/create_sample_data.ts new file mode 100644 index 00000000000000..bd5ade18aa91eb --- /dev/null +++ b/examples/embeddable_examples/public/create_sample_data.ts @@ -0,0 +1,36 @@ +/* + * 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 { SavedObjectsClientContract } from 'kibana/public'; +import { TodoSavedObjectAttributes } from '../common'; + +export async function createSampleData(client: SavedObjectsClientContract) { + await client.create( + 'todo', + { + task: 'Take the garbage out', + title: 'Garbage', + icon: 'trash', + }, + { + id: 'sample-todo-saved-object', + overwrite: true, + } + ); +} diff --git a/examples/embeddable_examples/public/index.ts b/examples/embeddable_examples/public/index.ts index 5fcd454b17a5c3..4aac63fb52e2be 100644 --- a/examples/embeddable_examples/public/index.ts +++ b/examples/embeddable_examples/public/index.ts @@ -17,7 +17,6 @@ * under the License. */ -import { PluginInitializer } from 'kibana/public'; export { HELLO_WORLD_EMBEDDABLE, HelloWorldEmbeddable, @@ -26,18 +25,8 @@ export { export { ListContainer, LIST_CONTAINER } from './list_container'; export { TODO_EMBEDDABLE } from './todo'; -import { - EmbeddableExamplesPlugin, - EmbeddableExamplesSetupDependencies, - EmbeddableExamplesStartDependencies, -} from './plugin'; +import { EmbeddableExamplesPlugin } from './plugin'; export { SearchableListContainer, SEARCHABLE_LIST_CONTAINER } from './searchable_list_container'; export { MULTI_TASK_TODO_EMBEDDABLE } from './multi_task_todo'; - -export const plugin: PluginInitializer< - void, - void, - EmbeddableExamplesSetupDependencies, - EmbeddableExamplesStartDependencies -> = () => new EmbeddableExamplesPlugin(); +export const plugin = () => new EmbeddableExamplesPlugin(); diff --git a/examples/embeddable_examples/public/plugin.ts b/examples/embeddable_examples/public/plugin.ts index 31a3037332dda9..75d34d2d6878fd 100644 --- a/examples/embeddable_examples/public/plugin.ts +++ b/examples/embeddable_examples/public/plugin.ts @@ -21,12 +21,20 @@ import { EmbeddableSetup, EmbeddableStart } from '../../../src/plugins/embeddabl import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public'; import { HelloWorldEmbeddableFactory, HELLO_WORLD_EMBEDDABLE } from './hello_world'; import { TODO_EMBEDDABLE, TodoEmbeddableFactory, TodoInput, TodoOutput } from './todo'; -import { MULTI_TASK_TODO_EMBEDDABLE, MultiTaskTodoEmbeddableFactory } from './multi_task_todo'; +import { + MULTI_TASK_TODO_EMBEDDABLE, + MultiTaskTodoEmbeddableFactory, + MultiTaskTodoInput, + MultiTaskTodoOutput, +} from './multi_task_todo'; import { SEARCHABLE_LIST_CONTAINER, SearchableListContainerFactory, } from './searchable_list_container'; import { LIST_CONTAINER, ListContainerFactory } from './list_container'; +import { createSampleData } from './create_sample_data'; +import { TodoRefInput, TodoRefOutput, TODO_REF_EMBEDDABLE } from './todo/todo_ref_embeddable'; +import { TodoRefEmbeddableFactory } from './todo/todo_ref_embeddable_factory'; export interface EmbeddableExamplesSetupDependencies { embeddable: EmbeddableSetup; @@ -36,9 +44,18 @@ export interface EmbeddableExamplesStartDependencies { embeddable: EmbeddableStart; } +export interface EmbeddableExamplesStart { + createSampleData: () => Promise; +} + export class EmbeddableExamplesPlugin implements - Plugin { + Plugin< + void, + EmbeddableExamplesStart, + EmbeddableExamplesSetupDependencies, + EmbeddableExamplesStartDependencies + > { public setup( core: CoreSetup, deps: EmbeddableExamplesSetupDependencies @@ -48,7 +65,7 @@ export class EmbeddableExamplesPlugin new HelloWorldEmbeddableFactory() ); - deps.embeddable.registerEmbeddableFactory( + deps.embeddable.registerEmbeddableFactory( MULTI_TASK_TODO_EMBEDDABLE, new MultiTaskTodoEmbeddableFactory() ); @@ -73,9 +90,21 @@ export class EmbeddableExamplesPlugin openModal: (await core.getStartServices())[0].overlays.openModal, })) ); + + deps.embeddable.registerEmbeddableFactory( + TODO_REF_EMBEDDABLE, + new TodoRefEmbeddableFactory(async () => ({ + savedObjectsClient: (await core.getStartServices())[0].savedObjects.client, + getEmbeddableFactory: (await core.getStartServices())[1].embeddable.getEmbeddableFactory, + })) + ); } - public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) {} + public start(core: CoreStart, deps: EmbeddableExamplesStartDependencies) { + return { + createSampleData: () => createSampleData(core.savedObjects.client), + }; + } public stop() {} } diff --git a/examples/embeddable_examples/public/todo/README.md b/examples/embeddable_examples/public/todo/README.md new file mode 100644 index 00000000000000..e782511f093b3f --- /dev/null +++ b/examples/embeddable_examples/public/todo/README.md @@ -0,0 +1,43 @@ +There are two examples in here: + - TodoEmbeddable + - TodoRefEmbeddable + + # TodoEmbeddable + + The first example you should review is the HelloWorldEmbeddable. That is as basic an embeddable as you can get. + This embeddable is the next step up - an embeddable that renders dynamic input data. The data is simple: + - a required task string + - an optional title + - an optional icon string + - an optional search string + +It also has output data, which is `hasMatch` - whether or not the search string has matched any input data. + +`hasMatch` is a better fit for output data than input data, because it's state that is _derived_ from input data. + +For example, if it was input data, you could create a TodoEmbeddable with input like this: + +```ts + todoEmbeddableFactory.create({ task: 'take out the garabage', search: 'garbage', hasMatch: false }); +``` + +That's wrong because there is actually a match from the search string inside the task. + +The TodoEmbeddable component itself doesn't do anything with the `hasMatch` variable other than set it, but +if you check out `SearchableListContainer`, you can see an example where this output data is being used. + +## TodoRefEmbeddable + +This is an example of an embeddable based off of a saved object. The input is just the `savedObjectId` and +the `search` string. It has even more output parameters, and this time, it does read it's own output parameters in +order to calculate `hasMatch`. + +Output: +```ts +{ + hasMatch: boolean, + savedAttributes?: TodoSavedAttributes +} +``` + +`savedAttributes` is optional because it's possible a TodoSavedObject could not be found with the given savedObjectId. diff --git a/examples/embeddable_examples/public/todo/todo_ref_component.tsx b/examples/embeddable_examples/public/todo/todo_ref_component.tsx new file mode 100644 index 00000000000000..8e0a17be1ec729 --- /dev/null +++ b/examples/embeddable_examples/public/todo/todo_ref_component.tsx @@ -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 React from 'react'; +import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; + +import { EuiText } from '@elastic/eui'; +import { EuiAvatar } from '@elastic/eui'; +import { EuiIcon } from '@elastic/eui'; +import { EuiFlexGrid } from '@elastic/eui'; +import { withEmbeddableSubscription } from '../../../../src/plugins/embeddable/public'; +import { TodoRefInput, TodoRefOutput, TodoRefEmbeddable } from './todo_ref_embeddable'; + +interface Props { + embeddable: TodoRefEmbeddable; + input: TodoRefInput; + output: TodoRefOutput; +} + +function wrapSearchTerms(task?: string, search?: string) { + if (!search) return task; + if (!task) return task; + const parts = task.split(new RegExp(`(${search})`, 'g')); + return parts.map((part, i) => + part === search ? ( + + {part} + + ) : ( + part + ) + ); +} + +export function TodoRefEmbeddableComponentInner({ + input: { search }, + output: { savedAttributes }, +}: Props) { + const icon = savedAttributes?.icon; + const title = savedAttributes?.title; + const task = savedAttributes?.task; + return ( + + + {icon ? ( + + ) : ( + + )} + + + + + +

{wrapSearchTerms(title || '', search)}

+ + + + {wrapSearchTerms(task, search)} + + + + + ); +} + +export const TodoRefEmbeddableComponent = withEmbeddableSubscription< + TodoRefInput, + TodoRefOutput, + TodoRefEmbeddable +>(TodoRefEmbeddableComponentInner); diff --git a/examples/embeddable_examples/public/todo/todo_ref_embeddable.tsx b/examples/embeddable_examples/public/todo/todo_ref_embeddable.tsx new file mode 100644 index 00000000000000..da2dfb2c1a2903 --- /dev/null +++ b/examples/embeddable_examples/public/todo/todo_ref_embeddable.tsx @@ -0,0 +1,153 @@ +/* + * 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 { Subscription } from 'rxjs'; +import { TodoSavedObjectAttributes } from 'examples/embeddable_examples/common'; +import { SavedObjectsClientContract } from 'kibana/public'; +import { + Embeddable, + IContainer, + EmbeddableOutput, + SavedObjectEmbeddableInput, +} from '../../../../src/plugins/embeddable/public'; +import { TodoRefEmbeddableComponent } from './todo_ref_component'; + +// Notice this is not the same value as the 'todo' saved object type. Many of our +// cases in prod today use the same value, but this is unnecessary. +export const TODO_REF_EMBEDDABLE = 'TODO_REF_EMBEDDABLE'; + +export interface TodoRefInput extends SavedObjectEmbeddableInput { + /** + * Optional search string which will be used to highlight search terms as + * well as calculate `output.hasMatch`. + */ + search?: string; +} + +export interface TodoRefOutput extends EmbeddableOutput { + /** + * Should be true if input.search is defined and the task or title contain + * search as a substring. + */ + hasMatch: boolean; + /** + * Will contain the saved object attributes of the Todo Saved Object that matches + * `input.savedObjectId`. If the id is invalid, this may be undefined. + */ + savedAttributes?: TodoSavedObjectAttributes; +} + +/** + * Returns whether any attributes contain the search string. If search is empty, true is returned. If + * there are no savedAttributes, false is returned. + * @param search - the search string + * @param savedAttributes - the saved object attributes for the saved object with id `input.savedObjectId` + */ +function getHasMatch(search?: string, savedAttributes?: TodoSavedObjectAttributes): boolean { + if (!search) return true; + if (!savedAttributes) return false; + return Boolean( + (savedAttributes.task && savedAttributes.task.match(search)) || + (savedAttributes.title && savedAttributes.title.match(search)) + ); +} + +/** + * This is an example of an embeddable that is backed by a saved object. It's essentially the + * same as `TodoEmbeddable` but that is "by value", while this is "by reference". + */ +export class TodoRefEmbeddable extends Embeddable { + public readonly type = TODO_REF_EMBEDDABLE; + private subscription: Subscription; + private node?: HTMLElement; + private savedObjectsClient: SavedObjectsClientContract; + private savedObjectId?: string; + + constructor( + initialInput: TodoRefInput, + { + parent, + savedObjectsClient, + }: { + parent?: IContainer; + savedObjectsClient: SavedObjectsClientContract; + } + ) { + super(initialInput, { hasMatch: false }, parent); + this.savedObjectsClient = savedObjectsClient; + + this.subscription = this.getInput$().subscribe(async () => { + // There is a little more work today for this embeddable because it has + // more output it needs to update in response to input state changes. + let savedAttributes: TodoSavedObjectAttributes | undefined; + + // Since this is an expensive task, we save a local copy of the previous + // savedObjectId locally and only retrieve the new saved object if the id + // actually changed. + if (this.savedObjectId !== this.input.savedObjectId) { + this.savedObjectId = this.input.savedObjectId; + const todoSavedObject = await this.savedObjectsClient.get( + 'todo', + this.input.savedObjectId + ); + savedAttributes = todoSavedObject?.attributes; + } + + // The search string might have changed as well so we need to make sure we recalculate + // hasMatch. + this.updateOutput({ + hasMatch: getHasMatch(this.input.search, savedAttributes), + savedAttributes, + }); + }); + } + + public render(node: HTMLElement) { + if (this.node) { + ReactDOM.unmountComponentAtNode(this.node); + } + this.node = node; + ReactDOM.render(, node); + } + + /** + * Lets re-sync our saved object to make sure it's up to date! + */ + public async reload() { + this.savedObjectId = this.input.savedObjectId; + const todoSavedObject = await this.savedObjectsClient.get( + 'todo', + this.input.savedObjectId + ); + const savedAttributes = todoSavedObject?.attributes; + this.updateOutput({ + hasMatch: getHasMatch(this.input.search, savedAttributes), + savedAttributes, + }); + } + + public destroy() { + super.destroy(); + this.subscription.unsubscribe(); + if (this.node) { + ReactDOM.unmountComponentAtNode(this.node); + } + } +} diff --git a/examples/embeddable_examples/public/todo/todo_ref_embeddable_factory.tsx b/examples/embeddable_examples/public/todo/todo_ref_embeddable_factory.tsx new file mode 100644 index 00000000000000..e585ddd89674fe --- /dev/null +++ b/examples/embeddable_examples/public/todo/todo_ref_embeddable_factory.tsx @@ -0,0 +1,83 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { SavedObjectsClientContract } from 'kibana/public'; +import { TodoSavedObjectAttributes } from 'examples/embeddable_examples/common'; +import { + IContainer, + EmbeddableStart, + ErrorEmbeddable, + EmbeddableFactoryDefinition, +} from '../../../../src/plugins/embeddable/public'; +import { + TodoRefEmbeddable, + TODO_REF_EMBEDDABLE, + TodoRefInput, + TodoRefOutput, +} from './todo_ref_embeddable'; + +interface StartServices { + getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; + savedObjectsClient: SavedObjectsClientContract; +} + +export class TodoRefEmbeddableFactory + implements + EmbeddableFactoryDefinition< + TodoRefInput, + TodoRefOutput, + TodoRefEmbeddable, + TodoSavedObjectAttributes + > { + public readonly type = TODO_REF_EMBEDDABLE; + public readonly savedObjectMetaData = { + name: 'Todo', + includeFields: ['task', 'icon', 'title'], + type: 'todo', + getIconForSavedObject: () => 'pencil', + }; + + constructor(private getStartServices: () => Promise) {} + + public async isEditable() { + return true; + } + + public createFromSavedObject = ( + savedObjectId: string, + input: Partial & { id: string }, + parent?: IContainer + ): Promise => { + return this.create({ ...input, savedObjectId }, parent); + }; + + public async create(input: TodoRefInput, parent?: IContainer) { + const { savedObjectsClient } = await this.getStartServices(); + return new TodoRefEmbeddable(input, { + parent, + savedObjectsClient, + }); + } + + public getDisplayName() { + return i18n.translate('embeddableExamples.todo.displayName', { + defaultMessage: 'Todo (by reference)', + }); + } +} diff --git a/examples/embeddable_examples/server/index.ts b/examples/embeddable_examples/server/index.ts new file mode 100644 index 00000000000000..9ddc3bc2cf7156 --- /dev/null +++ b/examples/embeddable_examples/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 { EmbeddableExamplesPlugin } from './plugin'; + +export const plugin: PluginInitializer = () => new EmbeddableExamplesPlugin(); diff --git a/examples/embeddable_examples/server/plugin.ts b/examples/embeddable_examples/server/plugin.ts new file mode 100644 index 00000000000000..d956b834d0d3ca --- /dev/null +++ b/examples/embeddable_examples/server/plugin.ts @@ -0,0 +1,31 @@ +/* + * 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 { todoSavedObject } from './todo_saved_object'; + +export class EmbeddableExamplesPlugin implements Plugin { + public setup(core: CoreSetup) { + core.savedObjects.registerType(todoSavedObject); + } + + public start(core: CoreStart) {} + + public stop() {} +} diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts b/examples/embeddable_examples/server/todo_saved_object.ts similarity index 68% rename from src/legacy/core_plugins/kibana/public/visualize/legacy.ts rename to examples/embeddable_examples/server/todo_saved_object.ts index 4ef2c936897141..58da2014de4982 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts +++ b/examples/embeddable_examples/server/todo_saved_object.ts @@ -17,12 +17,24 @@ * under the License. */ -import { PluginInitializerContext } from 'kibana/public'; -import { npSetup, npStart } from 'ui/new_platform'; -import { plugin } from './index'; +import { SavedObjectsType } from 'kibana/server'; -const instance = plugin({ - env: npSetup.plugins.kibanaLegacy.env, -} as PluginInitializerContext); -instance.setup(npSetup.core, npSetup.plugins); -instance.start(npStart.core, npStart.plugins); +export const todoSavedObject: SavedObjectsType = { + name: 'todo', + hidden: false, + namespaceType: 'agnostic', + mappings: { + properties: { + title: { + type: 'keyword', + }, + task: { + type: 'text', + }, + icon: { + type: 'keyword', + }, + }, + }, + migrations: {}, +}; diff --git a/examples/embeddable_examples/tsconfig.json b/examples/embeddable_examples/tsconfig.json index 091130487791bd..7fa03739119b43 100644 --- a/examples/embeddable_examples/tsconfig.json +++ b/examples/embeddable_examples/tsconfig.json @@ -6,6 +6,7 @@ }, "include": [ "index.ts", + "common/**/*.ts", "public/**/*.ts", "public/**/*.tsx", "server/**/*.ts", diff --git a/examples/embeddable_explorer/public/plugin.tsx b/examples/embeddable_explorer/public/plugin.tsx index 7c75b108d99122..bba1b1748e2077 100644 --- a/examples/embeddable_explorer/public/plugin.tsx +++ b/examples/embeddable_explorer/public/plugin.tsx @@ -18,6 +18,7 @@ */ import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; +import { EmbeddableExamplesStart } from 'examples/embeddable_examples/public/plugin'; import { UiActionsService } from '../../../src/plugins/ui_actions/public'; import { EmbeddableStart } from '../../../src/plugins/embeddable/public'; import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; @@ -26,6 +27,7 @@ interface StartDeps { uiActions: UiActionsService; embeddable: EmbeddableStart; inspector: InspectorStart; + embeddableExamples: EmbeddableExamplesStart; } export class EmbeddableExplorerPlugin implements Plugin { @@ -36,6 +38,7 @@ export class EmbeddableExplorerPlugin implements Plugin) { return new AnyType(options); diff --git a/packages/kbn-config-schema/src/typeguards/index.ts b/packages/kbn-config-schema/src/typeguards/index.ts new file mode 100644 index 00000000000000..e724878eb33e9a --- /dev/null +++ b/packages/kbn-config-schema/src/typeguards/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { isConfigSchema } from './is_config_schema'; diff --git a/packages/kbn-config-schema/src/typeguards/is_config_schema.test.ts b/packages/kbn-config-schema/src/typeguards/is_config_schema.test.ts new file mode 100644 index 00000000000000..e0ef3835ca0a37 --- /dev/null +++ b/packages/kbn-config-schema/src/typeguards/is_config_schema.test.ts @@ -0,0 +1,56 @@ +/* + * 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 '..'; +import { isConfigSchema } from './is_config_schema'; + +describe('isConfigSchema', () => { + it('returns true for every sub classes of `Type`', () => { + expect(isConfigSchema(schema.any())).toBe(true); + expect(isConfigSchema(schema.arrayOf(schema.string()))).toBe(true); + expect(isConfigSchema(schema.boolean())).toBe(true); + expect(isConfigSchema(schema.buffer())).toBe(true); + expect(isConfigSchema(schema.byteSize())).toBe(true); + expect(isConfigSchema(schema.duration())).toBe(true); + expect(isConfigSchema(schema.literal(''))).toBe(true); + expect(isConfigSchema(schema.mapOf(schema.string(), schema.number()))).toBe(true); + expect(isConfigSchema(schema.nullable(schema.string()))).toBe(true); + expect(isConfigSchema(schema.number())).toBe(true); + expect(isConfigSchema(schema.object({}))).toBe(true); + expect(isConfigSchema(schema.oneOf([schema.string()]))).toBe(true); + expect(isConfigSchema(schema.recordOf(schema.string(), schema.object({})))).toBe(true); + expect(isConfigSchema(schema.string())).toBe(true); + expect(isConfigSchema(schema.stream())).toBe(true); + }); + + it('returns false for every javascript data type', () => { + expect(isConfigSchema('foo')).toBe(false); + expect(isConfigSchema(42)).toBe(false); + expect(isConfigSchema(new Date())).toBe(false); + expect(isConfigSchema(null)).toBe(false); + expect(isConfigSchema(undefined)).toBe(false); + expect(isConfigSchema([1, 2, 3])).toBe(false); + expect(isConfigSchema({ foo: 'bar' })).toBe(false); + expect(isConfigSchema(function() {})).toBe(false); + }); + + it('returns true as long as `__isKbnConfigSchemaType` is true', () => { + expect(isConfigSchema({ __isKbnConfigSchemaType: true })).toBe(true); + }); +}); diff --git a/packages/kbn-config-schema/src/typeguards/is_config_schema.ts b/packages/kbn-config-schema/src/typeguards/is_config_schema.ts new file mode 100644 index 00000000000000..20e68ab2ead254 --- /dev/null +++ b/packages/kbn-config-schema/src/typeguards/is_config_schema.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 { Type } from '../types'; + +export function isConfigSchema(obj: any): obj is Type { + return obj ? obj.__isKbnConfigSchemaType === true : false; +} diff --git a/packages/kbn-config-schema/src/types/type.ts b/packages/kbn-config-schema/src/types/type.ts index 6d5ddf6b24afb5..5ca16c61399e7e 100644 --- a/packages/kbn-config-schema/src/types/type.ts +++ b/packages/kbn-config-schema/src/types/type.ts @@ -32,6 +32,9 @@ export abstract class Type { // sets the value to `null` while still keeping the type. public readonly type: V = null! as V; + // used for the `isConfigSchema` typeguard + public readonly __isKbnConfigSchemaType = true; + /** * Internal "schema" backed by Joi. * @type {Schema} diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts index d67b9574167530..cc564dd4a8387c 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts @@ -146,6 +146,7 @@ describe('OptimizerConfig::parseOptions()', () => { /x-pack/plugins, /plugins, /examples, + /x-pack/examples, -extra, ], "profileWebpack": false, diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index 1c8ae265bf6bb8..7e1514058446b9 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -91,14 +91,14 @@ export class OptimizerConfig { /** * BEWARE: this needs to stay roughly synchronized with - * `src/core/server/config/env.ts` which determins which paths + * `src/core/server/config/env.ts` which determines which paths * should be searched for plugins to load */ const pluginScanDirs = options.pluginScanDirs || [ Path.resolve(repoRoot, 'src/plugins'), ...(oss ? [] : [Path.resolve(repoRoot, 'x-pack/plugins')]), Path.resolve(repoRoot, 'plugins'), - ...(examples ? [Path.resolve('examples')] : []), + ...(examples ? [Path.resolve('examples'), Path.resolve('x-pack/examples')] : []), Path.resolve(repoRoot, '../kibana-extra'), ]; if (!pluginScanDirs.every(p => Path.isAbsolute(p))) { diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index fa8884e2ece758..6a2d02ee778dda 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -42623,28 +42623,21 @@ module.exports = require("tty"); const os = __webpack_require__(11); const hasFlag = __webpack_require__(12); -const {env} = process; +const env = process.env; let forceColor; if (hasFlag('no-color') || hasFlag('no-colors') || - hasFlag('color=false') || - hasFlag('color=never')) { - forceColor = 0; + hasFlag('color=false')) { + forceColor = false; } else if (hasFlag('color') || hasFlag('colors') || hasFlag('color=true') || hasFlag('color=always')) { - forceColor = 1; + forceColor = true; } if ('FORCE_COLOR' in env) { - if (env.FORCE_COLOR === true || env.FORCE_COLOR === 'true') { - forceColor = 1; - } else if (env.FORCE_COLOR === false || env.FORCE_COLOR === 'false') { - forceColor = 0; - } else { - forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); - } + forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; } function translateLevel(level) { @@ -42661,7 +42654,7 @@ function translateLevel(level) { } function supportsColor(stream) { - if (forceColor === 0) { + if (forceColor === false) { return 0; } @@ -42675,15 +42668,11 @@ function supportsColor(stream) { return 2; } - if (stream && !stream.isTTY && forceColor === undefined) { + if (stream && !stream.isTTY && forceColor !== true) { return 0; } - const min = forceColor || 0; - - if (env.TERM === 'dumb') { - return min; - } + const min = forceColor ? 1 : 0; if (process.platform === 'win32') { // Node.js 7.5.0 is the first version of Node.js to include a patch to @@ -42744,6 +42733,10 @@ function supportsColor(stream) { return 1; } + if (env.TERM === 'dumb') { + return min; + } + return min; } diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 66f17ab579ec39..f4b91d154cbb88 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -136,7 +136,7 @@ export const schema = Joi.object() browser: Joi.object() .keys({ type: Joi.string() - .valid('chrome', 'firefox', 'ie') + .valid('chrome', 'firefox', 'ie', 'msedge') .default('chrome'), logPollingMs: Joi.number().default(100), diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 7c5d6a62a11caa..c8614b1df9d5d6 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -9,7 +9,7 @@ "kbn:watch": "node scripts/build --watch" }, "dependencies": { - "@elastic/charts": "18.2.2", + "@elastic/charts": "18.3.0", "@elastic/eui": "21.0.1", "@kbn/i18n": "1.0.0", "abortcontroller-polyfill": "^1.4.0", diff --git a/src/cli/cluster/cluster_manager.ts b/src/cli/cluster/cluster_manager.ts index 44b6c39556afd8..a87e2aa11f2c0d 100644 --- a/src/cli/cluster/cluster_manager.ts +++ b/src/cli/cluster/cluster_manager.ts @@ -263,7 +263,7 @@ export class ClusterManager { ...pluginInternalDirsIgnore, fromRoot('src/legacy/server/sass/__tmp__'), fromRoot('x-pack/legacy/plugins/reporting/.chromium'), - fromRoot('x-pack/legacy/plugins/siem/cypress'), + fromRoot('x-pack/plugins/siem/cypress'), fromRoot('x-pack/legacy/plugins/apm/e2e'), fromRoot('x-pack/legacy/plugins/apm/scripts'), fromRoot('x-pack/legacy/plugins/canvas/canvas_plugin_src'), // prevents server from restarting twice for Canvas plugin changes, diff --git a/src/core/CONVENTIONS.md b/src/core/CONVENTIONS.md index 0f592d108c5617..a82cc27839a1d7 100644 --- a/src/core/CONVENTIONS.md +++ b/src/core/CONVENTIONS.md @@ -1,6 +1,6 @@ -# Kibana Conventions - -- [Kibana Conventions](#kibana-conventions) +- [Organisational Conventions](#organisational-conventions) + - [Definition of done](#definition-of-done) +- [Technical Conventions](#technical-conventions) - [Plugin Structure](#plugin-structure) - [The PluginInitializer](#the-plugininitializer) - [The Plugin class](#the-plugin-class) @@ -8,8 +8,34 @@ - [Services](#services) - [Usage Collection](#usage-collection) - [Saved Objects Types](#saved-objects-types) - -## Plugin Structure + - [Naming conventions](#naming-conventions) + +## Organisational Conventions +### Definition of done +Definition of done for a feature: +- has a dedicated Github issue describing problem space +- an umbrella task closed/updated with follow-ups +- all code review comments are resolved +- has been verified manually by at least one reviewer +- can be used by first & third party plugins +- there is no contradiction between client and server API +- works for OSS version + - works with and without a `server.basePath` configured + - cannot crash the Kibana server when it fails +- works for the commercial version with a license + - for a logged-in user + - for anonymous user + - compatible with Spaces +- has unit & integration tests for public contracts +- has functional tests for user scenarios +- uses standard tooling: + - code - `TypeScript` + - UI - `React` + - tests - `jest` & `FTR` +- has documentation for the public contract, provides a usage example + +## Technical Conventions +### Plugin Structure All Kibana plugins built at Elastic should follow the same structure. @@ -60,7 +86,7 @@ my_plugin/ - More should be fleshed out here... - Usage collectors for Telemetry should be defined in a separate `server/collectors/` directory. -### The PluginInitializer +#### The PluginInitializer ```ts // my_plugin/public/index.ts @@ -75,7 +101,7 @@ export { } ``` -### The Plugin class +#### The Plugin class ```ts // my_plugin/public/plugin.ts @@ -130,7 +156,7 @@ Difference between `setup` and `start`: The bulk of your plugin logic will most likely live inside _handlers_ registered during `setup`. -### Applications +#### Applications It's important that UI code is not included in the main bundle for your plugin. Our webpack configuration supports dynamic async imports to split out imports into a separate bundle. Every app's rendering logic and UI code should @@ -175,7 +201,7 @@ export class MyPlugin implements Plugin { } ``` -### Services +#### Services Service structure should mirror the plugin lifecycle to make reasoning about how the service is executed more clear. @@ -226,7 +252,7 @@ export class Plugin { } ``` -### Usage Collection +#### Usage Collection For creating and registering a Usage Collector. Collectors should be defined in a separate directory `server/collectors/`. You can read more about usage collectors on `src/plugins/usage_collection/README.md`. @@ -263,7 +289,7 @@ export function registerMyPluginUsageCollector(usageCollection?: UsageCollection } ``` -### Saved Objects Types +#### Saved Objects Types Saved object type definitions should be defined in their own `server/saved_objects` directory. diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 368d1f47e9c3fa..80f12dd78214d1 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -1252,26 +1252,27 @@ import { npStart: { plugins } } from 'ui/new_platform'; In server code, `core` can be accessed from either `server.newPlatform` or `kbnServer.newPlatform`. There are not currently very many services available on the server-side: -| Legacy Platform | New Platform | Notes | -| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | -| `server.config()` | [`initializerContext.config.create()`](/docs/development/core/server/kibana-plugin-core-server.plugininitializercontext.config.md) | Must also define schema. See _[how to configure plugin](#configure-plugin)_ | -| `server.route` | [`core.http.createRouter`](/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.createrouter.md) | [Examples](./MIGRATION_EXAMPLES.md#route-registration) | -| `server.renderApp()` / `server.renderAppWithDefaultConfig()` | [`context.rendering.render()`](/docs/development/core/server/kibana-plugin-core-server.iscopedrenderingclient.render.md) | [Examples](./MIGRATION_EXAMPLES.md#render-html-content) | -| `request.getBasePath()` | [`core.http.basePath.get`](/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.basepath.md) | | +| Legacy Platform | New Platform | Notes | +| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| `server.config()` | [`initializerContext.config.create()`](/docs/development/core/server/kibana-plugin-core-server.plugininitializercontext.config.md) | Must also define schema. See _[how to configure plugin](#configure-plugin)_ | +| `server.route` | [`core.http.createRouter`](/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.createrouter.md) | [Examples](./MIGRATION_EXAMPLES.md#route-registration) | +| `server.renderApp()` | [`response.renderCoreApp()`](docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.rendercoreapp.md) | [Examples](./MIGRATION_EXAMPLES.md#render-html-content) | +| `server.renderAppWithDefaultConfig()` | [`response.renderAnonymousCoreApp()`](docs/development/core/server/kibana-plugin-core-server.httpresourcesservicetoolkit.renderanonymouscoreapp.md) | [Examples](./MIGRATION_EXAMPLES.md#render-html-content) | +| `request.getBasePath()` | [`core.http.basePath.get`](/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.basepath.md) | | | `server.plugins.elasticsearch.getCluster('data')` | [`context.core.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md) | | | `server.plugins.elasticsearch.getCluster('admin')` | [`context.core.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md) | | -| `server.plugins.elasticsearch.createCluster(...)` | [`core.elasticsearch.legacy.createClient`](/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.legacy.md) | | -| `server.savedObjects.setScopedSavedObjectsClientFactory` | [`core.savedObjects.setClientFactoryProvider`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.setclientfactoryprovider.md) | | -| `server.savedObjects.addScopedSavedObjectsClientWrapperFactory` | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.addclientwrapper.md) | | +| `server.plugins.elasticsearch.createCluster(...)` | [`core.elasticsearch.legacy.createClient`](/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicestart.legacy.md) | | +| `server.savedObjects.setScopedSavedObjectsClientFactory` | [`core.savedObjects.setClientFactoryProvider`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.setclientfactoryprovider.md) | | +| `server.savedObjects.addScopedSavedObjectsClientWrapperFactory` | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.addclientwrapper.md) | | | `server.savedObjects.getSavedObjectsRepository` | [`core.savedObjects.createInternalRepository`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicestart.createinternalrepository.md) [`core.savedObjects.createScopedRepository`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicestart.createscopedrepository.md) | | -| `server.savedObjects.getScopedSavedObjectsClient` | [`core.savedObjects.getScopedClient`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicestart.getscopedclient.md) | | -| `request.getSavedObjectsClient` | [`context.core.savedObjects.client`](/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md) | | +| `server.savedObjects.getScopedSavedObjectsClient` | [`core.savedObjects.getScopedClient`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsservicestart.getscopedclient.md) | | +| `request.getSavedObjectsClient` | [`context.core.savedObjects.client`](/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md) | | | `request.getUiSettingsService` | [`context.core.uiSettings.client`](/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md) | | -| `kibana.Plugin.deprecations` | [Handle plugin configuration deprecations](#handle-plugin-config-deprecations) and [`PluginConfigDescriptor.deprecations`](docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md) | Deprecations from New Platform are not applied to legacy configuration | -| `kibana.Plugin.savedObjectSchemas` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) | -| `kibana.Plugin.mappings` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) | -| `kibana.Plugin.migrations` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) | -| `kibana.Plugin.savedObjectsManagement` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) | +| `kibana.Plugin.deprecations` | [Handle plugin configuration deprecations](#handle-plugin-config-deprecations) and [`PluginConfigDescriptor.deprecations`](docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md) | Deprecations from New Platform are not applied to legacy configuration | +| `kibana.Plugin.savedObjectSchemas` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) | +| `kibana.Plugin.mappings` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) | +| `kibana.Plugin.migrations` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) | +| `kibana.Plugin.savedObjectsManagement` | [`core.savedObjects.registerType`](docs/development/core/server/kibana-plugin-core-server.savedobjectsservicesetup.registertype.md) | [Examples](./MIGRATION_EXAMPLES.md#saved-objects-types) | _See also: [Server's CoreSetup API Docs](/docs/development/core/server/kibana-plugin-core-server.coresetup.md)_ @@ -1494,8 +1495,9 @@ The above example looks in the new platform as ``` The [request handler context](/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md) exposed the next scoped **core** services: -| Legacy Platform | New Platform | -| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------| + +| Legacy Platform | New Platform | +| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `request.getSavedObjectsClient` | [`context.savedObjects.client`](/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md) | | `server.plugins.elasticsearch.getCluster('admin')` | [`context.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md) | | `server.plugins.elasticsearch.getCluster('data')` | [`context.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-core-server.iscopedclusterclient.md) | diff --git a/src/core/MIGRATION_EXAMPLES.md b/src/core/MIGRATION_EXAMPLES.md index 37d0b9297ed3cc..8c5fe4875aaea2 100644 --- a/src/core/MIGRATION_EXAMPLES.md +++ b/src/core/MIGRATION_EXAMPLES.md @@ -700,21 +700,15 @@ application.register({ ## Render HTML Content You can return a blank HTML page bootstrapped with the core application bundle from an HTTP route handler -via the `rendering` context. You may wish to do this if you are rendering a chromeless application with a +via the `httpResources` service. You may wish to do this if you are rendering a chromeless application with a custom application route or have other custom rendering needs. -```ts -router.get( +```typescript +httpResources.register( { path: '/chromeless', validate: false }, (context, request, response) => { - const { http, rendering } = context.core; - - return response.ok({ - body: await rendering.render(), // generates an HTML document - headers: { - 'content-security-policy': http.csp.header, - }, - }); + //... some logic + return response.renderCoreApp(); } ); ``` @@ -724,18 +718,12 @@ comprises all UI Settings that are *user provided*, then injected into the page. You may wish to exclude fetching this data if not authorized or to slim the page size. -```ts -router.get( - { path: '/', validate: false }, +```typescript +httpResources.register( + { path: '/', validate: false, options: { authRequired: false } }, (context, request, response) => { - const { http, rendering } = context.core; - - return response.ok({ - body: await rendering.render({ includeUserSettings: false }), - headers: { - 'content-security-policy': http.csp.header, - }, - }); + //... some logic + return response.renderAnonymousCoreApp(); } ); ``` diff --git a/src/core/public/plugins/plugin_loader.test.ts b/src/core/public/plugins/plugin_loader.test.ts index b4e2c3095f14a7..18cc2d7a6f182e 100644 --- a/src/core/public/plugins/plugin_loader.test.ts +++ b/src/core/public/plugins/plugin_loader.test.ts @@ -62,7 +62,7 @@ test('`loadPluginBundles` creates a script tag and loads initializer', async () const fakeScriptTag = createdScriptTags[0]; expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith( 'src', - '/bundles/plugin/plugin-a/plugin-a.plugin.js' + '/bundles/plugin:plugin-a/plugin-a.plugin.js' ); expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith('id', 'kbn-plugin-plugin-a'); expect(fakeScriptTag.onload).toBeInstanceOf(Function); @@ -85,7 +85,7 @@ test('`loadPluginBundles` includes the basePath', async () => { const fakeScriptTag = createdScriptTags[0]; expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith( 'src', - '/mybasepath/bundles/plugin/plugin-a/plugin-a.plugin.js' + '/mybasepath/bundles/plugin:plugin-a/plugin-a.plugin.js' ); }); @@ -96,7 +96,7 @@ test('`loadPluginBundles` rejects if script.onerror is called', async () => { fakeScriptTag1.onerror(new Error('Whoa there!')); await expect(loadPromise).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to load \\"plugin-a\\" bundle (/bundles/plugin/plugin-a/plugin-a.plugin.js)"` + `"Failed to load \\"plugin-a\\" bundle (/bundles/plugin:plugin-a/plugin-a.plugin.js)"` ); }); @@ -105,7 +105,7 @@ test('`loadPluginBundles` rejects if timeout is reached', async () => { // Override the timeout to 1 ms for testi. loadPluginBundle(addBasePath, 'plugin-a', { timeoutMs: 1 }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Timeout reached when loading \\"plugin-a\\" bundle (/bundles/plugin/plugin-a/plugin-a.plugin.js)"` + `"Timeout reached when loading \\"plugin-a\\" bundle (/bundles/plugin:plugin-a/plugin-a.plugin.js)"` ); }); @@ -115,11 +115,11 @@ test('`loadPluginBundles` rejects if bundle does attach an initializer to window const fakeScriptTag1 = createdScriptTags[0]; // Setup a fake initializer as if a plugin bundle had actually been loaded. - coreWindow.__kbnBundles__['plugin/plugin-a'] = undefined; + coreWindow.__kbnBundles__['plugin:plugin-a'] = undefined; // Call the onload callback fakeScriptTag1.onload(); await expect(loadPromise).rejects.toThrowErrorMatchingInlineSnapshot( - `"Definition of plugin \\"plugin-a\\" should be a function (/bundles/plugin/plugin-a/plugin-a.plugin.js)."` + `"Definition of plugin \\"plugin-a\\" should be a function (/bundles/plugin:plugin-a/plugin-a.plugin.js)."` ); }); diff --git a/src/core/public/plugins/plugin_loader.ts b/src/core/public/plugins/plugin_loader.ts index bf7711055e97ba..9b35588dfe7262 100644 --- a/src/core/public/plugins/plugin_loader.ts +++ b/src/core/public/plugins/plugin_loader.ts @@ -93,7 +93,7 @@ export const loadPluginBundle: LoadPluginBundle = < const script = document.createElement('script'); // Assumes that all plugin bundles get put into the bundles/plugins subdirectory - const bundlePath = addBasePath(`/bundles/plugin/${pluginName}/${pluginName}.plugin.js`); + const bundlePath = addBasePath(`/bundles/plugin:${pluginName}/${pluginName}.plugin.js`); script.setAttribute('src', bundlePath); script.setAttribute('id', `kbn-plugin-${pluginName}`); script.setAttribute('async', ''); diff --git a/src/core/server/config/env.test.ts b/src/core/server/config/env.test.ts index c244012e344697..0fffcc44781d9c 100644 --- a/src/core/server/config/env.test.ts +++ b/src/core/server/config/env.test.ts @@ -164,6 +164,17 @@ test('pluginSearchPaths contains examples plugins path if --run-examples flag is expect(env.pluginSearchPaths).toContain('/some/home/dir/examples'); }); +test('pluginSearchPaths contains x-pack/examples plugins path if --run-examples flag is true', () => { + const env = new Env( + '/some/home/dir', + getEnvOptions({ + cliArgs: { runExamples: true }, + }) + ); + + expect(env.pluginSearchPaths).toContain('/some/home/dir/x-pack/examples'); +}); + test('pluginSearchPaths does not contains examples plugins path if --run-examples flag is false', () => { const env = new Env( '/some/home/dir', @@ -174,3 +185,14 @@ test('pluginSearchPaths does not contains examples plugins path if --run-example expect(env.pluginSearchPaths).not.toContain('/some/home/dir/examples'); }); + +test('pluginSearchPaths does not contains x-pack/examples plugins path if --run-examples flag is false', () => { + const env = new Env( + '/some/home/dir', + getEnvOptions({ + cliArgs: { runExamples: false }, + }) + ); + + expect(env.pluginSearchPaths).not.toContain('/some/home/dir/x-pack/examples'); +}); diff --git a/src/core/server/config/env.ts b/src/core/server/config/env.ts index 05a8f40a09a88f..d8068c5b383fab 100644 --- a/src/core/server/config/env.ts +++ b/src/core/server/config/env.ts @@ -109,7 +109,9 @@ export class Env { resolve(this.homeDir, 'src', 'plugins'), ...(options.cliArgs.oss ? [] : [resolve(this.homeDir, 'x-pack', 'plugins')]), resolve(this.homeDir, 'plugins'), - ...(options.cliArgs.runExamples ? [resolve(this.homeDir, 'examples')] : []), + ...(options.cliArgs.runExamples + ? [resolve(this.homeDir, 'examples'), resolve(this.homeDir, 'x-pack', 'examples')] + : []), resolve(this.homeDir, '..', 'kibana-extra'), ]; diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index 684f6e15caff98..18725f04a05b59 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -33,7 +33,12 @@ import { CoreService } from '../../types'; import { merge } from '../../utils'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; -import { ClusterClient, ScopeableRequest } from './cluster_client'; +import { + ClusterClient, + ScopeableRequest, + IClusterClient, + ICustomClusterClient, +} from './cluster_client'; import { ElasticsearchClientConfig } from './elasticsearch_client_config'; import { ElasticsearchConfig, ElasticsearchConfigType } from './elasticsearch_config'; import { InternalHttpServiceSetup, GetAuthHeaders } from '../http/'; @@ -58,12 +63,14 @@ export class ElasticsearchService implements CoreService { private readonly log: Logger; private readonly config$: Observable; - private subscription: Subscription | undefined; + private subscription?: Subscription; private stop$ = new Subject(); private kibanaVersion: string; - createClient: InternalElasticsearchServiceSetup['createClient'] | undefined; - dataClient: InternalElasticsearchServiceSetup['dataClient'] | undefined; - adminClient: InternalElasticsearchServiceSetup['adminClient'] | undefined; + private createClient?: ( + type: string, + clientConfig?: Partial + ) => ICustomClusterClient; + private adminClient?: IClusterClient; constructor(private readonly coreContext: CoreContext) { this.kibanaVersion = coreContext.env.packageInfo.version; diff --git a/src/core/server/http/index.ts b/src/core/server/http/index.ts index a75eb04fa01204..ca9dfde2e71dce 100644 --- a/src/core/server/http/index.ts +++ b/src/core/server/http/index.ts @@ -38,6 +38,7 @@ export { LifecycleResponseFactory, RedirectResponseOptions, RequestHandler, + RequestHandlerWrapper, ResponseError, ResponseErrorAttributes, ResponseHeaders, diff --git a/src/core/server/http/lifecycle/on_pre_response.ts b/src/core/server/http/lifecycle/on_pre_response.ts index 50d3d7b47bf8d6..050881472bc805 100644 --- a/src/core/server/http/lifecycle/on_pre_response.ts +++ b/src/core/server/http/lifecycle/on_pre_response.ts @@ -148,7 +148,7 @@ function findHeadersIntersection( log: Logger ) { Object.keys(headers).forEach(headerName => { - if (responseHeaders[headerName] !== undefined) { + if (Reflect.has(responseHeaders, headerName)) { log.warn(`onPreResponseHandler rewrote a response header [${headerName}].`); } }); diff --git a/src/core/server/http/router/error_wrapper.ts b/src/core/server/http/router/error_wrapper.ts index 8f895753c38c32..af99812eff4b37 100644 --- a/src/core/server/http/router/error_wrapper.ts +++ b/src/core/server/http/router/error_wrapper.ts @@ -18,20 +18,10 @@ */ import Boom from 'boom'; -import { KibanaRequest } from './request'; -import { KibanaResponseFactory } from './response'; -import { RequestHandler } from './router'; -import { RequestHandlerContext } from '../../../server'; -import { RouteMethod } from './route'; +import { RequestHandlerWrapper } from './router'; -export const wrapErrors = ( - handler: RequestHandler -): RequestHandler => { - return async ( - context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ) => { +export const wrapErrors: RequestHandlerWrapper = handler => { + return async (context, request, response) => { try { return await handler(context, request, response); } catch (e) { diff --git a/src/core/server/http/router/headers.ts b/src/core/server/http/router/headers.ts index 19eaee50819966..b79cc0d325f1e7 100644 --- a/src/core/server/http/router/headers.ts +++ b/src/core/server/http/router/headers.ts @@ -56,9 +56,9 @@ export type Headers = { [header in KnownHeaders]?: string | string[] | undefined * Http response headers to set. * @public */ -export type ResponseHeaders = { [header in KnownHeaders]?: string | string[] } & { - [header: string]: string | string[]; -}; +export type ResponseHeaders = + | Record + | Record; const normalizeHeaderField = (field: string) => field.trim().toLowerCase(); diff --git a/src/core/server/http/router/index.ts b/src/core/server/http/router/index.ts index d254f391ca5e41..83ceff4a25d86d 100644 --- a/src/core/server/http/router/index.ts +++ b/src/core/server/http/router/index.ts @@ -18,7 +18,7 @@ */ export { Headers, filterHeaders, ResponseHeaders, KnownHeaders } from './headers'; -export { Router, RequestHandler, IRouter, RouteRegistrar } from './router'; +export { Router, RequestHandler, RequestHandlerWrapper, IRouter, RouteRegistrar } from './router'; export { KibanaRequest, KibanaRequestEvents, diff --git a/src/core/server/http/router/router.ts b/src/core/server/http/router/router.ts index bb56ee3727d1a2..69402a74eda5f2 100644 --- a/src/core/server/http/router/router.ts +++ b/src/core/server/http/router/router.ts @@ -20,7 +20,7 @@ import { Request, ResponseObject, ResponseToolkit } from 'hapi'; import Boom from 'boom'; -import { Type } from '@kbn/config-schema'; +import { isConfigSchema } from '@kbn/config-schema'; import { Logger } from '../../logging'; import { KibanaRequest } from './request'; import { KibanaResponseFactory, kibanaResponseFactory, IKibanaResponse } from './response'; @@ -98,7 +98,7 @@ export interface IRouter { * Wrap a router handler to catch and converts legacy boom errors to proper custom errors. * @param handler {@link RequestHandler} - a route handler to wrap */ - handleLegacyErrors: (handler: RequestHandler) => RequestHandler; + handleLegacyErrors: RequestHandlerWrapper; /** * Returns all routes registered with this router. @@ -139,7 +139,7 @@ function routeSchemasFromRouteConfig( if (route.validate !== false) { Object.entries(route.validate).forEach(([key, schema]) => { - if (!(schema instanceof Type || typeof schema === 'function')) { + if (!(isConfigSchema(schema) || typeof schema === 'function')) { throw new Error( `Expected a valid validation logic declared with '@kbn/config-schema' package or a RouteValidationFunction at key: [${key}].` ); @@ -237,9 +237,7 @@ export class Router implements IRouter { return [...this.routes]; } - public handleLegacyErrors(handler: RequestHandler): RequestHandler { - return wrapErrors(handler); - } + public handleLegacyErrors = wrapErrors; private async handle({ routeSchemas, @@ -316,9 +314,33 @@ export type RequestHandler< P = unknown, Q = unknown, B = unknown, - Method extends RouteMethod = any + Method extends RouteMethod = any, + ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory > = ( context: RequestHandlerContext, request: KibanaRequest, - response: KibanaResponseFactory + response: ResponseFactory ) => IKibanaResponse | Promise>; + +/** + * Type-safe wrapper for {@link RequestHandler} function. + * @example + * ```typescript + * export const wrapper: RequestHandlerWrapper = handler => { + * return async (context, request, response) => { + * // do some logic + * ... + * }; + * } + * ``` + * @public + */ +export type RequestHandlerWrapper = < + P, + Q, + B, + Method extends RouteMethod = any, + ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory +>( + handler: RequestHandler +) => RequestHandler; diff --git a/src/core/server/http/router/validator/validator.ts b/src/core/server/http/router/validator/validator.ts index 97dd2bc894f813..6c766e69f0f37f 100644 --- a/src/core/server/http/router/validator/validator.ts +++ b/src/core/server/http/router/validator/validator.ts @@ -17,7 +17,7 @@ * under the License. */ -import { ValidationError, Type, schema, ObjectType } from '@kbn/config-schema'; +import { ValidationError, Type, schema, ObjectType, isConfigSchema } from '@kbn/config-schema'; import { Stream } from 'stream'; import { RouteValidationError } from './validator_error'; @@ -236,7 +236,7 @@ export class RouteValidator

{ data?: unknown, namespace?: string ): RouteValidationResultType { - if (validationRule instanceof Type) { + if (isConfigSchema(validationRule)) { return validationRule.validate(data, {}, namespace); } else if (typeof validationRule === 'function') { return this.validateFunction(validationRule, data, namespace); diff --git a/src/core/server/http_resources/http_resources_service.mock.ts b/src/core/server/http_resources/http_resources_service.mock.ts new file mode 100644 index 00000000000000..4536b0898cad9b --- /dev/null +++ b/src/core/server/http_resources/http_resources_service.mock.ts @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { httpServerMock } from '../http/http_server.mocks'; +import { HttpResources, HttpResourcesServiceToolkit } from './types'; + +const createHttpResourcesMock = (): jest.Mocked => ({ + register: jest.fn(), +}); + +function createInternalHttpResourcesSetup() { + return { + createRegistrar: createHttpResourcesMock, + }; +} + +function createHttpResourcesResponseFactory() { + const mocked: jest.Mocked = { + renderCoreApp: jest.fn(), + renderAnonymousCoreApp: jest.fn(), + renderHtml: jest.fn(), + renderJs: jest.fn(), + }; + + return { + ...httpServerMock.createResponseFactory(), + ...mocked, + }; +} + +export const httpResourcesMock = { + createRegistrar: createHttpResourcesMock, + createSetupContract: createInternalHttpResourcesSetup, + createResponseFactory: createHttpResourcesResponseFactory, +}; diff --git a/src/core/server/http_resources/http_resources_service.test.ts b/src/core/server/http_resources/http_resources_service.test.ts new file mode 100644 index 00000000000000..e6f129ba12d78d --- /dev/null +++ b/src/core/server/http_resources/http_resources_service.test.ts @@ -0,0 +1,258 @@ +/* + * 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, RouteConfig } from '../http'; + +import { coreMock } from '../mocks'; +import { mockCoreContext } from '../core_context.mock'; +import { httpServiceMock } from '../http/http_service.mock'; +import { httpServerMock } from '../http/http_server.mocks'; +import { renderingMock } from '../rendering/rendering_service.mock'; +import { HttpResourcesService, SetupDeps } from './http_resources_service'; +import { httpResourcesMock } from './http_resources_service.mock'; + +const coreContext = mockCoreContext.create(); + +describe('HttpResources service', () => { + let service: HttpResourcesService; + let setupDeps: SetupDeps; + let router: jest.Mocked; + const kibanaRequest = httpServerMock.createKibanaRequest(); + const context = { core: coreMock.createRequestHandlerContext() }; + describe('#createRegistrar', () => { + beforeEach(() => { + setupDeps = { + http: httpServiceMock.createSetupContract(), + rendering: renderingMock.createSetupContract(), + }; + service = new HttpResourcesService(coreContext); + router = httpServiceMock.createRouter(); + }); + + describe('register', () => { + describe('renderCoreApp', () => { + it('formats successful response', async () => { + const routeConfig: RouteConfig = { path: '/', validate: false }; + const { createRegistrar } = await service.setup(setupDeps); + const { register } = createRegistrar(router); + register(routeConfig, async (ctx, req, res) => { + return res.renderCoreApp(); + }); + const [[, routeHandler]] = router.get.mock.calls; + + const responseFactory = httpResourcesMock.createResponseFactory(); + await routeHandler(context, kibanaRequest, responseFactory); + expect(setupDeps.rendering.render).toHaveBeenCalledWith( + kibanaRequest, + context.core.uiSettings.client, + { + includeUserSettings: true, + } + ); + }); + + it('can attach headers, except the CSP header', async () => { + const routeConfig: RouteConfig = { path: '/', validate: false }; + const { createRegistrar } = await service.setup(setupDeps); + const { register } = createRegistrar(router); + register(routeConfig, async (ctx, req, res) => { + return res.renderCoreApp({ + headers: { + 'content-security-policy': "script-src 'unsafe-eval'", + 'x-kibana': '42', + }, + }); + }); + + const [[, routeHandler]] = router.get.mock.calls; + + const responseFactory = httpResourcesMock.createResponseFactory(); + await routeHandler(context, kibanaRequest, responseFactory); + + expect(responseFactory.ok).toHaveBeenCalledWith({ + body: '', + headers: { + 'x-kibana': '42', + 'content-security-policy': + "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'", + }, + }); + }); + }); + describe('renderAnonymousCoreApp', () => { + it('formats successful response', async () => { + const routeConfig: RouteConfig = { path: '/', validate: false }; + const { createRegistrar } = await service.setup(setupDeps); + const { register } = createRegistrar(router); + register(routeConfig, async (ctx, req, res) => { + return res.renderAnonymousCoreApp(); + }); + const [[, routeHandler]] = router.get.mock.calls; + + const responseFactory = httpResourcesMock.createResponseFactory(); + await routeHandler(context, kibanaRequest, responseFactory); + expect(setupDeps.rendering.render).toHaveBeenCalledWith( + kibanaRequest, + context.core.uiSettings.client, + { + includeUserSettings: false, + } + ); + }); + + it('can attach headers, except the CSP header', async () => { + const routeConfig: RouteConfig = { path: '/', validate: false }; + const { createRegistrar } = await service.setup(setupDeps); + const { register } = createRegistrar(router); + register(routeConfig, async (ctx, req, res) => { + return res.renderAnonymousCoreApp({ + headers: { + 'content-security-policy': "script-src 'unsafe-eval'", + 'x-kibana': '42', + }, + }); + }); + + const [[, routeHandler]] = router.get.mock.calls; + + const responseFactory = httpResourcesMock.createResponseFactory(); + await routeHandler(context, kibanaRequest, responseFactory); + + expect(responseFactory.ok).toHaveBeenCalledWith({ + body: '', + headers: { + 'x-kibana': '42', + 'content-security-policy': + "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'", + }, + }); + }); + }); + describe('renderHtml', () => { + it('formats successful response', async () => { + const htmlBody = ''; + const routeConfig: RouteConfig = { path: '/', validate: false }; + const { createRegistrar } = await service.setup(setupDeps); + const { register } = createRegistrar(router); + register(routeConfig, async (ctx, req, res) => { + return res.renderHtml({ body: htmlBody }); + }); + const [[, routeHandler]] = router.get.mock.calls; + + const responseFactory = httpResourcesMock.createResponseFactory(); + await routeHandler(context, kibanaRequest, responseFactory); + expect(responseFactory.ok).toHaveBeenCalledWith({ + body: htmlBody, + headers: { + 'content-type': 'text/html', + 'content-security-policy': + "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'", + }, + }); + }); + + it('can attach headers, except the CSP & "content-type" headers', async () => { + const htmlBody = ''; + const routeConfig: RouteConfig = { path: '/', validate: false }; + const { createRegistrar } = await service.setup(setupDeps); + const { register } = createRegistrar(router); + register(routeConfig, async (ctx, req, res) => { + return res.renderHtml({ + body: htmlBody, + headers: { + 'content-type': 'text/html5', + 'content-security-policy': "script-src 'unsafe-eval'", + 'x-kibana': '42', + }, + }); + }); + + const [[, routeHandler]] = router.get.mock.calls; + + const responseFactory = httpResourcesMock.createResponseFactory(); + await routeHandler(context, kibanaRequest, responseFactory); + + expect(responseFactory.ok).toHaveBeenCalledWith({ + body: htmlBody, + headers: { + 'content-type': 'text/html', + 'x-kibana': '42', + 'content-security-policy': + "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'", + }, + }); + }); + }); + describe('renderJs', () => { + it('formats successful response', async () => { + const jsBody = 'alert(1);'; + const routeConfig: RouteConfig = { path: '/', validate: false }; + const { createRegistrar } = await service.setup(setupDeps); + const { register } = createRegistrar(router); + register(routeConfig, async (ctx, req, res) => { + return res.renderJs({ body: jsBody }); + }); + const [[, routeHandler]] = router.get.mock.calls; + + const responseFactory = httpResourcesMock.createResponseFactory(); + await routeHandler(context, kibanaRequest, responseFactory); + expect(responseFactory.ok).toHaveBeenCalledWith({ + body: jsBody, + headers: { + 'content-type': 'text/javascript', + 'content-security-policy': + "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'", + }, + }); + }); + + it('can attach headers, except the CSP & "content-type" headers', async () => { + const jsBody = 'alert(1);'; + const routeConfig: RouteConfig = { path: '/', validate: false }; + const { createRegistrar } = await service.setup(setupDeps); + const { register } = createRegistrar(router); + register(routeConfig, async (ctx, req, res) => { + return res.renderJs({ + body: jsBody, + headers: { + 'content-type': 'text/html', + 'content-security-policy': "script-src 'unsafe-eval'", + 'x-kibana': '42', + }, + }); + }); + + const [[, routeHandler]] = router.get.mock.calls; + + const responseFactory = httpResourcesMock.createResponseFactory(); + await routeHandler(context, kibanaRequest, responseFactory); + + expect(responseFactory.ok).toHaveBeenCalledWith({ + body: jsBody, + headers: { + 'content-type': 'text/javascript', + 'x-kibana': '42', + 'content-security-policy': + "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'", + }, + }); + }); + }); + }); + }); +}); diff --git a/src/core/server/http_resources/http_resources_service.ts b/src/core/server/http_resources/http_resources_service.ts new file mode 100644 index 00000000000000..bc79ad68f40998 --- /dev/null +++ b/src/core/server/http_resources/http_resources_service.ts @@ -0,0 +1,130 @@ +/* + * 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 { RequestHandlerContext } from 'src/core/server'; + +import { CoreContext } from '../core_context'; +import { + IRouter, + RouteConfig, + InternalHttpServiceSetup, + KibanaRequest, + KibanaResponseFactory, +} from '../http'; + +import { Logger } from '../logging'; +import { InternalRenderingServiceSetup } from '../rendering'; +import { CoreService } from '../../types'; + +import { + InternalHttpResourcesSetup, + HttpResources, + HttpResourcesResponseOptions, + HttpResourcesRenderOptions, + HttpResourcesRequestHandler, + HttpResourcesServiceToolkit, +} from './types'; + +export interface SetupDeps { + http: InternalHttpServiceSetup; + rendering: InternalRenderingServiceSetup; +} + +export class HttpResourcesService implements CoreService { + private readonly logger: Logger; + constructor(core: CoreContext) { + this.logger = core.logger.get('http-resources'); + } + + setup(deps: SetupDeps) { + this.logger.debug('setting up HttpResourcesService'); + return { + createRegistrar: this.createRegistrar.bind(this, deps), + }; + } + + start() {} + stop() {} + + private createRegistrar(deps: SetupDeps, router: IRouter): HttpResources { + return { + register: ( + route: RouteConfig, + handler: HttpResourcesRequestHandler + ) => { + return router.get(route, (context, request, response) => { + return handler(context, request, { + ...response, + ...this.createResponseToolkit(deps, context, request, response), + }); + }); + }, + }; + } + + private createResponseToolkit( + deps: SetupDeps, + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): HttpResourcesServiceToolkit { + const cspHeader = deps.http.csp.header; + return { + async renderCoreApp(options: HttpResourcesRenderOptions = {}) { + const body = await deps.rendering.render(request, context.core.uiSettings.client, { + includeUserSettings: true, + }); + + return response.ok({ + body, + headers: { ...options.headers, 'content-security-policy': cspHeader }, + }); + }, + async renderAnonymousCoreApp(options: HttpResourcesRenderOptions = {}) { + const body = await deps.rendering.render(request, context.core.uiSettings.client, { + includeUserSettings: false, + }); + + return response.ok({ + body, + headers: { ...options.headers, 'content-security-policy': cspHeader }, + }); + }, + renderHtml(options: HttpResourcesResponseOptions) { + return response.ok({ + body: options.body, + headers: { + ...options.headers, + 'content-type': 'text/html', + 'content-security-policy': cspHeader, + }, + }); + }, + renderJs(options: HttpResourcesResponseOptions) { + return response.ok({ + body: options.body, + headers: { + ...options.headers, + 'content-type': 'text/javascript', + 'content-security-policy': cspHeader, + }, + }); + }, + }; + } +} diff --git a/src/core/server/http_resources/index.ts b/src/core/server/http_resources/index.ts new file mode 100644 index 00000000000000..b373c6a9efa89b --- /dev/null +++ b/src/core/server/http_resources/index.ts @@ -0,0 +1,29 @@ +/* + * 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 { HttpResourcesService } from './http_resources_service'; + +export { + HttpResourcesRenderOptions, + HttpResourcesResponseOptions, + HttpResourcesServiceToolkit, + HttpResourcesRequestHandler, + HttpResources, + InternalHttpResourcesSetup, +} from './types'; diff --git a/src/core/server/http_resources/integration_tests/http_resources_service.test.ts b/src/core/server/http_resources/integration_tests/http_resources_service.test.ts new file mode 100644 index 00000000000000..0a5daa02e17e9b --- /dev/null +++ b/src/core/server/http_resources/integration_tests/http_resources_service.test.ts @@ -0,0 +1,203 @@ +/* + * 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 * as kbnTestServer from '../../../../test_utils/kbn_server'; + +describe('http resources service', () => { + describe('register', () => { + let root: ReturnType; + const defaultCspRules = "script-src 'self'"; + beforeEach(async () => { + root = kbnTestServer.createRoot({ + csp: { + rules: [defaultCspRules], + }, + }); + }, 30000); + + afterEach(async () => { + await root.shutdown(); + }); + + describe('renderAnonymousCoreApp', () => { + it('renders core application', async () => { + const { http, httpResources } = await root.setup(); + + const router = http.createRouter(''); + const resources = httpResources.createRegistrar(router); + resources.register({ path: '/render-core', validate: false }, (context, req, res) => + res.renderAnonymousCoreApp() + ); + + await root.start(); + const response = await kbnTestServer.request.get(root, '/render-core').expect(200); + + expect(response.text.length).toBeGreaterThan(0); + }); + + it('attaches CSP header', async () => { + const { http, httpResources } = await root.setup(); + + const router = http.createRouter(''); + const resources = httpResources.createRegistrar(router); + resources.register({ path: '/render-core', validate: false }, (context, req, res) => + res.renderAnonymousCoreApp() + ); + + await root.start(); + const response = await kbnTestServer.request.get(root, '/render-core').expect(200); + + expect(response.header['content-security-policy']).toBe(defaultCspRules); + }); + + it('can attach headers, except the CSP header', async () => { + const { http, httpResources } = await root.setup(); + + const router = http.createRouter(''); + const resources = httpResources.createRegistrar(router); + resources.register({ path: '/render-core', validate: false }, (context, req, res) => + res.renderAnonymousCoreApp({ + headers: { + 'content-security-policy': "script-src 'unsafe-eval'", + 'x-kibana': '42', + }, + }) + ); + + await root.start(); + const response = await kbnTestServer.request.get(root, '/render-core').expect(200); + + expect(response.header['content-security-policy']).toBe(defaultCspRules); + expect(response.header['x-kibana']).toBe('42'); + }); + }); + + describe('custom renders', () => { + it('renders html', async () => { + const { http, httpResources } = await root.setup(); + + const router = http.createRouter(''); + const resources = httpResources.createRegistrar(router); + const htmlBody = ` + + + +

HTML body

+ + + `; + resources.register({ path: '/render-html', validate: false }, (context, req, res) => + res.renderHtml({ body: htmlBody }) + ); + + await root.start(); + const response = await kbnTestServer.request.get(root, '/render-html').expect(200); + + expect(response.text).toBe(htmlBody); + expect(response.header['content-type']).toBe('text/html; charset=utf-8'); + }); + + it('renders javascript', async () => { + const { http, httpResources } = await root.setup(); + + const router = http.createRouter(''); + const resources = httpResources.createRegistrar(router); + const jsBody = 'window.alert("from js body");'; + resources.register({ path: '/render-js', validate: false }, (context, req, res) => + res.renderJs({ body: jsBody }) + ); + + await root.start(); + const response = await kbnTestServer.request.get(root, '/render-js').expect(200); + + expect(response.text).toBe(jsBody); + expect(response.header['content-type']).toBe('text/javascript; charset=utf-8'); + }); + + it('attaches CSP header', async () => { + const { http, httpResources } = await root.setup(); + + const router = http.createRouter(''); + const resources = httpResources.createRegistrar(router); + const htmlBody = ` + + + +

HTML body

+ + + `; + resources.register({ path: '/render-html', validate: false }, (context, req, res) => + res.renderHtml({ body: htmlBody }) + ); + + await root.start(); + const response = await kbnTestServer.request.get(root, '/render-html').expect(200); + + expect(response.header['content-security-policy']).toBe(defaultCspRules); + }); + + it('can attach headers, except the CSP & "content-type" headers', async () => { + const { http, httpResources } = await root.setup(); + + const router = http.createRouter(''); + const resources = httpResources.createRegistrar(router); + resources.register({ path: '/render-core', validate: false }, (context, req, res) => + res.renderHtml({ + body: '

Hi

', + headers: { + 'content-security-policy': "script-src 'unsafe-eval'", + 'content-type': 'text/html', + 'x-kibana': '42', + }, + }) + ); + + await root.start(); + const response = await kbnTestServer.request.get(root, '/render-core').expect(200); + + expect(response.header['content-security-policy']).toBe(defaultCspRules); + expect(response.header['x-kibana']).toBe('42'); + }); + + it('can adjust route config', async () => { + const { http, httpResources } = await root.setup(); + + const router = http.createRouter(''); + const resources = httpResources.createRegistrar(router); + const validate = { + params: schema.object({ + id: schema.string(), + }), + }; + + resources.register({ path: '/render-js-with-param/{id}', validate }, (context, req, res) => + res.renderJs({ body: `window.alert(${req.params.id});` }) + ); + + await root.start(); + const response = await kbnTestServer.request + .get(root, '/render-js-with-param/42') + .expect(200); + + expect(response.text).toBe('window.alert(42);'); + }); + }); + }); +}); diff --git a/src/core/server/http_resources/types.ts b/src/core/server/http_resources/types.ts new file mode 100644 index 00000000000000..d761e2def10231 --- /dev/null +++ b/src/core/server/http_resources/types.ts @@ -0,0 +1,116 @@ +/* + * 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, + RouteConfig, + IKibanaResponse, + ResponseHeaders, + HttpResponseOptions, + KibanaResponseFactory, + RequestHandler, +} from '../http'; + +/** + * Allows to configure HTTP response parameters + * @public + */ +export interface HttpResourcesRenderOptions { + /** + * HTTP Headers with additional information about response. + * @remarks + * All HTML pages are already pre-configured with `content-security-policy` header that cannot be overridden. + * */ + headers?: ResponseHeaders; +} + +/** + * HTTP Resources response parameters + * @public + */ +export type HttpResourcesResponseOptions = HttpResponseOptions; + +/** + * Extended set of {@link KibanaResponseFactory} helpers used to respond with HTML or JS resource. + * @public + */ +export interface HttpResourcesServiceToolkit { + /** To respond with HTML page bootstrapping Kibana application. */ + renderCoreApp: (options?: HttpResourcesRenderOptions) => Promise; + /** To respond with HTML page bootstrapping Kibana application without retrieving user-specific information. */ + renderAnonymousCoreApp: (options?: HttpResourcesRenderOptions) => Promise; + /** To respond with a custom HTML page. */ + renderHtml: (options: HttpResourcesResponseOptions) => IKibanaResponse; + /** To respond with a custom JS script file. */ + renderJs: (options: HttpResourcesResponseOptions) => IKibanaResponse; +} + +/** + * Extended version of {@link RequestHandler} having access to {@link HttpResourcesServiceToolkit} + * to respond with HTML or JS resources. + * @param context {@link RequestHandlerContext} - the core context exposed for this request. + * @param request {@link KibanaRequest} - object containing information about requested resource, + * such as path, method, headers, parameters, query, body, etc. + * @param response {@link KibanaResponseFactory} {@libk HttpResourcesServiceToolkit} - a set of helper functions used to respond to a request. + * + * @example + * ```typescript + * httpResources.register({ + * path: '/login', + * validate: { + * params: schema.object({ id: schema.string() }), + * }, + * }, + * async (context, request, response) => { + * //.. + * return response.renderCoreApp(); + * }); + * @public + */ +export type HttpResourcesRequestHandler

= RequestHandler< + P, + Q, + B, + 'get', + KibanaResponseFactory & HttpResourcesServiceToolkit +>; + +/** + * Allows to configure HTTP response parameters + * @internal + */ +export interface InternalHttpResourcesSetup { + createRegistrar(router: IRouter): HttpResources; +} + +/** + * HttpResources service is responsible for serving static & dynamic assets for Kibana application via HTTP. + * Provides API allowing plug-ins to respond with: + * - a pre-configured HTML page bootstrapping Kibana client app + * - custom HTML page + * - custom JS script file. + * @public + */ +export interface HttpResources { + /** To register a route handler executing passed function to form response. */ + register: ( + route: RouteConfig, + handler: HttpResourcesRequestHandler + ) => void; +} diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 039988fa089680..ef57fae159d7ec 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -47,7 +47,8 @@ import { } from './elasticsearch'; import { HttpServiceSetup } from './http'; -import { IScopedRenderingClient } from './rendering'; +import { HttpResources } from './http_resources'; + import { PluginsServiceSetup, PluginsServiceStart, PluginOpaqueId } from './plugins'; import { ContextSetup } from './context'; import { IUiSettingsClient, UiSettingsServiceSetup, UiSettingsServiceStart } from './ui_settings'; @@ -146,6 +147,7 @@ export { OnPreResponseInfo, RedirectResponseOptions, RequestHandler, + RequestHandlerWrapper, RequestHandlerContextContainer, RequestHandlerContextProvider, ResponseError, @@ -175,7 +177,15 @@ export { DestructiveRouteMethod, SafeRouteMethod, } from './http'; -export { RenderingServiceSetup, IRenderOptions } from './rendering'; + +export { + HttpResourcesRenderOptions, + HttpResourcesResponseOptions, + HttpResourcesServiceToolkit, + HttpResourcesRequestHandler, +} from './http_resources'; + +export { IRenderOptions } from './rendering'; export { Logger, LoggerFactory, LogMeta, LogRecord, LogLevel } from './logging'; export { @@ -313,8 +323,6 @@ export { * Plugin specific context passed to a route handler. * * Provides the following clients and services: - * - {@link IScopedRenderingClient | rendering} - Rendering client - * which uses the data of the incoming request * - {@link SavedObjectsClient | savedObjects.client} - Saved Objects client * which uses the credentials of the incoming request * - {@link ISavedObjectTypeRegistry | savedObjects.typeRegistry} - Type registry containing @@ -330,7 +338,6 @@ export { */ export interface RequestHandlerContext { core: { - rendering: IScopedRenderingClient; savedObjects: { client: SavedObjectsClientContract; typeRegistry: ISavedObjectTypeRegistry; @@ -362,7 +369,10 @@ export interface CoreSetup ({ +jest.doMock('./plugins/find_legacy_plugin_specs', () => ({ findLegacyPluginSpecs: findLegacyPluginSpecsMock, })); + +export const logLegacyThirdPartyPluginDeprecationWarningMock = jest.fn(); +jest.doMock('./plugins/log_legacy_plugins_warning', () => ({ + logLegacyThirdPartyPluginDeprecationWarning: logLegacyThirdPartyPluginDeprecationWarningMock, +})); diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index 0cf2ebe55ea10d..a75f7dda302c27 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -22,7 +22,10 @@ jest.mock('../../../cli/cluster/cluster_manager'); jest.mock('./config/legacy_deprecation_adapters', () => ({ convertLegacyDeprecationProvider: (provider: any) => Promise.resolve(provider), })); -import { findLegacyPluginSpecsMock } from './legacy_service.test.mocks'; +import { + findLegacyPluginSpecsMock, + logLegacyThirdPartyPluginDeprecationWarningMock, +} from './legacy_service.test.mocks'; import { BehaviorSubject, throwError } from 'rxjs'; @@ -41,6 +44,7 @@ import { httpServiceMock } from '../http/http_service.mock'; import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock'; import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock'; import { capabilitiesServiceMock } from '../capabilities/capabilities_service.mock'; +import { httpResourcesMock } from '../http_resources/http_resources_service.mock'; import { setupMock as renderingServiceMock } from '../rendering/__mocks__/rendering_service'; import { uuidServiceMock } from '../uuid/uuid_service.mock'; import { metricsServiceMock } from '../metrics/metrics_service.mock'; @@ -86,23 +90,11 @@ beforeEach(() => { getAuthHeaders: () => undefined, } as any, }, + httpResources: httpResourcesMock.createSetupContract(), savedObjects: savedObjectsServiceMock.createInternalSetupContract(), plugins: { initialized: true, contracts: new Map([['plugin-id', 'plugin-value']]), - uiPlugins: { - public: new Map([['plugin-id', {} as DiscoveredPlugin]]), - internal: new Map([ - [ - 'plugin-id', - { - publicTargetDir: 'path/to/target/public', - publicAssetsDir: '/plugins/name/assets/', - }, - ], - ]), - browserConfigs: new Map(), - }, }, rendering: renderingServiceMock, metrics: metricsServiceMock.createInternalSetupContract(), @@ -110,6 +102,19 @@ beforeEach(() => { status: statusServiceMock.createInternalSetupContract(), }, plugins: { 'plugin-id': 'plugin-value' }, + uiPlugins: { + public: new Map([['plugin-id', {} as DiscoveredPlugin]]), + internal: new Map([ + [ + 'plugin-id', + { + publicTargetDir: 'path/to/target/public', + publicAssetsDir: '/plugins/name/assets/', + }, + ], + ]), + browserConfigs: new Map(), + }, }; startDeps = { @@ -474,6 +479,38 @@ describe('#discoverPlugins()', () => { expect(configService.addDeprecationProvider).toHaveBeenCalledWith('', 'providerA'); expect(configService.addDeprecationProvider).toHaveBeenCalledWith('', 'providerB'); }); + + it(`logs deprecations for legacy third party plugins`, async () => { + const pluginSpecs = [ + { getId: () => 'pluginA', getDeprecationsProvider: () => undefined }, + { getId: () => 'pluginB', getDeprecationsProvider: () => undefined }, + ]; + findLegacyPluginSpecsMock.mockImplementation( + settings => + Promise.resolve({ + pluginSpecs, + pluginExtendedConfig: settings, + disabledPluginSpecs: [], + uiExports: {}, + navLinks: [], + }) as any + ); + + const legacyService = new LegacyService({ + coreId, + env, + logger, + configService: configService as any, + }); + + await legacyService.discoverPlugins(); + + expect(logLegacyThirdPartyPluginDeprecationWarningMock).toHaveBeenCalledTimes(1); + expect(logLegacyThirdPartyPluginDeprecationWarningMock).toHaveBeenCalledWith({ + specs: pluginSpecs, + log: expect.any(Object), + }); + }); }); test('Sets the server.uuid property on the legacy configuration', async () => { diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index f77230301ce023..b95362e1ea26e0 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -28,7 +28,7 @@ import { DevConfig, DevConfigType, config as devConfig } from '../dev'; import { BasePathProxyServer, HttpConfig, HttpConfigType, config as httpConfig } from '../http'; import { Logger } from '../logging'; import { PathConfigType } from '../path'; -import { findLegacyPluginSpecs } from './plugins'; +import { findLegacyPluginSpecs, logLegacyThirdPartyPluginDeprecationWarning } from './plugins'; import { convertLegacyDeprecationProvider } from './config'; import { ILegacyInternals, @@ -133,6 +133,11 @@ export class LegacyService implements CoreService { this.coreContext.env.packageInfo ); + logLegacyThirdPartyPluginDeprecationWarning({ + specs: pluginSpecs, + log: this.log, + }); + this.legacyPlugins = { pluginSpecs, disabledPluginSpecs, @@ -269,6 +274,7 @@ export class LegacyService implements CoreService { uiSettings: { asScopedToClient: startDeps.core.uiSettings.asScopedToClient }, }; + const router = setupDeps.core.http.createRouter('', this.legacyId); const coreSetup: CoreSetup = { capabilities: setupDeps.core.capabilities, context: setupDeps.core.context, @@ -283,7 +289,8 @@ export class LegacyService implements CoreService { null, this.legacyId ), - createRouter: () => setupDeps.core.http.createRouter('', this.legacyId), + createRouter: () => router, + resources: setupDeps.core.httpResources.createRegistrar(router), registerOnPreAuth: setupDeps.core.http.registerOnPreAuth, registerAuth: setupDeps.core.http.registerAuth, registerOnPostAuth: setupDeps.core.http.registerOnPostAuth, @@ -342,7 +349,7 @@ export class LegacyService implements CoreService { }, hapiServer: setupDeps.core.http.server, kibanaMigrator: startDeps.core.savedObjects.migrator, - uiPlugins: setupDeps.core.plugins.uiPlugins, + uiPlugins: setupDeps.uiPlugins, elasticsearch: setupDeps.core.elasticsearch, rendering: setupDeps.core.rendering, uiSettings: setupDeps.core.uiSettings, diff --git a/src/core/server/legacy/plugins/index.ts b/src/core/server/legacy/plugins/index.ts index a6d55e1da78390..7ec5dbc1983ab9 100644 --- a/src/core/server/legacy/plugins/index.ts +++ b/src/core/server/legacy/plugins/index.ts @@ -18,3 +18,4 @@ */ export { findLegacyPluginSpecs } from './find_legacy_plugin_specs'; +export { logLegacyThirdPartyPluginDeprecationWarning } from './log_legacy_plugins_warning'; diff --git a/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts b/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts new file mode 100644 index 00000000000000..1790b096a71aea --- /dev/null +++ b/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.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 { loggerMock } from '../../logging/logger.mock'; +import { logLegacyThirdPartyPluginDeprecationWarning } from './log_legacy_plugins_warning'; +import { LegacyPluginSpec } from '../types'; + +const createPluginSpec = ({ id, path }: { id: string; path: string }): LegacyPluginSpec => { + return { + getId: () => id, + getExpectedKibanaVersion: () => 'kibana', + getConfigPrefix: () => 'plugin.config', + getDeprecationsProvider: () => undefined, + getPack: () => ({ + getPath: () => path, + }), + }; +}; + +describe('logLegacyThirdPartyPluginDeprecationWarning', () => { + let log: ReturnType; + + beforeEach(() => { + log = loggerMock.create(); + }); + + it('logs warning for third party plugins', () => { + logLegacyThirdPartyPluginDeprecationWarning({ + specs: [createPluginSpec({ id: 'plugin', path: '/some-external-path' })], + log, + }); + expect(log.warn).toHaveBeenCalledTimes(1); + expect(log.warn.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Some installed third party plugin(s) [plugin] are using the legacy plugin format and will no longer work in a future Kibana release. Please refer to https://www.elastic.co/guide/en/kibana/master/breaking-changes-8.0.html for a list of breaking changes and https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md for documentation on how to migrate legacy plugins.", + ] + `); + }); + + it('lists all the deprecated plugins and only log once', () => { + logLegacyThirdPartyPluginDeprecationWarning({ + specs: [ + createPluginSpec({ id: 'pluginA', path: '/abs/path/to/pluginA' }), + createPluginSpec({ id: 'pluginB', path: '/abs/path/to/pluginB' }), + createPluginSpec({ id: 'pluginC', path: '/abs/path/to/pluginC' }), + ], + log, + }); + expect(log.warn).toHaveBeenCalledTimes(1); + expect(log.warn.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Some installed third party plugin(s) [pluginA, pluginB, pluginC] are using the legacy plugin format and will no longer work in a future Kibana release. Please refer to https://www.elastic.co/guide/en/kibana/master/breaking-changes-8.0.html for a list of breaking changes and https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md for documentation on how to migrate legacy plugins.", + ] + `); + }); + + it('does not log warning for internal legacy plugins', () => { + logLegacyThirdPartyPluginDeprecationWarning({ + specs: [ + createPluginSpec({ + id: 'plugin', + path: '/absolute/path/to/kibana/src/legacy/core_plugins', + }), + createPluginSpec({ + id: 'plugin', + path: '/absolute/path/to/kibana/x-pack', + }), + ], + log, + }); + + expect(log.warn).not.toHaveBeenCalled(); + }); +}); diff --git a/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts b/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts new file mode 100644 index 00000000000000..f9c3dcbf554cb8 --- /dev/null +++ b/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Logger } from '../../logging'; +import { LegacyPluginSpec } from '../types'; + +const internalPaths = ['/src/legacy/core_plugins', '/x-pack']; + +const breakingChangesUrl = + 'https://www.elastic.co/guide/en/kibana/master/breaking-changes-8.0.html'; +const migrationGuideUrl = 'https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md'; + +export const logLegacyThirdPartyPluginDeprecationWarning = ({ + specs, + log, +}: { + specs: LegacyPluginSpec[]; + log: Logger; +}) => { + const thirdPartySpecs = specs.filter(isThirdPartyPluginSpec); + if (thirdPartySpecs.length > 0) { + const pluginIds = thirdPartySpecs.map(spec => spec.getId()); + log.warn( + `Some installed third party plugin(s) [${pluginIds.join( + ', ' + )}] are using the legacy plugin format and will no longer work in a future Kibana release. ` + + `Please refer to ${breakingChangesUrl} for a list of breaking changes ` + + `and ${migrationGuideUrl} for documentation on how to migrate legacy plugins.` + ); + } +}; + +const isThirdPartyPluginSpec = (spec: LegacyPluginSpec): boolean => { + const pluginPath = spec.getPack().getPath(); + return !internalPaths.some(internalPath => pluginPath.indexOf(internalPath) > -1); +}; diff --git a/src/core/server/legacy/types.ts b/src/core/server/legacy/types.ts index 0c1a7730f92a77..2567ca790e04f7 100644 --- a/src/core/server/legacy/types.ts +++ b/src/core/server/legacy/types.ts @@ -22,8 +22,8 @@ import { Server } from 'hapi'; import { ChromeNavLink } from '../../public'; import { KibanaRequest, LegacyRequest } from '../http'; import { InternalCoreSetup, InternalCoreStart } from '../internal_types'; -import { PluginsServiceSetup, PluginsServiceStart } from '../plugins'; -import { RenderingServiceSetup } from '../rendering'; +import { PluginsServiceSetup, PluginsServiceStart, UiPlugins } from '../plugins'; +import { InternalRenderingServiceSetup } from '../rendering'; import { SavedObjectsLegacyUiExports } from '../types'; /** @@ -34,7 +34,7 @@ export type LegacyVars = Record; type LegacyCoreSetup = InternalCoreSetup & { plugins: PluginsServiceSetup; - rendering: RenderingServiceSetup; + rendering: InternalRenderingServiceSetup; }; type LegacyCoreStart = InternalCoreStart & { plugins: PluginsServiceStart }; @@ -98,6 +98,7 @@ export interface LegacyPluginSpec { getExpectedKibanaVersion: () => string; getConfigPrefix: () => string; getDeprecationsProvider: () => LegacyConfigDeprecationProvider | undefined; + getPack: () => LegacyPluginPack; } /** @@ -173,6 +174,7 @@ export type LegacyUiExports = SavedObjectsLegacyUiExports & { export interface LegacyServiceSetupDeps { core: LegacyCoreSetup; plugins: Record; + uiPlugins: UiPlugins; } /** diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index faf73044cac4d1..3b9a39db722789 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -23,10 +23,12 @@ import { CspConfig } from './csp'; import { loggingServiceMock } from './logging/logging_service.mock'; import { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock'; import { httpServiceMock } from './http/http_service.mock'; +import { httpResourcesMock } from './http_resources/http_resources_service.mock'; import { contextServiceMock } from './context/context_service.mock'; import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; import { savedObjectsClientMock } from './saved_objects/service/saved_objects_client.mock'; import { typeRegistryMock as savedObjectsTypeRegistryMock } from './saved_objects/saved_objects_type_registry.mock'; +import { renderingMock } from './rendering/rendering_service.mock'; import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; import { SharedGlobalConfig } from './plugins'; import { InternalCoreSetup, InternalCoreStart } from './internal_types'; @@ -36,6 +38,7 @@ import { uuidServiceMock } from './uuid/uuid_service.mock'; import { statusServiceMock } from './status/status_service.mock'; export { httpServerMock } from './http/http_server.mocks'; +export { httpResourcesMock } from './http_resources/http_resources_service.mock'; export { sessionStorageMock } from './http/cookie_session_storage.mocks'; export { configServiceMock } from './config/config_service.mock'; export { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock'; @@ -45,6 +48,7 @@ export { savedObjectsRepositoryMock } from './saved_objects/service/lib/reposito export { typeRegistryMock as savedObjectsTypeRegistryMock } from './saved_objects/saved_objects_type_registry.mock'; export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; export { metricsServiceMock } from './metrics/metrics_service.mock'; +export { renderingMock } from './rendering/rendering_service.mock'; export function pluginInitializerContextConfigMock(config: T) { const globalConfig: SharedGlobalConfig = { @@ -120,6 +124,7 @@ function createCoreSetupMock({ get: httpService.auth.get, isAuthenticated: httpService.auth.isAuthenticated, }, + resources: httpResourcesMock.createRegistrar(), getServerInfo: httpService.getServerInfo, }; httpMock.createRouter.mockImplementation(() => httpService.createRouter('')); @@ -167,6 +172,8 @@ function createInternalCoreSetupMock() { savedObjects: savedObjectsServiceMock.createInternalSetupContract(), status: statusServiceMock.createInternalSetupContract(), uuid: uuidServiceMock.createSetupContract(), + httpResources: httpResourcesMock.createSetupContract(), + rendering: renderingMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), }; return setupDeps; @@ -184,9 +191,6 @@ function createInternalCoreStartMock() { function createCoreRequestHandlerContextMock() { return { - rendering: { - render: jest.fn(), - }, savedObjects: { client: savedObjectsClientMock.create(), typeRegistry: savedObjectsTypeRegistryMock.create(), diff --git a/src/core/server/plugins/index.ts b/src/core/server/plugins/index.ts index c7ef213c8f1875..e480de750bb1a5 100644 --- a/src/core/server/plugins/index.ts +++ b/src/core/server/plugins/index.ts @@ -17,7 +17,12 @@ * under the License. */ -export { PluginsService, PluginsServiceSetup, PluginsServiceStart } from './plugins_service'; +export { + PluginsService, + PluginsServiceSetup, + PluginsServiceStart, + UiPlugins, +} from './plugins_service'; export { config } from './plugins_config'; /** @internal */ export { isNewPlatformPlugin } from './discovery'; diff --git a/src/core/server/plugins/plugin.ts b/src/core/server/plugins/plugin.ts index 7c67ab7a48df1d..d7cfaa14d23434 100644 --- a/src/core/server/plugins/plugin.ts +++ b/src/core/server/plugins/plugin.ts @@ -21,7 +21,7 @@ import { join } from 'path'; import typeDetect from 'type-detect'; import { Subject } from 'rxjs'; import { first } from 'rxjs/operators'; -import { Type } from '@kbn/config-schema'; +import { isConfigSchema } from '@kbn/config-schema'; import { Logger } from '../logging'; import { @@ -150,7 +150,7 @@ export class PluginWrapper< } const configDescriptor = pluginDefinition.config; - if (!(configDescriptor.schema instanceof Type)) { + if (!isConfigSchema(configDescriptor.schema)) { throw new Error('Configuration schema expected to be an instance of Type'); } return configDescriptor; diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 61d97aea97459c..ab18a9cbbc0626 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -136,6 +136,8 @@ export function createPluginSetupContext( deps: PluginsServiceSetupDeps, plugin: PluginWrapper ): CoreSetup { + const router = deps.http.createRouter('', plugin.opaqueId); + return { capabilities: { registerProvider: deps.capabilities.registerProvider, @@ -155,7 +157,8 @@ export function createPluginSetupContext( null, plugin.opaqueId ), - createRouter: () => deps.http.createRouter('', plugin.opaqueId), + createRouter: () => router, + resources: deps.httpResources.createRegistrar(router), registerOnPreAuth: deps.http.registerOnPreAuth, registerAuth: deps.http.registerAuth, registerOnPostAuth: deps.http.registerOnPostAuth, diff --git a/src/core/server/plugins/plugins_service.mock.ts b/src/core/server/plugins/plugins_service.mock.ts index 29e5b83b2e4c71..a40566767ddaef 100644 --- a/src/core/server/plugins/plugins_service.mock.ts +++ b/src/core/server/plugins/plugins_service.mock.ts @@ -23,14 +23,10 @@ type PluginsServiceMock = jest.Mocked>; const createSetupContractMock = (): PluginsServiceSetup => ({ contracts: new Map(), - uiPlugins: { - browserConfigs: new Map(), - internal: new Map(), - public: new Map(), - }, initialized: true, }); const createStartContractMock = () => ({ contracts: new Map() }); + const createServiceMock = (): PluginsServiceMock => ({ discover: jest.fn(), setup: jest.fn().mockResolvedValue(createSetupContractMock()), @@ -38,8 +34,17 @@ const createServiceMock = (): PluginsServiceMock => ({ stop: jest.fn(), }); +function createUiPlugins() { + return { + browserConfigs: new Map(), + internal: new Map(), + public: new Map(), + }; +} + export const pluginServiceMock = { create: createServiceMock, createSetupContract: createSetupContractMock, createStartContract: createStartContractMock, + createUiPlugins, }; diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 14147ab9f2a8dd..38fda12bd290f6 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -120,6 +120,7 @@ describe('PluginsService', () => { pluginsService = new PluginsService({ coreId, env, logger, configService }); [mockPluginSystem] = MockPluginsSystem.mock.instances as any; + mockPluginSystem.uiPlugins.mockReturnValue(new Map()); }); afterEach(() => { @@ -202,7 +203,6 @@ describe('PluginsService', () => { .mockImplementation(path => Promise.resolve(!path.includes('disabled'))); mockPluginSystem.setupPlugins.mockResolvedValue(new Map()); - mockPluginSystem.uiPlugins.mockReturnValue(new Map()); mockDiscover.mockReturnValue({ error$: from([]), @@ -234,8 +234,6 @@ describe('PluginsService', () => { const setup = await pluginsService.setup(setupDeps); expect(setup.contracts).toBeInstanceOf(Map); - expect(setup.uiPlugins.public).toBeInstanceOf(Map); - expect(setup.uiPlugins.internal).toBeInstanceOf(Map); expect(mockPluginSystem.addPlugin).not.toHaveBeenCalled(); expect(mockPluginSystem.setupPlugins).toHaveBeenCalledTimes(1); expect(mockPluginSystem.setupPlugins).toHaveBeenCalledWith(setupDeps); @@ -273,7 +271,8 @@ describe('PluginsService', () => { plugin$: from([firstPlugin, secondPlugin]), }); - await expect(pluginsService.discover()).resolves.toBeUndefined(); + const { pluginTree } = await pluginsService.discover(); + expect(pluginTree).toBeUndefined(); expect(mockDiscover).toHaveBeenCalledTimes(1); expect(mockPluginSystem.addPlugin).toHaveBeenCalledTimes(2); @@ -308,7 +307,8 @@ describe('PluginsService', () => { plugin$: from([firstPlugin, secondPlugin, thirdPlugin, lastPlugin, missingDepsPlugin]), }); - await expect(pluginsService.discover()).resolves.toBeUndefined(); + const { pluginTree } = await pluginsService.discover(); + expect(pluginTree).toBeUndefined(); expect(mockDiscover).toHaveBeenCalledTimes(1); expect(mockPluginSystem.addPlugin).toHaveBeenCalledTimes(4); @@ -466,12 +466,8 @@ describe('PluginsService', () => { }); mockPluginSystem.uiPlugins.mockReturnValue(new Map([pluginToDiscoveredEntry(plugin)])); - await pluginsService.discover(); - const { - uiPlugins: { browserConfigs }, - } = await pluginsService.setup(setupDeps); - - const uiConfig$ = browserConfigs.get('plugin-with-expose'); + const { uiPlugins } = await pluginsService.discover(); + const uiConfig$ = uiPlugins.browserConfigs.get('plugin-with-expose'); expect(uiConfig$).toBeDefined(); const uiConfig = await uiConfig$!.pipe(take(1)).toPromise(); @@ -506,12 +502,8 @@ describe('PluginsService', () => { }); mockPluginSystem.uiPlugins.mockReturnValue(new Map([pluginToDiscoveredEntry(plugin)])); - await pluginsService.discover(); - const { - uiPlugins: { browserConfigs }, - } = await pluginsService.setup(setupDeps); - - expect([...browserConfigs.entries()]).toHaveLength(0); + const { uiPlugins } = await pluginsService.discover(); + expect([...uiPlugins.browserConfigs.entries()]).toHaveLength(0); }); }); @@ -539,8 +531,7 @@ describe('PluginsService', () => { describe('uiPlugins.internal', () => { it('includes disabled plugins', async () => { config$.next({ plugins: { initialize: true }, plugin1: { enabled: false } }); - await pluginsService.discover(); - const { uiPlugins } = await pluginsService.setup(setupDeps); + const { uiPlugins } = await pluginsService.discover(); expect(uiPlugins.internal).toMatchInlineSnapshot(` Map { "plugin-1" => Object { diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index a0ecee47c675f2..d7a348affe94ff 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -39,23 +39,25 @@ export interface PluginsServiceSetup { initialized: boolean; /** Setup contracts returned by plugins. */ contracts: Map; - uiPlugins: { - /** - * Paths to all discovered ui plugin entrypoints on the filesystem, even if - * disabled. - */ - internal: Map; - - /** - * Information needed by client-side to load plugins and wire dependencies. - */ - public: Map; - - /** - * Configuration for plugins to be exposed to the client-side. - */ - browserConfigs: Map>; - }; +} + +/** @internal */ +export interface UiPlugins { + /** + * Paths to all discovered ui plugin entrypoints on the filesystem, even if + * disabled. + */ + internal: Map; + + /** + * Information needed by client-side to load plugins and wire dependencies. + */ + public: Map; + + /** + * Configuration for plugins to be exposed to the client-side. + */ + browserConfigs: Map>; } /** @internal */ @@ -97,8 +99,17 @@ export class PluginsService implements CoreService; -export const setupMock: jest.Mocked = { +export const setupMock: jest.Mocked = { render: jest.fn(), }; export const mockSetup = jest.fn().mockResolvedValue(setupMock); diff --git a/src/core/server/rendering/rendering_service.mock.ts b/src/core/server/rendering/rendering_service.mock.ts new file mode 100644 index 00000000000000..7eba332512386d --- /dev/null +++ b/src/core/server/rendering/rendering_service.mock.ts @@ -0,0 +1,31 @@ +/* + * 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 { InternalRenderingServiceSetup } from './types'; + +function createRenderingSetup() { + const mocked: jest.Mocked = { + render: jest.fn().mockResolvedValue(''), + }; + return mocked; +} + +export const renderingMock = { + createSetupContract: createRenderingSetup, +}; diff --git a/src/core/server/rendering/rendering_service.test.ts b/src/core/server/rendering/rendering_service.test.ts index 43ff4f633085c9..d1c527aca4dbaf 100644 --- a/src/core/server/rendering/rendering_service.test.ts +++ b/src/core/server/rendering/rendering_service.test.ts @@ -22,7 +22,7 @@ import { load } from 'cheerio'; import { httpServerMock } from '../http/http_server.mocks'; import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock'; import { mockRenderingServiceParams, mockRenderingSetupDeps } from './__mocks__/params'; -import { RenderingServiceSetup } from './types'; +import { InternalRenderingServiceSetup } from './types'; import { RenderingService } from './rendering_service'; const INJECTED_METADATA = { @@ -62,15 +62,9 @@ describe('RenderingService', () => { }); describe('setup()', () => { - it('creates instance of RenderingServiceSetup', async () => { - const rendering = await service.setup(mockRenderingSetupDeps); - - expect(rendering.render).toBeInstanceOf(Function); - }); - describe('render()', () => { let uiSettings: ReturnType; - let render: RenderingServiceSetup['render']; + let render: InternalRenderingServiceSetup['render']; beforeEach(async () => { uiSettings = uiSettingsServiceMock.createClient(); @@ -78,6 +72,13 @@ describe('RenderingService', () => { registered: { name: 'title' }, }); render = (await service.setup(mockRenderingSetupDeps)).render; + await service.start({ + legacy: { + legacyInternals: { + getVars: () => ({}), + }, + }, + } as any); }); it('renders "core" page', async () => { diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx index dbafd5806bd74a..a02d85d22b2cbb 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/src/core/server/rendering/rendering_service.tsx @@ -23,41 +23,37 @@ import { take } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; +import { UiPlugins } from '../plugins'; import { CoreService } from '../../types'; import { CoreContext } from '../core_context'; import { Template } from './views'; +import { LegacyService } from '../legacy'; import { IRenderOptions, RenderingSetupDeps, - RenderingServiceSetup, + InternalRenderingServiceSetup, RenderingMetadata, } from './types'; /** @internal */ -export class RenderingService implements CoreService { +export class RenderingService implements CoreService { + private legacyInternals?: LegacyService['legacyInternals']; constructor(private readonly coreContext: CoreContext) {} public async setup({ http, legacyPlugins, - plugins, - }: RenderingSetupDeps): Promise { - async function getUiConfig(pluginId: string) { - const browserConfig = plugins.uiPlugins.browserConfigs.get(pluginId); - - return ((await browserConfig?.pipe(take(1)).toPromise()) ?? {}) as Record; - } - + uiPlugins, + }: RenderingSetupDeps): Promise { return { render: async ( request, uiSettings, - { - app = { getId: () => 'core' }, - includeUserSettings = true, - vars = {}, - }: IRenderOptions = {} + { app = { getId: () => 'core' }, includeUserSettings = true, vars }: IRenderOptions = {} ) => { + if (!this.legacyInternals) { + throw new Error('Cannot render before "start"'); + } const { env } = this.coreContext; const basePath = http.basePath.get(request); const serverBasePath = http.basePath.serverBasePath; @@ -87,12 +83,12 @@ export class RenderingService implements CoreService { translationsUrl: `${basePath}/translations/${i18n.getLocale()}.json`, }, csp: { warnLegacyBrowsers: http.csp.warnLegacyBrowsers }, - vars, + vars: vars ?? (await this.legacyInternals!.getVars('core', request)), uiPlugins: await Promise.all( - [...plugins.uiPlugins.public].map(async ([id, plugin]) => ({ + [...uiPlugins.public].map(async ([id, plugin]) => ({ id, plugin, - config: await getUiConfig(id), + config: await this.getUiConfig(uiPlugins, id), })) ), legacyMetadata: { @@ -116,7 +112,15 @@ export class RenderingService implements CoreService { }; } - public async start() {} + public async start({ legacy }: { legacy: LegacyService }) { + this.legacyInternals = legacy.legacyInternals; + } public async stop() {} + + private async getUiConfig(uiPlugins: UiPlugins, pluginId: string) { + const browserConfig = uiPlugins.browserConfigs.get(pluginId); + + return ((await browserConfig?.pipe(take(1)).toPromise()) ?? {}) as Record; + } } diff --git a/src/core/server/rendering/types.ts b/src/core/server/rendering/types.ts index cfaa23d4911396..2a3be930550062 100644 --- a/src/core/server/rendering/types.ts +++ b/src/core/server/rendering/types.ts @@ -23,7 +23,7 @@ import { Env } from '../config'; import { ICspConfig } from '../csp'; import { InternalHttpServiceSetup, KibanaRequest, LegacyRequest } from '../http'; import { LegacyNavLink, LegacyServiceDiscoverPlugins } from '../legacy'; -import { PluginsServiceSetup, DiscoveredPlugin } from '../plugins'; +import { UiPlugins, DiscoveredPlugin } from '../plugins'; import { IUiSettingsClient, UserProvidedValues } from '../ui_settings'; /** @internal */ @@ -75,7 +75,7 @@ export interface RenderingMetadata { export interface RenderingSetupDeps { http: InternalHttpServiceSetup; legacyPlugins: LegacyServiceDiscoverPlugins; - plugins: PluginsServiceSetup; + uiPlugins: UiPlugins; } /** @public */ @@ -102,31 +102,8 @@ export interface IRenderOptions { vars?: Record; } -/** @public */ -export interface IScopedRenderingClient { - /** - * Generate a `KibanaResponse` which renders an HTML page bootstrapped - * with the `core` bundle. Intended as a response body for HTTP route handlers. - * - * @example - * ```ts - * router.get( - * { path: '/', validate: false }, - * (context, request, response) => - * response.ok({ - * body: await context.core.rendering.render(), - * headers: { - * 'content-security-policy': context.core.http.csp.header, - * }, - * }) - * ); - * ``` - */ - render(options?: Pick): Promise; -} - /** @internal */ -export interface RenderingServiceSetup { +export interface InternalRenderingServiceSetup { /** * Generate a `KibanaResponse` which renders an HTML page bootstrapped * with the `core` bundle or the ID of another specified legacy bundle. diff --git a/src/core/server/saved_objects/migrations/core/build_index_map.test.ts b/src/core/server/saved_objects/migrations/core/build_index_map.test.ts index 44add4e9770067..2c710d4eaa0797 100644 --- a/src/core/server/saved_objects/migrations/core/build_index_map.test.ts +++ b/src/core/server/saved_objects/migrations/core/build_index_map.test.ts @@ -26,7 +26,7 @@ const createRegistry = (...types: Array>) => { types.forEach(type => registry.registerType({ name: 'unknown', - namespaceAgnostic: false, + namespaceType: 'single', hidden: false, mappings: { properties: {} }, migrations: {}, @@ -41,7 +41,7 @@ test('mappings without index pattern goes to default index', () => { kibanaIndexName: '.kibana', registry: createRegistry({ name: 'type1', - namespaceAgnostic: false, + namespaceType: 'single', }), indexMap: { type1: { @@ -73,7 +73,7 @@ test(`mappings with custom index pattern doesn't go to default index`, () => { kibanaIndexName: '.kibana', registry: createRegistry({ name: 'type1', - namespaceAgnostic: false, + namespaceType: 'single', indexPattern: '.other_kibana', }), indexMap: { @@ -106,7 +106,7 @@ test('creating a script gets added to the index pattern', () => { kibanaIndexName: '.kibana', registry: createRegistry({ name: 'type1', - namespaceAgnostic: false, + namespaceType: 'single', indexPattern: '.other_kibana', convertToAliasScript: `ctx._id = ctx._source.type + ':' + ctx._id`, }), @@ -141,12 +141,12 @@ test('throws when two scripts are defined for an index pattern', () => { const registry = createRegistry( { name: 'type1', - namespaceAgnostic: false, + namespaceType: 'single', convertToAliasScript: `ctx._id = ctx._source.type + ':' + ctx._id`, }, { name: 'type2', - namespaceAgnostic: false, + namespaceType: 'single', convertToAliasScript: `ctx._id = ctx._source.type + ':' + ctx._id`, } ); diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts index ef3f546b5e574d..64270c677ff206 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts @@ -32,7 +32,7 @@ const createRegistry = (...types: Array>) => { types.forEach(type => registry.registerType({ name: 'unknown', - namespaceAgnostic: false, + namespaceType: 'single', hidden: false, mappings: { properties: {} }, migrations: {}, diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts index 257b32c1e4c237..3f5c0c38766157 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts @@ -27,7 +27,7 @@ const defaultSavedObjectTypes: SavedObjectsType[] = [ { name: 'testtype', hidden: false, - namespaceAgnostic: false, + namespaceType: 'single', mappings: { properties: { name: { type: 'keyword' }, diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts index 336eeff99f47b6..cda0e86f15bdf9 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts @@ -28,8 +28,8 @@ const createRegistry = (types: Array>) => { types.forEach(type => registry.registerType({ name: 'unknown', - namespaceAgnostic: false, hidden: false, + namespaceType: 'single', mappings: { properties: {} }, migrations: {}, ...type, @@ -120,7 +120,7 @@ function mockOptions(): KibanaMigratorOptions { { name: 'testtype', hidden: false, - namespaceAgnostic: false, + namespaceType: 'single', mappings: { properties: { name: { type: 'keyword' }, @@ -131,7 +131,7 @@ function mockOptions(): KibanaMigratorOptions { { name: 'testtype2', hidden: false, - namespaceAgnostic: false, + namespaceType: 'single', indexPattern: 'other-index', mappings: { properties: { diff --git a/src/core/server/saved_objects/routes/integration_tests/test_utils.ts b/src/core/server/saved_objects/routes/integration_tests/test_utils.ts index 82a889f75d3c12..23e0285201dc72 100644 --- a/src/core/server/saved_objects/routes/integration_tests/test_utils.ts +++ b/src/core/server/saved_objects/routes/integration_tests/test_utils.ts @@ -49,7 +49,7 @@ export const createExportableType = (name: string): SavedObjectsType => { return { name, hidden: false, - namespaceAgnostic: false, + namespaceType: 'single', mappings: { properties: {}, }, diff --git a/src/core/server/saved_objects/saved_objects_type_registry.test.ts b/src/core/server/saved_objects/saved_objects_type_registry.test.ts index 84337474f3ee32..f82822f90f4893 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.test.ts +++ b/src/core/server/saved_objects/saved_objects_type_registry.test.ts @@ -183,14 +183,6 @@ describe('SavedObjectTypeRegistry', () => { expectResult(false, { namespaceType: 'single' }); expectResult(false, { namespaceType: undefined }); }); - - // deprecated test cases - it(`returns true when namespaceAgnostic is true`, () => { - expectResult(true, { namespaceAgnostic: true, namespaceType: 'agnostic' }); - expectResult(true, { namespaceAgnostic: true, namespaceType: 'multiple' }); - expectResult(true, { namespaceAgnostic: true, namespaceType: 'single' }); - expectResult(true, { namespaceAgnostic: true, namespaceType: undefined }); - }); }); describe('#isSingleNamespace', () => { @@ -213,14 +205,6 @@ describe('SavedObjectTypeRegistry', () => { expectResult(false, { namespaceType: 'agnostic' }); expectResult(false, { namespaceType: 'multiple' }); }); - - // deprecated test cases - it(`returns false when namespaceAgnostic is true`, () => { - expectResult(false, { namespaceAgnostic: true, namespaceType: 'agnostic' }); - expectResult(false, { namespaceAgnostic: true, namespaceType: 'multiple' }); - expectResult(false, { namespaceAgnostic: true, namespaceType: 'single' }); - expectResult(false, { namespaceAgnostic: true, namespaceType: undefined }); - }); }); describe('#isMultiNamespace', () => { @@ -243,14 +227,6 @@ describe('SavedObjectTypeRegistry', () => { expectResult(false, { namespaceType: 'single' }); expectResult(false, { namespaceType: undefined }); }); - - // deprecated test cases - it(`returns false when namespaceAgnostic is true`, () => { - expectResult(false, { namespaceAgnostic: true, namespaceType: 'agnostic' }); - expectResult(false, { namespaceAgnostic: true, namespaceType: 'multiple' }); - expectResult(false, { namespaceAgnostic: true, namespaceType: 'single' }); - expectResult(false, { namespaceAgnostic: true, namespaceType: undefined }); - }); }); describe('#isHidden', () => { diff --git a/src/core/server/saved_objects/saved_objects_type_registry.ts b/src/core/server/saved_objects/saved_objects_type_registry.ts index be3fdb86a994cf..740313a53d1e24 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.ts +++ b/src/core/server/saved_objects/saved_objects_type_registry.ts @@ -72,11 +72,7 @@ export class SavedObjectTypeRegistry { * resolves to `false` if the type is not registered */ public isNamespaceAgnostic(type: string) { - return ( - this.types.get(type)?.namespaceType === 'agnostic' || - this.types.get(type)?.namespaceAgnostic || - false - ); + return this.types.get(type)?.namespaceType === 'agnostic'; } /** @@ -84,6 +80,7 @@ export class SavedObjectTypeRegistry { * resolves to `true` if the type is not registered */ public isSingleNamespace(type: string) { + // in the case we somehow registered a type with an invalid `namespaceType`, treat it as single-namespace return !this.isNamespaceAgnostic(type) && !this.isMultiNamespace(type); } @@ -92,7 +89,7 @@ export class SavedObjectTypeRegistry { * resolves to `false` if the type is not registered */ public isMultiNamespace(type: string) { - return !this.isNamespaceAgnostic(type) && this.types.get(type)?.namespaceType === 'multiple'; + return this.types.get(type)?.namespaceType === 'multiple'; } /** diff --git a/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts b/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts index a6b580e9b3461f..ea881805e1ae6b 100644 --- a/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts +++ b/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts @@ -32,7 +32,7 @@ describe('SavedObjectsRepository#createRepository', () => { typeRegistry.registerType({ name: 'nsAgnosticType', hidden: false, - namespaceAgnostic: true, + namespaceType: 'agnostic', mappings: { properties: { name: { type: 'keyword' }, @@ -44,7 +44,7 @@ describe('SavedObjectsRepository#createRepository', () => { typeRegistry.registerType({ name: 'nsType', hidden: false, - namespaceAgnostic: false, + namespaceType: 'single', indexPattern: 'beats', mappings: { properties: { @@ -56,7 +56,7 @@ describe('SavedObjectsRepository#createRepository', () => { typeRegistry.registerType({ name: 'hiddenType', hidden: true, - namespaceAgnostic: true, + namespaceType: 'agnostic', mappings: { properties: { name: { type: 'keyword' }, diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index 9efc82603b1796..b50c6dc9a1abf0 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -202,15 +202,10 @@ export interface SavedObjectsType { * See {@link SavedObjectsServiceStart.createInternalRepository | createInternalRepository}. */ hidden: boolean; - /** - * Is the type global (true), or not (false). - * @deprecated Use `namespaceType` instead. - */ - namespaceAgnostic?: boolean; /** * The {@link SavedObjectsNamespaceType | namespace type} for the type. */ - namespaceType?: SavedObjectsNamespaceType; + namespaceType: SavedObjectsNamespaceType; /** * If defined, the type instances will be stored in the given index instead of the default one. */ diff --git a/src/core/server/saved_objects/utils.test.ts b/src/core/server/saved_objects/utils.test.ts index 64bdf1771deccc..033aeea7c018da 100644 --- a/src/core/server/saved_objects/utils.test.ts +++ b/src/core/server/saved_objects/utils.test.ts @@ -421,14 +421,6 @@ describe('convertTypesToLegacySchema', () => { namespaceType: 'multiple', mappings: { properties: {} }, }, - // deprecated test case - { - name: 'typeD', - hidden: false, - namespaceAgnostic: true, - namespaceType: 'multiple', // if namespaceAgnostic and namespaceType are both set, namespaceAgnostic takes precedence - mappings: { properties: {} }, - }, ]; expect(convertTypesToLegacySchema(types)).toEqual({ typeA: { @@ -448,12 +440,6 @@ describe('convertTypesToLegacySchema', () => { isNamespaceAgnostic: false, multiNamespace: true, }, - // deprecated test case - typeD: { - hidden: false, - isNamespaceAgnostic: true, - multiNamespace: false, - }, }); }); }); diff --git a/src/core/server/saved_objects/utils.ts b/src/core/server/saved_objects/utils.ts index 53489638126293..af7c08d1fbfcc0 100644 --- a/src/core/server/saved_objects/utils.ts +++ b/src/core/server/saved_objects/utils.ts @@ -82,8 +82,8 @@ export const convertTypesToLegacySchema = ( return { ...schema, [type.name]: { - isNamespaceAgnostic: type.namespaceAgnostic || type.namespaceType === 'agnostic', - multiNamespace: !type.namespaceAgnostic && type.namespaceType === 'multiple', + isNamespaceAgnostic: type.namespaceType === 'agnostic', + multiNamespace: type.namespaceType === 'multiple', hidden: type.hidden, indexPattern: type.indexPattern, convertToAliasScript: type.convertToAliasScript, diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 37051da4b17da4..7ca5c75f19e8ff 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -632,7 +632,9 @@ export interface CoreSetup; // (undocumented) - http: HttpServiceSetup; + http: HttpServiceSetup & { + resources: HttpResources; + }; // (undocumented) metrics: MetricsServiceSetup; // (undocumented) @@ -861,6 +863,30 @@ export type Headers = { [header: string]: string | string[] | undefined; }; +// @public +export interface HttpResources { + register: (route: RouteConfig, handler: HttpResourcesRequestHandler) => void; +} + +// @public +export interface HttpResourcesRenderOptions { + headers?: ResponseHeaders; +} + +// @public +export type HttpResourcesRequestHandler

= RequestHandler; + +// @public +export type HttpResourcesResponseOptions = HttpResponseOptions; + +// @public +export interface HttpResourcesServiceToolkit { + renderAnonymousCoreApp: (options?: HttpResourcesRenderOptions) => Promise; + renderCoreApp: (options?: HttpResourcesRenderOptions) => Promise; + renderHtml: (options: HttpResourcesResponseOptions) => IKibanaResponse; + renderJs: (options: HttpResourcesResponseOptions) => IKibanaResponse; +} + // @public export interface HttpResponseOptions { body?: HttpResponsePayload; @@ -989,7 +1015,7 @@ export interface IRouter { // // @internal getRoutes: () => RouterRoute[]; - handleLegacyErrors: (handler: RequestHandler) => RequestHandler; + handleLegacyErrors: RequestHandlerWrapper; patch: RouteRegistrar<'patch'>; post: RouteRegistrar<'post'>; put: RouteRegistrar<'put'>; @@ -1008,11 +1034,6 @@ export type ISavedObjectTypeRegistry = Omit; -// @public (undocumented) -export interface IScopedRenderingClient { - render(options?: Pick): Promise; -} - // @public export interface IUiSettingsClient { get: (key: string) => Promise; @@ -1150,6 +1171,10 @@ export interface LegacyServiceSetupDeps { core: LegacyCoreSetup; // (undocumented) plugins: Record; + // Warning: (ae-forgotten-export) The symbol "UiPlugins" needs to be exported by the entry point index.d.ts + // + // (undocumented) + uiPlugins: UiPlugins; } // @public @deprecated (undocumented) @@ -1466,12 +1491,6 @@ export type PluginOpaqueId = symbol; export interface PluginsServiceSetup { contracts: Map; initialized: boolean; - // (undocumented) - uiPlugins: { - internal: Map; - public: Map; - browserConfigs: Map>; - }; } // @internal (undocumented) @@ -1496,19 +1515,13 @@ export type RedirectResponseOptions = HttpResponseOptions & { }; }; -// @internal (undocumented) -export interface RenderingServiceSetup { - render(request: R, uiSettings: IUiSettingsClient, options?: IRenderOptions): Promise; -} - // @public -export type RequestHandler

= (context: RequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory) => IKibanaResponse | Promise>; +export type RequestHandler

= (context: RequestHandlerContext, request: KibanaRequest, response: ResponseFactory) => IKibanaResponse | Promise>; // @public export interface RequestHandlerContext { // (undocumented) core: { - rendering: IScopedRenderingClient; savedObjects: { client: SavedObjectsClientContract; typeRegistry: ISavedObjectTypeRegistry; @@ -1529,6 +1542,9 @@ export type RequestHandlerContextContainer = IContextContainer = IContextProvider, TContextName>; +// @public +export type RequestHandlerWrapper = (handler: RequestHandler) => RequestHandler; + // @public export function resolveSavedObjectsImportErrors({ readStream, objectLimit, retries, savedObjectsClient, supportedTypes, namespace, }: SavedObjectsResolveImportErrorsOptions): Promise; @@ -1542,11 +1558,7 @@ export type ResponseError = string | Error | { export type ResponseErrorAttributes = Record; // @public -export type ResponseHeaders = { - [header in KnownHeaders]?: string | string[]; -} & { - [header: string]: string | string[]; -}; +export type ResponseHeaders = Record | Record; // @public export interface RouteConfig { @@ -2251,9 +2263,7 @@ export interface SavedObjectsType { mappings: SavedObjectsTypeMappingDefinition; migrations?: SavedObjectMigrationMap; name: string; - // @deprecated - namespaceAgnostic?: boolean; - namespaceType?: SavedObjectsNamespaceType; + namespaceType: SavedObjectsNamespaceType; } // @public @@ -2458,12 +2468,11 @@ export const validBodyOutput: readonly ["data", "stream"]; // Warnings were encountered during analysis: // // src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:162:3 - (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:163:3 - (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:164:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:166:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/plugins_service.ts:47:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:163:3 - (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:164:3 - (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:166:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:167:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:230:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:230:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:232:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index 24c41d511180a1..1e3e1638cf2a04 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -46,7 +46,10 @@ const rawConfigService = rawConfigServiceMock.create({}); beforeEach(() => { mockConfigService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true })); - mockPluginsService.discover.mockResolvedValue(new Map()); + mockPluginsService.discover.mockResolvedValue({ + pluginTree: new Map(), + uiPlugins: { internal: new Map(), public: new Map(), browserConfigs: new Map() }, + }); }); afterEach(() => { @@ -88,7 +91,10 @@ test('injects legacy dependency to context#setup()', async () => { [pluginA, []], [pluginB, [pluginA]], ]); - mockPluginsService.discover.mockResolvedValue(pluginDependencies); + mockPluginsService.discover.mockResolvedValue({ + pluginTree: pluginDependencies, + uiPlugins: { internal: new Map(), public: new Map(), browserConfigs: new Map() }, + }); await server.setup(); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 07ea431dd3a0df..d4c0ebcfb7cf27 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -29,7 +29,8 @@ import { import { CoreApp } from './core_app'; import { ElasticsearchService } from './elasticsearch'; import { HttpService } from './http'; -import { RenderingService, RenderingServiceSetup } from './rendering'; +import { HttpResourcesService } from './http_resources'; +import { RenderingService } from './rendering'; import { LegacyService, ensureValidConfiguration } from './legacy'; import { Logger, LoggerFactory } from './logging'; import { UiSettingsService } from './ui_settings'; @@ -71,6 +72,7 @@ export class Server { private readonly uiSettings: UiSettingsService; private readonly uuid: UuidService; private readonly metrics: MetricsService; + private readonly httpResources: HttpResourcesService; private readonly status: StatusService; private readonly coreApp: CoreApp; @@ -99,13 +101,14 @@ export class Server { this.metrics = new MetricsService(core); this.status = new StatusService(core); this.coreApp = new CoreApp(core); + this.httpResources = new HttpResourcesService(core); } public async setup() { this.log.debug('setting up server'); // Discover any plugins before continuing. This allows other systems to utilize the plugin dependency graph. - const pluginDependencies = await this.plugins.discover(); + const { pluginTree, uiPlugins } = await this.plugins.discover(); const legacyPlugins = await this.legacy.discoverPlugins(); // Immediately terminate in case of invalid configuration @@ -117,10 +120,7 @@ export class Server { // 1) Can access context from any NP plugin // 2) Can register context providers that will only be available to other legacy plugins and will not leak into // New Platform plugins. - pluginDependencies: new Map([ - ...pluginDependencies, - [this.legacy.legacyId, [...pluginDependencies.keys()]], - ]), + pluginDependencies: new Map([...pluginTree, [this.legacy.legacyId, [...pluginTree.keys()]]]), }); const uuidSetup = await this.uuid.setup(); @@ -148,6 +148,17 @@ export class Server { const metricsSetup = await this.metrics.setup({ http: httpSetup }); + const renderingSetup = await this.rendering.setup({ + http: httpSetup, + legacyPlugins, + uiPlugins, + }); + + const httpResourcesSetup = this.httpResources.setup({ + http: httpSetup, + rendering: renderingSetup, + }); + const statusSetup = this.status.setup({ elasticsearch: elasticsearchServiceSetup, savedObjects: savedObjectsSetup, @@ -158,28 +169,25 @@ export class Server { context: contextServiceSetup, elasticsearch: elasticsearchServiceSetup, http: httpSetup, - metrics: metricsSetup, savedObjects: savedObjectsSetup, status: statusSetup, uiSettings: uiSettingsSetup, uuid: uuidSetup, + metrics: metricsSetup, + rendering: renderingSetup, + httpResources: httpResourcesSetup, }; const pluginsSetup = await this.plugins.setup(coreSetup); this.pluginsInitialized = pluginsSetup.initialized; - const renderingSetup = await this.rendering.setup({ - http: httpSetup, - legacyPlugins, - plugins: pluginsSetup, - }); - await this.legacy.setup({ core: { ...coreSetup, plugins: pluginsSetup, rendering: renderingSetup }, plugins: mapToObject(pluginsSetup.contracts), + uiPlugins, }); - this.registerCoreContext(coreSetup, renderingSetup); + this.registerCoreContext(coreSetup); this.coreApp.setup(coreSetup); return coreSetup; @@ -201,7 +209,7 @@ export class Server { uiSettings: uiSettingsStart, }; - const pluginsStart = await this.plugins.start(this.coreStart!); + const pluginsStart = await this.plugins.start(this.coreStart); await this.legacy.start({ core: { @@ -212,7 +220,9 @@ export class Server { }); await this.http.start(); - await this.rendering.start(); + await this.rendering.start({ + legacy: this.legacy, + }); await this.metrics.start(); return this.coreStart; @@ -232,7 +242,7 @@ export class Server { await this.status.stop(); } - private registerCoreContext(coreSetup: InternalCoreSetup, rendering: RenderingServiceSetup) { + private registerCoreContext(coreSetup: InternalCoreSetup) { coreSetup.http.registerRouteHandlerContext( coreId, 'core', @@ -241,13 +251,6 @@ export class Server { const uiSettingsClient = coreSetup.uiSettings.asScopedToClient(savedObjectsClient); return { - rendering: { - render: async (options = {}) => - rendering.render(req, uiSettingsClient, { - ...options, - vars: await this.legacy.legacyInternals!.getVars('core', req), - }), - }, savedObjects: { client: savedObjectsClient, typeRegistry: this.coreStart!.savedObjects.getTypeRegistry(), diff --git a/src/core/server/ui_settings/saved_objects/ui_settings.ts b/src/core/server/ui_settings/saved_objects/ui_settings.ts index 031315bec0dab5..1bea65ddee9241 100644 --- a/src/core/server/ui_settings/saved_objects/ui_settings.ts +++ b/src/core/server/ui_settings/saved_objects/ui_settings.ts @@ -22,7 +22,7 @@ import { SavedObjectsType } from '../../saved_objects'; export const uiSettingsType: SavedObjectsType = { name: 'config', hidden: false, - namespaceAgnostic: false, + namespaceType: 'single', mappings: { // we don't want to allow `true` in the public `SavedObjectsTypeMappingDefinition` type, however // this is needed for the config that is kinda a special type. To avoid adding additional internal types diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 8ed64f004c9be3..43114b2edccfc8 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -24,6 +24,6 @@ export const storybookAliases = { drilldowns: 'x-pack/plugins/drilldowns/scripts/storybook.js', embeddable: 'src/plugins/embeddable/scripts/storybook.js', infra: 'x-pack/legacy/plugins/infra/scripts/storybook.js', - siem: 'x-pack/legacy/plugins/siem/scripts/storybook.js', + siem: 'x-pack/plugins/siem/scripts/storybook.js', ui_actions: 'x-pack/plugins/advanced_ui_actions/scripts/storybook.js', }; diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index 34756912fc247e..01d8a30b598c19 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -27,7 +27,7 @@ export const PROJECTS = [ new Project(resolve(REPO_ROOT, 'test/tsconfig.json'), { name: 'kibana/test' }), new Project(resolve(REPO_ROOT, 'x-pack/tsconfig.json')), new Project(resolve(REPO_ROOT, 'x-pack/test/tsconfig.json'), { name: 'x-pack/test' }), - new Project(resolve(REPO_ROOT, 'x-pack/legacy/plugins/siem/cypress/tsconfig.json'), { + new Project(resolve(REPO_ROOT, 'x-pack/plugins/siem/cypress/tsconfig.json'), { name: 'siem/cypress', }), new Project(resolve(REPO_ROOT, 'x-pack/legacy/plugins/apm/e2e/tsconfig.json'), { @@ -44,6 +44,9 @@ export const PROJECTS = [ ...glob .sync('examples/*/tsconfig.json', { cwd: REPO_ROOT }) .map(path => new Project(resolve(REPO_ROOT, path))), + ...glob + .sync('x-pack/examples/*/tsconfig.json', { cwd: REPO_ROOT }) + .map(path => new Project(resolve(REPO_ROOT, path))), ...glob .sync('test/plugin_functional/plugins/*/tsconfig.json', { cwd: REPO_ROOT }) .map(path => new Project(resolve(REPO_ROOT, path))), diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 1d643418997f56..989583742acd0a 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -54,11 +54,7 @@ export default function(kibana) { }, uiExports: { - hacks: [ - 'plugins/kibana/discover/legacy', - 'plugins/kibana/dev_tools', - 'plugins/kibana/visualize/legacy', - ], + hacks: ['plugins/kibana/discover/legacy', 'plugins/kibana/dev_tools'], app: { id: 'kibana', title: 'Kibana', diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html index d068e824a3e0a8..1221b01657e45f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html @@ -140,6 +140,7 @@

{{screenTitle}}

position="'top'" > -
-
- -
- - -`; - -exports[`CreateIndexPatternWizard renders index pattern step when there are indices 1`] = ` - -
-
- -
- -
-`; - -exports[`CreateIndexPatternWizard renders the empty state when there are no indices 1`] = ` - -
-
- -
- -
-`; - -exports[`CreateIndexPatternWizard renders time field step when step is set to 2 1`] = ` - -
-
- -
- -
-`; - -exports[`CreateIndexPatternWizard renders when there are no indices but there are remote clusters 1`] = ` - -
-
- -
- -
-`; - -exports[`CreateIndexPatternWizard shows system indices even if there are no other indices if the include system indices is toggled 1`] = ` - -
-
- -
- -
-`; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap new file mode 100644 index 00000000000000..09a06bd8827ce4 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap @@ -0,0 +1,312 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CreateIndexPatternWizard defaults to the loading state 1`] = ` + +
+
+ +
+ +
+`; + +exports[`CreateIndexPatternWizard renders index pattern step when there are indices 1`] = ` + +
+
+ +
+ +
+`; + +exports[`CreateIndexPatternWizard renders the empty state when there are no indices 1`] = ` + +
+
+ +
+ +
+`; + +exports[`CreateIndexPatternWizard renders time field step when step is set to 2 1`] = ` + +
+
+ +
+ +
+`; + +exports[`CreateIndexPatternWizard renders when there are no indices but there are remote clusters 1`] = ` + +
+
+ +
+ +
+`; + +exports[`CreateIndexPatternWizard shows system indices even if there are no other indices if the include system indices is toggled 1`] = ` + +
+
+ +
+ +
+`; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx index 648bf7f8f97388..d8f677b7f60897 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx @@ -48,7 +48,7 @@ interface StepIndexPatternProps { esService: DataPublicPluginStart['search']['__LEGACY']['esClient']; savedObjectsClient: SavedObjectsClient; indexPatternCreationType: IndexPatternCreationConfig; - goToNextStep: () => void; + goToNextStep: (query: string) => void; initialQuery?: string; uiSettings: IUiSettingsClient; } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.tsx similarity index 55% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.tsx index 941f87d4d9fd26..45af98661eda36 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.test.tsx @@ -21,17 +21,12 @@ import React from 'react'; import { shallow } from 'enzyme'; import { CreateIndexPatternWizard } from './create_index_pattern_wizard'; -const mockIndexPatternCreationType = { - getIndexPatternType: () => 'default', - getIndexPatternName: () => 'name', - getIsBeta: () => false, - checkIndicesForErrors: () => false, - getShowSystemIndices: () => false, - renderPrompt: () => {}, - getIndexPatternMappings: () => { - return {}; - }, -}; +import { coreMock } from '../../../../../../../../core/public/mocks'; +import { dataPluginMock } from '../../../../../../../../plugins/data/public/mocks'; +import { IndexPatternCreationConfig } from '../../../../../../../../plugins/index_pattern_management/public'; +import { IndexPattern } from '../../../../../../../../plugins/data/public'; +import { SavedObjectsClient } from '../../../../../../../../core/public'; + jest.mock('./components/step_index_pattern', () => ({ StepIndexPattern: 'StepIndexPattern' })); jest.mock('./components/step_time_field', () => ({ StepTimeField: 'StepTimeField' })); jest.mock('./components/header', () => ({ Header: 'Header' })); @@ -46,39 +41,36 @@ jest.mock('ui/chrome', () => ({ addBasePath: () => {}, })); -const loadingDataDocUrl = ''; +const { savedObjects, overlays, uiSettings } = coreMock.createStart(); +const { indexPatterns, search } = dataPluginMock.createStartContract(); +const mockIndexPatternCreationType = new IndexPatternCreationConfig({ + type: 'default', + name: 'name', +}); + const initialQuery = ''; const services = { - es: {}, - indexPatterns: {}, - savedObjectsClient: {}, - config: {}, - changeUrl: () => {}, - scopeApply: () => {}, - + es: search.__LEGACY.esClient, + indexPatterns, + savedObjectsClient: savedObjects.client as SavedObjectsClient, + uiSettings, + changeUrl: jest.fn(), + openConfirm: overlays.openConfirm, indexPatternCreationType: mockIndexPatternCreationType, }; describe('CreateIndexPatternWizard', () => { - it(`defaults to the loading state`, async () => { + test(`defaults to the loading state`, () => { const component = shallow( - + ); expect(component).toMatchSnapshot(); }); - it('renders the empty state when there are no indices', async () => { + test('renders the empty state when there are no indices', async () => { const component = shallow( - + ); component.setState({ @@ -91,13 +83,9 @@ describe('CreateIndexPatternWizard', () => { expect(component).toMatchSnapshot(); }); - it('renders when there are no indices but there are remote clusters', async () => { + test('renders when there are no indices but there are remote clusters', async () => { const component = shallow( - + ); component.setState({ @@ -110,13 +98,9 @@ describe('CreateIndexPatternWizard', () => { expect(component).toMatchSnapshot(); }); - it('shows system indices even if there are no other indices if the include system indices is toggled', async () => { + test('shows system indices even if there are no other indices if the include system indices is toggled', async () => { const component = shallow( - + ); component.setState({ @@ -129,13 +113,9 @@ describe('CreateIndexPatternWizard', () => { expect(component).toMatchSnapshot(); }); - it('renders index pattern step when there are indices', async () => { + test('renders index pattern step when there are indices', async () => { const component = shallow( - + ); component.setState({ @@ -147,13 +127,9 @@ describe('CreateIndexPatternWizard', () => { expect(component).toMatchSnapshot(); }); - it('renders time field step when step is set to 2', async () => { + test('renders time field step when step is set to 2', async () => { const component = shallow( - + ); component.setState({ @@ -166,37 +142,30 @@ describe('CreateIndexPatternWizard', () => { expect(component).toMatchSnapshot(); }); - it('invokes the provided services when creating an index pattern', async () => { - const get = jest.fn(); - const set = jest.fn(); + test('invokes the provided services when creating an index pattern', async () => { const create = jest.fn().mockImplementation(() => 'id'); const clear = jest.fn(); - const changeUrl = jest.fn(); - - const component = shallow( - ({ - create, - }), - clearCache: clear, - }, - changeUrl, - indexPatternCreationType: mockIndexPatternCreationType, - }} - /> + services.indexPatterns.clearCache = clear; + const indexPattern = ({ + id: '1', + title: 'my-fake-index-pattern', + timeFieldName: 'timestamp', + fields: [], + create, + } as unknown) as IndexPattern; + services.indexPatterns.make = async () => { + return indexPattern; + }; + + const component = shallow( + ); component.setState({ indexPattern: 'foo' }); - await component.instance().createIndexPattern(null, 'id'); - expect(get).toBeCalled(); + await component.instance().createIndexPattern(undefined, 'id'); + expect(services.uiSettings.get).toBeCalled(); expect(create).toBeCalled(); expect(clear).toBeCalledWith('id'); - expect(changeUrl).toBeCalledWith(`/management/kibana/index_patterns/id`); + expect(services.changeUrl).toBeCalledWith(`/management/kibana/index_patterns/id`); }); }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.tsx similarity index 63% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.tsx index 1a93188edd6cc1..4166d48349d351 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/create_index_pattern_wizard.tsx @@ -17,11 +17,11 @@ * under the License. */ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { ReactElement, Component } from 'react'; -import { EuiGlobalToastList } from '@elastic/eui'; +import { EuiGlobalToastList, EuiGlobalToastListToast } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { StepIndexPattern } from './components/step_index_pattern'; import { StepTimeField } from './components/step_time_field'; @@ -31,41 +31,61 @@ import { EmptyState } from './components/empty_state'; import { MAX_SEARCH_SIZE } from './constants'; import { ensureMinimumTime, getIndices } from './lib'; -import { i18n } from '@kbn/i18n'; - -export class CreateIndexPatternWizard extends Component { - static propTypes = { - initialQuery: PropTypes.string, - services: PropTypes.shape({ - es: PropTypes.object.isRequired, - indexPatterns: PropTypes.object.isRequired, - savedObjectsClient: PropTypes.object.isRequired, - indexPatternCreationType: PropTypes.object.isRequired, - config: PropTypes.object.isRequired, - changeUrl: PropTypes.func.isRequired, - openConfirm: PropTypes.func.isRequired, - }).isRequired, +import { + SavedObjectsClient, + IUiSettingsClient, + OverlayStart, +} from '../../../../../../../../core/public'; +import { DataPublicPluginStart } from '../../../../../../../../plugins/data/public'; +import { IndexPatternCreationConfig } from '../../../../../../../../plugins/index_pattern_management/public'; +import { MatchedIndex } from './types'; + +interface CreateIndexPatternWizardProps { + initialQuery: string; + services: { + indexPatternCreationType: IndexPatternCreationConfig; + es: DataPublicPluginStart['search']['__LEGACY']['esClient']; + indexPatterns: DataPublicPluginStart['indexPatterns']; + savedObjectsClient: SavedObjectsClient; + uiSettings: IUiSettingsClient; + changeUrl: (url: string) => void; + openConfirm: OverlayStart['openConfirm']; }; +} - constructor(props) { - super(props); - this.indexPatternCreationType = this.props.services.indexPatternCreationType; - this.state = { - step: 1, - indexPattern: '', - allIndices: [], - remoteClustersExist: false, - isInitiallyLoadingIndices: true, - isIncludingSystemIndices: false, - toasts: [], - }; - } +interface CreateIndexPatternWizardState { + step: number; + indexPattern: string; + allIndices: MatchedIndex[]; + remoteClustersExist: boolean; + isInitiallyLoadingIndices: boolean; + isIncludingSystemIndices: boolean; + toasts: EuiGlobalToastListToast[]; +} + +export class CreateIndexPatternWizard extends Component< + CreateIndexPatternWizardProps, + CreateIndexPatternWizardState +> { + state = { + step: 1, + indexPattern: '', + allIndices: [], + remoteClustersExist: false, + isInitiallyLoadingIndices: true, + isIncludingSystemIndices: false, + toasts: [], + }; async UNSAFE_componentWillMount() { this.fetchData(); } - catchAndWarn = async (asyncFn, errorValue, errorMsg) => { + catchAndWarn = async ( + asyncFn: Promise, + errorValue: [] | string[], + errorMsg: ReactElement + ) => { try { return await asyncFn; } catch (errors) { @@ -109,22 +129,26 @@ export class CreateIndexPatternWizard extends Component { // query local and remote indices, updating state independently ensureMinimumTime( this.catchAndWarn( - getIndices(services.es, this.indexPatternCreationType, `*`, MAX_SEARCH_SIZE), + getIndices(services.es, services.indexPatternCreationType, `*`, MAX_SEARCH_SIZE), [], indicesFailMsg ) - ).then(allIndices => this.setState({ allIndices, isInitiallyLoadingIndices: false })); + ).then((allIndices: MatchedIndex[]) => + this.setState({ allIndices, isInitiallyLoadingIndices: false }) + ); this.catchAndWarn( // if we get an error from remote cluster query, supply fallback value that allows user entry. // ['a'] is fallback value - getIndices(services.es, this.indexPatternCreationType, `*:*`, 1), + getIndices(services.es, services.indexPatternCreationType, `*:*`, 1), ['a'], clustersFailMsg - ).then(remoteIndices => this.setState({ remoteClustersExist: !!remoteIndices.length })); + ).then((remoteIndices: string[] | MatchedIndex[]) => + this.setState({ remoteClustersExist: !!remoteIndices.length }) + ); }; - createIndexPattern = async (timeFieldName, indexPatternId) => { + createIndexPattern = async (timeFieldName: string | undefined, indexPatternId: string) => { const { services } = this.props; const { indexPattern } = this.state; @@ -134,13 +158,13 @@ export class CreateIndexPatternWizard extends Component { id: indexPatternId, title: indexPattern, timeFieldName, - ...this.indexPatternCreationType.getIndexPatternMappings(), + ...services.indexPatternCreationType.getIndexPatternMappings(), }); const createdId = await emptyPattern.create(); if (!createdId) { const confirmMessage = i18n.translate('kbn.management.indexPattern.titleExistsLabel', { - values: { title: this.title }, + values: { title: emptyPattern.title }, defaultMessage: "An index pattern with the title '{title}' already exists.", }); @@ -157,15 +181,15 @@ export class CreateIndexPatternWizard extends Component { } } - if (!services.config.get('defaultIndex')) { - await services.config.set('defaultIndex', createdId); + if (!services.uiSettings.get('defaultIndex')) { + await services.uiSettings.set('defaultIndex', createdId); } services.indexPatterns.clearCache(createdId); services.changeUrl(`/management/kibana/index_patterns/${createdId}`); }; - goToTimeFieldStep = indexPattern => { + goToTimeFieldStep = (indexPattern: string) => { this.setState({ step: 2, indexPattern }); }; @@ -174,22 +198,23 @@ export class CreateIndexPatternWizard extends Component { }; onChangeIncludingSystemIndices = () => { - this.setState(state => ({ - isIncludingSystemIndices: !state.isIncludingSystemIndices, + this.setState(prevState => ({ + isIncludingSystemIndices: !prevState.isIncludingSystemIndices, })); }; renderHeader() { const { isIncludingSystemIndices } = this.state; + const { services } = this.props; return (
); } @@ -208,7 +233,7 @@ export class CreateIndexPatternWizard extends Component { return ; } - const hasDataIndices = allIndices.some(({ name }) => !name.startsWith('.')); + const hasDataIndices = allIndices.some(({ name }: MatchedIndex) => !name.startsWith('.')); if (!hasDataIndices && !isIncludingSystemIndices && !remoteClustersExist) { return ; } @@ -222,7 +247,7 @@ export class CreateIndexPatternWizard extends Component { isIncludingSystemIndices={isIncludingSystemIndices} esService={services.es} savedObjectsClient={services.savedObjectsClient} - indexPatternCreationType={this.indexPatternCreationType} + indexPatternCreationType={services.indexPatternCreationType} goToNextStep={this.goToTimeFieldStep} uiSettings={services.uiSettings} /> @@ -237,7 +262,7 @@ export class CreateIndexPatternWizard extends Component { indexPatternsService={services.indexPatterns} goToPreviousStep={this.goToIndexPatternStep} createIndexPattern={this.createIndexPattern} - indexPatternCreationType={this.indexPatternCreationType} + indexPatternCreationType={services.indexPatternCreationType} /> ); } @@ -245,9 +270,9 @@ export class CreateIndexPatternWizard extends Component { return null; } - removeToast = removedToast => { + removeToast = (id: string) => { this.setState(prevState => ({ - toasts: prevState.toasts.filter(toast => toast.id !== removedToast.id), + toasts: prevState.toasts.filter(toast => toast.id !== id), })); }; @@ -263,7 +288,9 @@ export class CreateIndexPatternWizard extends Component { { + this.removeToast(id); + }} toastLifeTimeMs={6000} /> diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js index 47cb773258cb46..ed1fc026c560cb 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js @@ -36,17 +36,15 @@ uiRoutes.when('/management/kibana/index_pattern', { $routeParams.type ); const services = { - config: npStart.core.uiSettings, + uiSettings: npStart.core.uiSettings, es: npStart.plugins.data.search.__LEGACY.esClient, indexPatterns: npStart.plugins.data.indexPatterns, - $http: npStart.core.http, savedObjectsClient: npStart.core.savedObjects.client, indexPatternCreationType, changeUrl: url => { $scope.$evalAsync(() => kbnUrl.changePath(url)); }, openConfirm: npStart.core.overlays.openConfirm, - uiSettings: npStart.core.uiSettings, }; const initialQuery = $routeParams.id ? decodeURIComponent($routeParams.id) : undefined; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts index 40583af7177fee..b1500f8303b666 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts @@ -20,7 +20,7 @@ import { getIndices } from './get_indices'; import { IndexPatternCreationConfig } from '../../../../../../../../../plugins/index_pattern_management/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LegacyApiCaller } from '../../../../../../../../../plugins/data/public/search'; +import { LegacyApiCaller } from '../../../../../../../../../plugins/data/public/search/legacy'; export const successfulResponse = { hits: { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html index fee8525a6af417..2decaf423183e1 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html @@ -1,11 +1,5 @@
-
- -
-
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js index 3569e9caf4e275..95d6cb6878e532 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js @@ -34,6 +34,8 @@ import { FieldEditor } from 'ui/field_editor'; import { I18nContext } from 'ui/i18n'; import { i18n } from '@kbn/i18n'; +import { IndexHeader } from '../index_header'; + const REACT_FIELD_EDITOR_ID = 'reactFieldEditor'; const renderFieldEditor = ( $scope, @@ -49,6 +51,7 @@ const renderFieldEditor = ( render( + - +

diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js index c65054f583ef2d..69184a513f53a5 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js @@ -18,7 +18,7 @@ */ import _ from 'lodash'; -import './index_header'; +import { IndexHeader } from './index_header'; import './create_edit_field'; import { docTitle } from 'ui/doc_title'; import { KbnUrlProvider } from 'ui/url'; @@ -44,6 +44,7 @@ import { createEditIndexPatternPageStateContainer } from './edit_index_pattern_s const REACT_SOURCE_FILTERS_DOM_ELEMENT_ID = 'reactSourceFiltersTable'; const REACT_INDEXED_FIELDS_DOM_ELEMENT_ID = 'reactIndexedFieldsTable'; const REACT_SCRIPTED_FIELDS_DOM_ELEMENT_ID = 'reactScriptedFieldsTable'; +const REACT_INDEX_HEADER_DOM_ELEMENT_ID = 'reactIndexHeader'; const TAB_INDEXED_FIELDS = 'indexedFields'; const TAB_SCRIPTED_FIELDS = 'scriptedFields'; @@ -160,6 +161,33 @@ function destroyIndexedFieldsTable() { node && unmountComponentAtNode(node); } +function destroyIndexHeader() { + const node = document.getElementById(REACT_INDEX_HEADER_DOM_ELEMENT_ID); + node && unmountComponentAtNode(node); +} + +function renderIndexHeader($scope, config) { + $scope.$$postDigest(() => { + const node = document.getElementById(REACT_INDEX_HEADER_DOM_ELEMENT_ID); + if (!node) { + return; + } + + render( + + + , + node + ); + }); +} + function handleTabChange($scope, newTab) { destroyIndexedFieldsTable(); destroySourceFiltersTable(); @@ -391,6 +419,9 @@ uiModules destroyIndexedFieldsTable(); destroyScriptedFieldsTable(); destroySourceFiltersTable(); + destroyIndexHeader(); destroyState(); }); + + renderIndexHeader($scope, config); }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.ts similarity index 94% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.ts index 7c288286bd61e4..44c05a55b36f95 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index.ts @@ -17,4 +17,4 @@ * under the License. */ -import './index_header'; +export { IndexHeader } from './index_header'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.html b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.html deleted file mode 100644 index d6b91d96f13d34..00000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.html +++ /dev/null @@ -1,59 +0,0 @@ -

-
- -

- - {{indexPattern.title}} -

-
- -
- - - - - -
-
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.js deleted file mode 100644 index 87bce06c1146cd..00000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.js +++ /dev/null @@ -1,40 +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 { uiModules } from 'ui/modules'; -import template from './index_header.html'; -uiModules.get('apps/management').directive('kbnManagementIndexPatternsHeader', function(config) { - return { - restrict: 'E', - template, - replace: true, - scope: { - indexPattern: '=', - setDefault: '&', - refreshFields: '&', - delete: '&', - }, - link: function($scope, $el, attrs) { - $scope.delete = attrs.delete ? $scope.delete : null; - $scope.setDefault = attrs.setDefault ? $scope.setDefault : null; - $scope.refreshFields = attrs.refreshFields ? $scope.refreshFields : null; - config.bindToScope($scope, 'defaultIndex'); - }, - }; -}); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx new file mode 100644 index 00000000000000..866d10ecb0e19d --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx @@ -0,0 +1,134 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiToolTip, + EuiFlexItem, + EuiIcon, + EuiTitle, + EuiButtonIcon, +} from '@elastic/eui'; +import { IIndexPattern } from '../../../../../../../../../plugins/data/public'; + +interface IndexHeaderProps { + defaultIndex: string; + indexPattern: IIndexPattern; + setDefault?: () => void; + refreshFields?: () => void; + deleteIndexPattern?: () => void; +} + +const setDefaultAriaLabel = i18n.translate('kbn.management.editIndexPattern.setDefaultAria', { + defaultMessage: 'Set as default index.', +}); + +const setDefaultTooltip = i18n.translate('kbn.management.editIndexPattern.setDefaultTooltip', { + defaultMessage: 'Set as default index.', +}); + +const refreshAriaLabel = i18n.translate('kbn.management.editIndexPattern.refreshAria', { + defaultMessage: 'Reload field list.', +}); + +const refreshTooltip = i18n.translate('kbn.management.editIndexPattern.refreshTooltip', { + defaultMessage: 'Refresh field list.', +}); + +const removeAriaLabel = i18n.translate('kbn.management.editIndexPattern.removeAria', { + defaultMessage: 'Remove index pattern.', +}); + +const removeTooltip = i18n.translate('kbn.management.editIndexPattern.removeTooltip', { + defaultMessage: 'Remove index pattern.', +}); + +export function IndexHeader({ + defaultIndex, + indexPattern, + setDefault, + refreshFields, + deleteIndexPattern, +}: IndexHeaderProps) { + return ( + + + + {defaultIndex === indexPattern.id && ( + + + + )} + + +

{indexPattern.title}

+
+
+
+
+ + + {setDefault && ( + + + + + + )} + + {refreshFields && ( + + + + + + )} + + {deleteIndexPattern && ( + + + + + + )} + + +
+ ); +} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/__snapshots__/indexed_fields_table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap similarity index 90% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/__snapshots__/indexed_fields_table.test.js.snap rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap index b38036a0c2bf0f..db2a032b1e4d9c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/__snapshots__/indexed_fields_table.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap @@ -16,9 +16,10 @@ exports[`IndexedFieldsTable should filter based on the query bar 1`] = ` "excluded": false, "format": undefined, "indexPattern": undefined, - "info": undefined, + "info": Array [], "name": "Elastic", "searchable": true, + "type": "name", }, ] } @@ -42,7 +43,7 @@ exports[`IndexedFieldsTable should filter based on the type filter 1`] = ` "excluded": false, "format": undefined, "indexPattern": undefined, - "info": undefined, + "info": Array [], "name": "timestamp", "type": "date", }, @@ -68,16 +69,17 @@ exports[`IndexedFieldsTable should render normally 1`] = ` "excluded": false, "format": undefined, "indexPattern": undefined, - "info": undefined, + "info": Array [], "name": "Elastic", "searchable": true, + "type": "name", }, Object { "displayName": "timestamp", "excluded": false, "format": undefined, "indexPattern": undefined, - "info": undefined, + "info": Array [], "name": "timestamp", "type": "date", }, @@ -86,7 +88,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` "excluded": false, "format": undefined, "indexPattern": undefined, - "info": undefined, + "info": Array [], "name": "conflictingField", "type": "conflict", }, diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap similarity index 92% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap index f3aa2c5da4b67b..2d51b1722cfb21 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap @@ -98,19 +98,26 @@ exports[`Table should render normally 1`] = ` Array [ Object { "displayName": "Elastic", - "info": Object {}, + "excluded": false, + "format": "", + "info": Array [], "name": "Elastic", "searchable": true, + "type": "name", }, Object { "displayName": "timestamp", - "info": Object {}, + "excluded": false, + "format": "YYYY-MM-DD", + "info": Array [], "name": "timestamp", "type": "date", }, Object { "displayName": "conflictingField", - "info": Object {}, + "excluded": false, + "format": "", + "info": Array [], "name": "conflictingField", "type": "conflict", }, diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/index.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/index.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/index.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.js deleted file mode 100644 index 29e160cf1c1826..00000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.js +++ /dev/null @@ -1,231 +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 React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; - -import { EuiIcon, EuiInMemoryTable, EuiIconTip } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; - -export class Table extends PureComponent { - static propTypes = { - indexPattern: PropTypes.object.isRequired, - items: PropTypes.array.isRequired, - editField: PropTypes.func.isRequired, - }; - - renderBooleanTemplate(value, label) { - return value ? : ; - } - - renderFieldName(name, field) { - const { indexPattern } = this.props; - - const infoLabel = i18n.translate( - 'kbn.management.editIndexPattern.fields.table.additionalInfoAriaLabel', - { defaultMessage: 'Additional field information' } - ); - const timeLabel = i18n.translate( - 'kbn.management.editIndexPattern.fields.table.primaryTimeAriaLabel', - { defaultMessage: 'Primary time field' } - ); - const timeContent = i18n.translate( - 'kbn.management.editIndexPattern.fields.table.primaryTimeTooltip', - { defaultMessage: 'This field represents the time that events occurred.' } - ); - - return ( - - {name} - {field.info && field.info.length ? ( - -   - ( -
{info}
- ))} - /> -
- ) : null} - {indexPattern.timeFieldName === name ? ( - -   - - - ) : null} -
- ); - } - - renderFieldType(type, isConflict) { - const label = i18n.translate('kbn.management.editIndexPattern.fields.table.multiTypeAria', { - defaultMessage: 'Multiple type field', - }); - const content = i18n.translate( - 'kbn.management.editIndexPattern.fields.table.multiTypeTooltip', - { - defaultMessage: - 'The type of this field changes across indices. It is unavailable for many analysis functions.', - } - ); - - return ( - - {type} - {isConflict ? ( - -   - - - ) : ( - '' - )} - - ); - } - - render() { - const { items, editField } = this.props; - - const pagination = { - initialPageSize: 10, - pageSizeOptions: [5, 10, 25, 50], - }; - - const columns = [ - { - field: 'displayName', - name: i18n.translate('kbn.management.editIndexPattern.fields.table.nameHeader', { - defaultMessage: 'Name', - }), - dataType: 'string', - sortable: true, - render: (value, field) => { - return this.renderFieldName(value, field); - }, - width: '38%', - 'data-test-subj': 'indexedFieldName', - }, - { - field: 'type', - name: i18n.translate('kbn.management.editIndexPattern.fields.table.typeHeader', { - defaultMessage: 'Type', - }), - dataType: 'string', - sortable: true, - render: value => { - return this.renderFieldType(value, value === 'conflict'); - }, - 'data-test-subj': 'indexedFieldType', - }, - { - field: 'format', - name: i18n.translate('kbn.management.editIndexPattern.fields.table.formatHeader', { - defaultMessage: 'Format', - }), - dataType: 'string', - sortable: true, - }, - { - field: 'searchable', - name: i18n.translate('kbn.management.editIndexPattern.fields.table.searchableHeader', { - defaultMessage: 'Searchable', - }), - description: i18n.translate( - 'kbn.management.editIndexPattern.fields.table.searchableDescription', - { defaultMessage: 'These fields can be used in the filter bar' } - ), - dataType: 'boolean', - sortable: true, - render: value => - this.renderBooleanTemplate( - value, - i18n.translate('kbn.management.editIndexPattern.fields.table.isSearchableAria', { - defaultMessage: 'Is searchable', - }) - ), - }, - { - field: 'aggregatable', - name: i18n.translate('kbn.management.editIndexPattern.fields.table.aggregatableLabel', { - defaultMessage: 'Aggregatable', - }), - description: i18n.translate( - 'kbn.management.editIndexPattern.fields.table.aggregatableDescription', - { defaultMessage: 'These fields can be used in visualization aggregations' } - ), - dataType: 'boolean', - sortable: true, - render: value => - this.renderBooleanTemplate( - value, - i18n.translate('kbn.management.editIndexPattern.fields.table.isAggregatableAria', { - defaultMessage: 'Is aggregatable', - }) - ), - }, - { - field: 'excluded', - name: i18n.translate('kbn.management.editIndexPattern.fields.table.excludedLabel', { - defaultMessage: 'Excluded', - }), - description: i18n.translate( - 'kbn.management.editIndexPattern.fields.table.excludedDescription', - { defaultMessage: 'Fields that are excluded from _source when it is fetched' } - ), - dataType: 'boolean', - sortable: true, - render: value => - this.renderBooleanTemplate( - value, - i18n.translate('kbn.management.editIndexPattern.fields.table.isExcludedAria', { - defaultMessage: 'Is excluded', - }) - ), - }, - { - name: '', - actions: [ - { - name: i18n.translate('kbn.management.editIndexPattern.fields.table.editLabel', { - defaultMessage: 'Edit', - }), - description: i18n.translate( - 'kbn.management.editIndexPattern.fields.table.editDescription', - { defaultMessage: 'Edit' } - ), - icon: 'pencil', - onClick: editField, - type: 'icon', - 'data-test-subj': 'editFieldFormat', - }, - ], - width: '40px', - }, - ]; - - return ( - - ); - } -} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/table.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx similarity index 66% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/table.test.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx index 4fd9ef7485bdf1..d0479a9a9e0329 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/__jest__/table.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx @@ -19,31 +19,53 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; - -import { Table } from '../table'; +import { IIndexPattern } from '../../../../../../../../../../../plugins/data/public'; +import { IndexedFieldItem } from '../../types'; +import { Table } from './table'; const indexPattern = { timeFieldName: 'timestamp', -}; - -const items = [ - { name: 'Elastic', displayName: 'Elastic', searchable: true, info: {} }, - { name: 'timestamp', displayName: 'timestamp', type: 'date', info: {} }, - { name: 'conflictingField', displayName: 'conflictingField', type: 'conflict', info: {} }, +} as IIndexPattern; + +const items: IndexedFieldItem[] = [ + { + name: 'Elastic', + displayName: 'Elastic', + searchable: true, + info: [], + type: 'name', + excluded: false, + format: '', + }, + { + name: 'timestamp', + displayName: 'timestamp', + type: 'date', + info: [], + excluded: false, + format: 'YYYY-MM-DD', + }, + { + name: 'conflictingField', + displayName: 'conflictingField', + type: 'conflict', + info: [], + excluded: false, + format: '', + }, ]; describe('Table', () => { - it('should render normally', async () => { - const component = shallowWithI18nProvider( + test('should render normally', () => { + const component = shallow( {}} /> ); expect(component).toMatchSnapshot(); }); - it('should render normal field name', async () => { - const component = shallowWithI18nProvider( + test('should render normal field name', () => { + const component = shallow(
{}} /> ); @@ -51,8 +73,8 @@ describe('Table', () => { expect(tableCell).toMatchSnapshot(); }); - it('should render timestamp field name', async () => { - const component = shallowWithI18nProvider( + test('should render timestamp field name', () => { + const component = shallow(
{}} /> ); @@ -60,8 +82,8 @@ describe('Table', () => { expect(tableCell).toMatchSnapshot(); }); - it('should render the boolean template (true)', async () => { - const component = shallowWithI18nProvider( + test('should render the boolean template (true)', () => { + const component = shallow(
{}} /> ); @@ -69,8 +91,8 @@ describe('Table', () => { expect(tableCell).toMatchSnapshot(); }); - it('should render the boolean template (false)', async () => { - const component = shallowWithI18nProvider( + test('should render the boolean template (false)', () => { + const component = shallow(
{}} /> ); @@ -78,8 +100,8 @@ describe('Table', () => { expect(tableCell).toMatchSnapshot(); }); - it('should render normal type', async () => { - const component = shallowWithI18nProvider( + test('should render normal type', () => { + const component = shallow(
{}} /> ); @@ -87,8 +109,8 @@ describe('Table', () => { expect(tableCell).toMatchSnapshot(); }); - it('should render conflicting type', async () => { - const component = shallowWithI18nProvider( + test('should render conflicting type', () => { + const component = shallow(
{}} /> ); @@ -96,10 +118,10 @@ describe('Table', () => { expect(tableCell).toMatchSnapshot(); }); - it('should allow edits', () => { + test('should allow edits', () => { const editField = jest.fn(); - const component = shallowWithI18nProvider( + const component = shallow(
); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.tsx new file mode 100644 index 00000000000000..aa8e8b8e13a07a --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/components/table/table.tsx @@ -0,0 +1,281 @@ +/* + * 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, { PureComponent } from 'react'; + +import { EuiIcon, EuiInMemoryTable, EuiIconTip, EuiBasicTableColumn } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { IIndexPattern } from '../../../../../../../../../../../plugins/data/public'; +import { IndexedFieldItem } from '../../types'; + +// localized labels +const additionalInfoAriaLabel = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.additionalInfoAriaLabel', + { defaultMessage: 'Additional field information' } +); + +const primaryTimeAriaLabel = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.primaryTimeAriaLabel', + { defaultMessage: 'Primary time field' } +); + +const primaryTimeTooltip = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.primaryTimeTooltip', + { defaultMessage: 'This field represents the time that events occurred.' } +); + +const multiTypeAriaLabel = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.multiTypeAria', + { + defaultMessage: 'Multiple type field', + } +); + +const multiTypeTooltip = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.multiTypeTooltip', + { + defaultMessage: + 'The type of this field changes across indices. It is unavailable for many analysis functions.', + } +); + +const nameHeader = i18n.translate('kbn.management.editIndexPattern.fields.table.nameHeader', { + defaultMessage: 'Name', +}); + +const typeHeader = i18n.translate('kbn.management.editIndexPattern.fields.table.typeHeader', { + defaultMessage: 'Type', +}); + +const formatHeader = i18n.translate('kbn.management.editIndexPattern.fields.table.formatHeader', { + defaultMessage: 'Format', +}); + +const searchableHeader = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.searchableHeader', + { + defaultMessage: 'Searchable', + } +); + +const searchableDescription = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.searchableDescription', + { defaultMessage: 'These fields can be used in the filter bar' } +); + +const isSearchableAriaLabel = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.isSearchableAria', + { + defaultMessage: 'Is searchable', + } +); + +const aggregatableLabel = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.aggregatableLabel', + { + defaultMessage: 'Aggregatable', + } +); + +const aggregatableDescription = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.aggregatableDescription', + { defaultMessage: 'These fields can be used in visualization aggregations' } +); + +const isAggregatableAriaLabel = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.isAggregatableAria', + { + defaultMessage: 'Is aggregatable', + } +); + +const excludedLabel = i18n.translate('kbn.management.editIndexPattern.fields.table.excludedLabel', { + defaultMessage: 'Excluded', +}); + +const excludedDescription = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.excludedDescription', + { defaultMessage: 'Fields that are excluded from _source when it is fetched' } +); + +const isExcludedAriaLabel = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.isExcludedAria', + { + defaultMessage: 'Is excluded', + } +); + +const editLabel = i18n.translate('kbn.management.editIndexPattern.fields.table.editLabel', { + defaultMessage: 'Edit', +}); + +const editDescription = i18n.translate( + 'kbn.management.editIndexPattern.fields.table.editDescription', + { defaultMessage: 'Edit' } +); + +interface IndexedFieldProps { + indexPattern: IIndexPattern; + items: IndexedFieldItem[]; + editField: (field: IndexedFieldItem) => void; +} + +export class Table extends PureComponent { + renderBooleanTemplate(value: string, arialLabel: string) { + return value ? : ; + } + + renderFieldName(name: string, field: IndexedFieldItem) { + const { indexPattern } = this.props; + + return ( + + {name} + {field.info && field.info.length ? ( + +   + ( +
{info}
+ ))} + /> +
+ ) : null} + {indexPattern.timeFieldName === name ? ( + +   + + + ) : null} +
+ ); + } + + renderFieldType(type: string, isConflict: boolean) { + return ( + + {type} + {isConflict ? ( + +   + + + ) : ( + '' + )} + + ); + } + + render() { + const { items, editField } = this.props; + + const pagination = { + initialPageSize: 10, + pageSizeOptions: [5, 10, 25, 50], + }; + + const columns: Array> = [ + { + field: 'displayName', + name: nameHeader, + dataType: 'string', + sortable: true, + render: (value: string, field: IndexedFieldItem) => { + return this.renderFieldName(value, field); + }, + width: '38%', + 'data-test-subj': 'indexedFieldName', + }, + { + field: 'type', + name: typeHeader, + dataType: 'string', + sortable: true, + render: (value: string) => { + return this.renderFieldType(value, value === 'conflict'); + }, + 'data-test-subj': 'indexedFieldType', + }, + { + field: 'format', + name: formatHeader, + dataType: 'string', + sortable: true, + }, + { + field: 'searchable', + name: searchableHeader, + description: searchableDescription, + dataType: 'boolean', + sortable: true, + render: (value: string) => this.renderBooleanTemplate(value, isSearchableAriaLabel), + }, + { + field: 'aggregatable', + name: aggregatableLabel, + description: aggregatableDescription, + dataType: 'boolean', + sortable: true, + render: (value: string) => this.renderBooleanTemplate(value, isAggregatableAriaLabel), + }, + { + field: 'excluded', + name: excludedLabel, + description: excludedDescription, + dataType: 'boolean', + sortable: true, + render: (value: string) => this.renderBooleanTemplate(value, isExcludedAriaLabel), + }, + { + name: '', + actions: [ + { + name: editLabel, + description: editDescription, + icon: 'pencil', + onClick: editField, + type: 'icon', + 'data-test-subj': 'editFieldFormat', + }, + ], + width: '40px', + }, + ]; + + return ( + + ); + } +} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/index.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/index.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/index.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/indexed_fields_table.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx similarity index 70% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/indexed_fields_table.test.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx index 26e271ea477daf..f8b78a92e098eb 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/__jest__/indexed_fields_table.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx @@ -19,8 +19,8 @@ import React from 'react'; import { shallow } from 'enzyme'; - -import { IndexedFieldsTable } from '../indexed_fields_table'; +import { IndexPatternField, IIndexPattern } from '../../../../../../../../../plugins/data/public'; +import { IndexedFieldsTable } from './indexed_fields_table'; jest.mock('@elastic/eui', () => ({ EuiFlexGroup: 'eui-flex-group', @@ -29,7 +29,7 @@ jest.mock('@elastic/eui', () => ({ EuiInMemoryTable: 'eui-in-memory-table', })); -jest.mock('../components/table', () => ({ +jest.mock('./components/table', () => ({ // Note: this seems to fix React complaining about non lowercase attributes Table: () => { return 'table'; @@ -37,27 +37,37 @@ jest.mock('../components/table', () => ({ })); const helpers = { - redirectToRoute: () => {}, + redirectToRoute: (obj: any) => {}, + getFieldInfo: () => [], }; const fields = [ - { name: 'Elastic', displayName: 'Elastic', searchable: true }, + { + name: 'Elastic', + displayName: 'Elastic', + searchable: true, + type: 'name', + }, { name: 'timestamp', displayName: 'timestamp', type: 'date' }, { name: 'conflictingField', displayName: 'conflictingField', type: 'conflict' }, -]; +] as IndexPatternField[]; -const indexPattern = { +const indexPattern = ({ getNonScriptedFields: () => fields, -}; +} as unknown) as IIndexPattern; describe('IndexedFieldsTable', () => { - it('should render normally', async () => { + test('should render normally', async () => { const component = shallow( {}} + fieldWildcardMatcher={() => { + return () => false; + }} + indexedFieldTypeFilter="" + fieldFilter="" /> ); @@ -67,13 +77,17 @@ describe('IndexedFieldsTable', () => { expect(component).toMatchSnapshot(); }); - it('should filter based on the query bar', async () => { + test('should filter based on the query bar', async () => { const component = shallow( {}} + fieldWildcardMatcher={() => { + return () => false; + }} + indexedFieldTypeFilter="" + fieldFilter="" /> ); @@ -84,13 +98,17 @@ describe('IndexedFieldsTable', () => { expect(component).toMatchSnapshot(); }); - it('should filter based on the type filter', async () => { + test('should filter based on the type filter', async () => { const component = shallow( {}} + fieldWildcardMatcher={() => { + return () => false; + }} + indexedFieldTypeFilter="" + fieldFilter="" /> ); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx similarity index 68% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx index 074e5784f3dae6..7c2bb565615d71 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx @@ -18,26 +18,33 @@ */ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { createSelector } from 'reselect'; - +import { IndexPatternField, IIndexPattern } from '../../../../../../../../../plugins/data/public'; import { Table } from './components/table'; import { getFieldFormat } from './lib'; +import { IndexedFieldItem } from './types'; -export class IndexedFieldsTable extends Component { - static propTypes = { - fields: PropTypes.array.isRequired, - indexPattern: PropTypes.object.isRequired, - fieldFilter: PropTypes.string, - indexedFieldTypeFilter: PropTypes.string, - helpers: PropTypes.shape({ - redirectToRoute: PropTypes.func.isRequired, - getFieldInfo: PropTypes.func, - }), - fieldWildcardMatcher: PropTypes.func.isRequired, +interface IndexedFieldsTableProps { + fields: IndexPatternField[]; + indexPattern: IIndexPattern; + fieldFilter?: string; + indexedFieldTypeFilter?: string; + helpers: { + redirectToRoute: (obj: any) => void; + getFieldInfo: (indexPattern: IIndexPattern, field: string) => string[]; }; + fieldWildcardMatcher: (filters: any[]) => (val: any) => boolean; +} + +interface IndexedFieldsTableState { + fields: IndexedFieldItem[]; +} - constructor(props) { +export class IndexedFieldsTable extends Component< + IndexedFieldsTableProps, + IndexedFieldsTableState +> { + constructor(props: IndexedFieldsTableProps) { super(props); this.state = { @@ -45,7 +52,7 @@ export class IndexedFieldsTable extends Component { }; } - UNSAFE_componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps: IndexedFieldsTableProps) { if (nextProps.fields !== this.props.fields) { this.setState({ fields: this.mapFields(nextProps.fields), @@ -53,10 +60,11 @@ export class IndexedFieldsTable extends Component { } } - mapFields(fields) { + mapFields(fields: IndexPatternField[]): IndexedFieldItem[] { const { indexPattern, fieldWildcardMatcher, helpers } = this.props; const sourceFilters = - indexPattern.sourceFilters && indexPattern.sourceFilters.map(f => f.value); + indexPattern.sourceFilters && + indexPattern.sourceFilters.map((f: Record) => f.value); const fieldWildcardMatch = fieldWildcardMatcher(sourceFilters || []); return ( @@ -76,9 +84,10 @@ export class IndexedFieldsTable extends Component { } getFilteredFields = createSelector( - state => state.fields, - (state, props) => props.fieldFilter, - (state, props) => props.indexedFieldTypeFilter, + (state: IndexedFieldsTableState) => state.fields, + (state: IndexedFieldsTableState, props: IndexedFieldsTableProps) => props.fieldFilter, + (state: IndexedFieldsTableState, props: IndexedFieldsTableProps) => + props.indexedFieldTypeFilter, (fields, fieldFilter, indexedFieldTypeFilter) => { if (fieldFilter) { const normalizedFieldFilter = fieldFilter.toLowerCase(); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/__jest__/get_field_format.test.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.test.ts similarity index 74% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/__jest__/get_field_format.test.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.test.ts index 7090f701999197..fc7477c074ac2d 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/__jest__/get_field_format.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.test.ts @@ -17,9 +17,10 @@ * under the License. */ -import { getFieldFormat } from '../get_field_format'; +import { IIndexPattern } from '../../../../../../../../../../plugins/data/public'; +import { getFieldFormat } from './get_field_format'; -const indexPattern = { +const indexPattern = ({ fieldFormatMap: { Elastic: { type: { @@ -27,26 +28,26 @@ const indexPattern = { }, }, }, -}; +} as unknown) as IIndexPattern; describe('getFieldFormat', () => { - it('should handle no arguments', () => { + test('should handle no arguments', () => { expect(getFieldFormat()).toEqual(''); }); - it('should handle no field name', () => { + test('should handle no field name', () => { expect(getFieldFormat(indexPattern)).toEqual(''); }); - it('should handle empty name', () => { + test('should handle empty name', () => { expect(getFieldFormat(indexPattern, '')).toEqual(''); }); - it('should handle undefined field name', () => { + test('should handle undefined field name', () => { expect(getFieldFormat(indexPattern, 'none')).toEqual(undefined); }); - it('should retrieve field format', () => { + test('should retrieve field format', () => { expect(getFieldFormat(indexPattern, 'Elastic')).toEqual('string'); }); }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.ts similarity index 84% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.ts index 9402694bb13718..1d6f267430f076 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/get_field_format.ts @@ -18,8 +18,9 @@ */ import { get } from 'lodash'; +import { IIndexPattern } from '../../../../../../../../../../plugins/data/public'; -export function getFieldFormat(indexPattern, fieldName) { +export function getFieldFormat(indexPattern?: IIndexPattern, fieldName?: string): string { return indexPattern && fieldName ? get(indexPattern, ['fieldFormatMap', fieldName, 'type', 'title']) : ''; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/index.ts similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/index.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/lib/index.ts diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/types.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/types.ts new file mode 100644 index 00000000000000..f27f4608bf5d58 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/indexed_fields_table/types.ts @@ -0,0 +1,25 @@ +/* + * 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 { IFieldType } from '../../../../../../../../../plugins/data/public'; + +export interface IndexedFieldItem extends IFieldType { + info: string[]; + excluded: boolean; +} diff --git a/src/legacy/core_plugins/kibana/public/visualize/_index.scss b/src/legacy/core_plugins/kibana/public/visualize/_index.scss deleted file mode 100644 index 079d82936bb574..00000000000000 --- a/src/legacy/core_plugins/kibana/public/visualize/_index.scss +++ /dev/null @@ -1,5 +0,0 @@ -// Visualize plugin styles -@import 'np_ready/index'; - -// should be removed while moving the visualize into NP -@import '../../../../../plugins/vis_default_editor/public/index' diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts deleted file mode 100644 index f6d73b987912d5..00000000000000 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts +++ /dev/null @@ -1,36 +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. - */ - -/** - * The imports in this file are static functions and types which still live in legacy folders and are used - * within dashboard. To consolidate them all in one place, they are re-exported from this file. Eventually - * this list should become empty. Imports from the top level of shimmed or moved plugins can be imported - * directly where they are needed. - */ - -export { DashboardConstants } from '../../../../../plugins/dashboard/public'; -export { - VisSavedObject, - VISUALIZE_EMBEDDABLE_TYPE, -} from '../../../../../plugins/visualizations/public/'; -export { - configureAppAngularModule, - migrateLegacyQuery, - subscribeWithScope, -} from '../../../../../plugins/kibana_legacy/public'; diff --git a/src/legacy/core_plugins/timelion/public/app.js b/src/legacy/core_plugins/timelion/public/app.js index c15318d29e7610..7f5c7d4664af86 100644 --- a/src/legacy/core_plugins/timelion/public/app.js +++ b/src/legacy/core_plugins/timelion/public/app.js @@ -27,10 +27,11 @@ import { fatalError, toastNotifications } from 'ui/notify'; import { timefilter } from 'ui/timefilter'; import { npStart } from 'ui/new_platform'; import { getSavedSheetBreadcrumbs, getCreateBreadcrumbs } from './breadcrumbs'; -import { getTimezone } from '../../vis_type_timelion/public'; +import { getTimezone } from '../../../../plugins/vis_type_timelion/public'; import 'uiExports/savedObjectTypes'; +require('ui/i18n'); require('ui/autoload/all'); // TODO: remove ui imports completely (move to plugins) @@ -57,7 +58,7 @@ require('plugins/timelion/directives/timelion_options_sheet'); document.title = 'Timelion - Kibana'; -const app = require('ui/modules').get('apps/timelion', []); +const app = require('ui/modules').get('apps/timelion', ['i18n', 'ngSanitize']); require('ui/routes').enable(); diff --git a/src/legacy/core_plugins/timelion/public/directives/timelion_expression_input.js b/src/legacy/core_plugins/timelion/public/directives/timelion_expression_input.js index 57262fda55e48a..35ac883e5d99cf 100644 --- a/src/legacy/core_plugins/timelion/public/directives/timelion_expression_input.js +++ b/src/legacy/core_plugins/timelion/public/directives/timelion_expression_input.js @@ -43,7 +43,7 @@ import _ from 'lodash'; import $ from 'jquery'; import PEG from 'pegjs'; -import grammar from 'raw-loader!../../../../../plugins/timelion/common/chain.peg'; +import grammar from 'raw-loader!../../../../../plugins/vis_type_timelion/common/chain.peg'; import timelionExpressionInputTemplate from './timelion_expression_input.html'; import { SUGGESTION_TYPE, @@ -52,7 +52,7 @@ import { insertAtLocation, } from './timelion_expression_input_helpers'; import { comboBoxKeyCodes } from '@elastic/eui'; -import { getArgValueSuggestions } from '../../../vis_type_timelion/public/helpers/arg_value_suggestions'; +import { npStart } from 'ui/new_platform'; const Parser = PEG.generate(grammar); @@ -68,7 +68,7 @@ export function TimelionExpInput($http, $timeout) { replace: true, template: timelionExpressionInputTemplate, link: function(scope, elem) { - const argValueSuggestions = getArgValueSuggestions(); + const argValueSuggestions = npStart.plugins.visTypeTimelion.getArgValueSuggestions(); const expressionInput = elem.find('[data-expression-input]'); const functionReference = {}; let suggestibleFunctionLocation = {}; diff --git a/src/legacy/core_plugins/timelion/public/panels/timechart/schema.ts b/src/legacy/core_plugins/timelion/public/panels/timechart/schema.ts index cd40cbfa89ffed..34b389f5ff4ce5 100644 --- a/src/legacy/core_plugins/timelion/public/panels/timechart/schema.ts +++ b/src/legacy/core_plugins/timelion/public/panels/timechart/schema.ts @@ -17,7 +17,8 @@ * under the License. */ -import '../../../../vis_type_timelion/public/flot'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import '../../../../../../plugins/vis_type_timelion/public/flot'; import _ from 'lodash'; import $ from 'jquery'; import moment from 'moment-timezone'; @@ -28,11 +29,14 @@ import { calculateInterval, DEFAULT_TIME_FORMAT, // @ts-ignore -} from '../../../../../../plugins/timelion/common/lib'; -import { tickFormatters } from '../../../../vis_type_timelion/public/helpers/tick_formatters'; +} from '../../../../../../plugins/vis_type_timelion/common/lib'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { tickFormatters } from '../../../../../../plugins/vis_type_timelion/public/helpers/tick_formatters'; import { TimelionVisualizationDependencies } from '../../plugin'; -import { xaxisFormatterProvider } from '../../../../vis_type_timelion/public/helpers/xaxis_formatter'; -import { generateTicksProvider } from '../../../../vis_type_timelion/public/helpers/tick_generator'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { xaxisFormatterProvider } from '../../../../../../plugins/vis_type_timelion/public/helpers/xaxis_formatter'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { generateTicksProvider } from '../../../../../../plugins/vis_type_timelion/public/helpers/tick_generator'; const DEBOUNCE_DELAY = 50; diff --git a/src/legacy/core_plugins/vis_type_markdown/index.ts b/src/legacy/core_plugins/vis_type_markdown/index.ts deleted file mode 100644 index 3c00420e57d551..00000000000000 --- a/src/legacy/core_plugins/vis_type_markdown/index.ts +++ /dev/null @@ -1,44 +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 { resolve } from 'path'; -import { Legacy } from 'kibana'; - -import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types'; - -const markdownPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => - new Plugin({ - id: 'markdown_vis', - require: ['kibana', 'elasticsearch'], - publicDir: resolve(__dirname, 'public'), - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - hacks: [resolve(__dirname, 'public/legacy')], - injectDefaultVars: server => ({}), - }, - init: (server: Legacy.Server) => ({}), - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - } as Legacy.PluginSpecOptions); - -// eslint-disable-next-line import/no-default-export -export default markdownPluginInitializer; diff --git a/src/legacy/core_plugins/vis_type_markdown/package.json b/src/legacy/core_plugins/vis_type_markdown/package.json deleted file mode 100644 index 5c233d82fe5067..00000000000000 --- a/src/legacy/core_plugins/vis_type_markdown/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "markdown_vis", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts b/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts deleted file mode 100644 index 1cfc583f6e0052..00000000000000 --- a/src/legacy/core_plugins/vis_type_markdown/public/legacy.ts +++ /dev/null @@ -1,33 +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 { PluginInitializerContext } from 'kibana/public'; -import { npSetup, npStart } from 'ui/new_platform'; -import { MarkdownPluginSetupDependencies } from './plugin'; -import { plugin } from '.'; - -const plugins: Readonly = { - expressions: npSetup.plugins.expressions, - visualizations: npSetup.plugins.visualizations, -}; - -const pluginInstance = plugin({} as PluginInitializerContext); - -export const setup = pluginInstance.setup(npSetup.core, plugins); -export const start = pluginInstance.start(npStart.core); diff --git a/src/legacy/core_plugins/vis_type_metric/index.ts b/src/legacy/core_plugins/vis_type_metric/index.ts deleted file mode 100644 index 8e6654e40f0fcf..00000000000000 --- a/src/legacy/core_plugins/vis_type_metric/index.ts +++ /dev/null @@ -1,44 +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 { resolve } from 'path'; -import { Legacy } from 'kibana'; - -import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types'; - -const metricPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => - new Plugin({ - id: 'metric_vis', - require: ['kibana', 'elasticsearch'], - publicDir: resolve(__dirname, 'public'), - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - hacks: [resolve(__dirname, 'public/legacy')], - injectDefaultVars: server => ({}), - }, - init: (server: Legacy.Server) => ({}), - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - } as Legacy.PluginSpecOptions); - -// eslint-disable-next-line import/no-default-export -export default metricPluginInitializer; diff --git a/src/legacy/core_plugins/vis_type_metric/package.json b/src/legacy/core_plugins/vis_type_metric/package.json deleted file mode 100644 index e570261fc30dd3..00000000000000 --- a/src/legacy/core_plugins/vis_type_metric/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "metric_vis", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/vis_type_metric/public/legacy.ts b/src/legacy/core_plugins/vis_type_metric/public/legacy.ts deleted file mode 100644 index ba883601e5d65e..00000000000000 --- a/src/legacy/core_plugins/vis_type_metric/public/legacy.ts +++ /dev/null @@ -1,34 +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 { PluginInitializerContext } from 'kibana/public'; -import { npSetup, npStart } from 'ui/new_platform'; -import { MetricVisPluginSetupDependencies } from './plugin'; -import { plugin } from '.'; - -const plugins: Readonly = { - expressions: npSetup.plugins.expressions, - visualizations: npSetup.plugins.visualizations, - charts: npSetup.plugins.charts, -}; - -const pluginInstance = plugin({} as PluginInitializerContext); - -export const setup = pluginInstance.setup(npSetup.core, plugins); -export const start = pluginInstance.start(npStart.core, { data: npStart.plugins.data }); diff --git a/src/legacy/core_plugins/vis_type_timelion/index.ts b/src/legacy/core_plugins/vis_type_timelion/index.ts deleted file mode 100644 index 7bca5154c84fd4..00000000000000 --- a/src/legacy/core_plugins/vis_type_timelion/index.ts +++ /dev/null @@ -1,44 +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 { resolve } from 'path'; -import { Legacy } from 'kibana'; - -import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types'; - -const timelionVisPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => - new Plugin({ - id: 'timelion_vis', - require: ['kibana', 'elasticsearch'], - publicDir: resolve(__dirname, 'public'), - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - hacks: [resolve(__dirname, 'public/legacy')], - injectDefaultVars: server => ({}), - }, - init: (server: Legacy.Server) => ({}), - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - }); - -// eslint-disable-next-line import/no-default-export -export default timelionVisPluginInitializer; diff --git a/src/legacy/core_plugins/vis_type_timelion/package.json b/src/legacy/core_plugins/vis_type_timelion/package.json deleted file mode 100644 index 9b09f98ce6cafa..00000000000000 --- a/src/legacy/core_plugins/vis_type_timelion/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "timelion_vis", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/_index.scss b/src/legacy/core_plugins/vis_type_timelion/public/components/_index.scss deleted file mode 100644 index 1d887f43ff9a1c..00000000000000 --- a/src/legacy/core_plugins/vis_type_timelion/public/components/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './panel'; -@import './timelion_expression_input'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/index.ts b/src/legacy/core_plugins/vis_type_timeseries/index.ts deleted file mode 100644 index 596fd5b581a719..00000000000000 --- a/src/legacy/core_plugins/vis_type_timeseries/index.ts +++ /dev/null @@ -1,45 +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 { resolve } from 'path'; -import { Legacy } from 'kibana'; - -import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types'; - -const metricsPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => - new Plugin({ - id: 'metrics', - require: ['kibana', 'elasticsearch'], - publicDir: resolve(__dirname, 'public'), - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - hacks: [resolve(__dirname, 'public/legacy')], - injectDefaultVars: server => ({}), - }, - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(true), - chartResolution: Joi.number().default(150), - minimumBucketSize: Joi.number().default(10), - }).default(); - }, - } as Legacy.PluginSpecOptions); - -// eslint-disable-next-line import/no-default-export -export default metricsPluginInitializer; diff --git a/src/legacy/core_plugins/vis_type_timeseries/package.json b/src/legacy/core_plugins/vis_type_timeseries/package.json deleted file mode 100644 index 6b4874dfe6a68b..00000000000000 --- a/src/legacy/core_plugins/vis_type_timeseries/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "author": "Chris Cowan", - "name": "metrics", - "version": "kibana" -} - diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts b/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts deleted file mode 100644 index 42f116701be51b..00000000000000 --- a/src/legacy/core_plugins/vis_type_timeseries/public/legacy.ts +++ /dev/null @@ -1,33 +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 { PluginInitializerContext } from 'kibana/public'; -import { npSetup, npStart } from 'ui/new_platform'; -import { MetricsPluginSetupDependencies } from './plugin'; -import { plugin } from '.'; - -const plugins: Readonly = { - expressions: npSetup.plugins.expressions, - visualizations: npSetup.plugins.visualizations, -}; - -const pluginInstance = plugin({} as PluginInitializerContext); - -export const setup = pluginInstance.setup(npSetup.core, plugins); -export const start = pluginInstance.start(npStart.core, npStart.plugins); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts index aa11e0ef41fbac..579caa1cb88f67 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts @@ -30,6 +30,7 @@ const setupPlugins: Readonly = { expressions: npSetup.plugins.expressions, visualizations: npSetup.plugins.visualizations, charts: npSetup.plugins.charts, + visTypeXy: npSetup.plugins.visTypeXy, }; const startPlugins: Readonly = { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts b/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts index 2731fb6f5fbe6e..ef3f664252856e 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts @@ -24,6 +24,7 @@ import { PluginInitializerContext, } from 'kibana/public'; +import { VisTypeXyPluginSetup } from 'src/plugins/vis_type_xy/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn'; @@ -39,7 +40,6 @@ import { createGoalVisTypeDefinition, } from './vis_type_vislib_vis_types'; import { ChartsPluginSetup } from '../../../../plugins/charts/public'; -import { ConfigSchema as VisTypeXyConfigSchema } from '../../vis_type_xy'; import { DataPublicPluginStart } from '../../../../plugins/data/public'; import { setFormatService, setDataActions } from './services'; @@ -53,6 +53,7 @@ export interface VisTypeVislibPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; charts: ChartsPluginSetup; + visTypeXy?: VisTypeXyPluginSetup; } /** @internal */ @@ -68,7 +69,7 @@ export class VisTypeVislibPlugin implements Plugin { public async setup( core: VisTypeVislibCoreSetup, - { expressions, visualizations, charts }: VisTypeVislibPluginSetupDependencies + { expressions, visualizations, charts, visTypeXy }: VisTypeVislibPluginSetupDependencies ) { const visualizationDependencies: Readonly = { uiSettings: core.uiSettings, @@ -86,12 +87,8 @@ export class VisTypeVislibPlugin implements Plugin { ]; const vislibFns = [createVisTypeVislibVisFn(), createPieVisFn()]; - const visTypeXy = core.injectedMetadata.getInjectedVar('visTypeXy') as - | VisTypeXyConfigSchema['visTypeXy'] - | undefined; - // if visTypeXy plugin is disabled it's config will be undefined - if (!visTypeXy || !visTypeXy.enabled) { + if (!visTypeXy) { const convertedTypes: any[] = []; const convertedFns: any[] = []; diff --git a/src/legacy/core_plugins/vis_type_xy/index.ts b/src/legacy/core_plugins/vis_type_xy/index.ts deleted file mode 100644 index 58d2e425eef408..00000000000000 --- a/src/legacy/core_plugins/vis_type_xy/index.ts +++ /dev/null @@ -1,56 +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 { resolve } from 'path'; -import { Legacy } from 'kibana'; - -import { LegacyPluginApi, LegacyPluginInitializer } from '../../types'; - -export interface ConfigSchema { - visTypeXy: { - enabled: boolean; - }; -} - -const visTypeXyPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => - new Plugin({ - id: 'visTypeXy', - require: ['kibana', 'elasticsearch', 'visualizations', 'interpreter'], - publicDir: resolve(__dirname, 'public'), - uiExports: { - hacks: [resolve(__dirname, 'public/legacy')], - injectDefaultVars(server): ConfigSchema { - const config = server.config(); - - return { - visTypeXy: { - enabled: config.get('visTypeXy.enabled') as boolean, - }, - }; - }, - }, - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(false), - }).default(); - }, - } as Legacy.PluginSpecOptions); - -// eslint-disable-next-line import/no-default-export -export default visTypeXyPluginInitializer; diff --git a/src/legacy/core_plugins/vis_type_xy/package.json b/src/legacy/core_plugins/vis_type_xy/package.json deleted file mode 100644 index 920f7dcb44e87c..00000000000000 --- a/src/legacy/core_plugins/vis_type_xy/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "visTypeXy", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/vis_type_xy/public/legacy.ts b/src/legacy/core_plugins/vis_type_xy/public/legacy.ts deleted file mode 100644 index 740ceeaac6a7d5..00000000000000 --- a/src/legacy/core_plugins/vis_type_xy/public/legacy.ts +++ /dev/null @@ -1,40 +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 { npSetup, npStart } from 'ui/new_platform'; -import { PluginInitializerContext } from 'kibana/public'; - -import { plugin } from '.'; -import { VisTypeXyPluginSetupDependencies, VisTypeXyPluginStartDependencies } from './plugin'; - -const setupPlugins: Readonly = { - expressions: npSetup.plugins.expressions, - visualizations: npSetup.plugins.visualizations, - charts: npSetup.plugins.charts, -}; - -const startPlugins: Readonly = { - expressions: npStart.plugins.expressions, - visualizations: npStart.plugins.visualizations, -}; - -const pluginInstance = plugin({} as PluginInitializerContext); - -export const setup = pluginInstance.setup(npSetup.core, setupPlugins); -export const start = pluginInstance.start(npStart.core, startPlugins); diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index a9b8c293748543..0d2f3528c90190 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -41,10 +41,11 @@ import { // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { LegacyConfig, ILegacyService, ILegacyInternals } from '../../core/server/legacy'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { UiPlugins } from '../../core/server/plugins'; import { ApmOssPlugin } from '../core_plugins/apm_oss'; import { CallClusterWithRequest, ElasticsearchPlugin } from '../core_plugins/elasticsearch'; import { UsageCollectionSetup } from '../../plugins/usage_collection/server'; -import { Capabilities } from '../../core/server'; import { UiSettingsServiceFactoryOptions } from '../../legacy/ui/ui_settings/ui_settings_service_factory'; import { HomeServerPluginSetup } from '../../plugins/home/server'; @@ -111,7 +112,7 @@ export interface KibanaCore { kibanaMigrator: LegacyServiceStartDeps['core']['savedObjects']['migrator']; legacy: ILegacyInternals; rendering: LegacyServiceSetupDeps['core']['rendering']; - uiPlugins: LegacyServiceSetupDeps['core']['plugins']['uiPlugins']; + uiPlugins: UiPlugins; uiSettings: LegacyServiceSetupDeps['core']['uiSettings']; savedObjectsClientProvider: LegacyServiceStartDeps['core']['savedObjects']['clientProvider']; }; diff --git a/src/legacy/server/logging/log_reporter.js b/src/legacy/server/logging/log_reporter.js index 6e62a5ee284e38..b784d03a5b86e9 100644 --- a/src/legacy/server/logging/log_reporter.js +++ b/src/legacy/server/logging/log_reporter.js @@ -30,7 +30,7 @@ import { LogInterceptor } from './log_interceptor'; // thrown every time we start the server. // In order to keep using the legacy logger until we remove it I'm just adding // a new hard limit here. -process.stdout.setMaxListeners(15); +process.stdout.setMaxListeners(25); export function getLoggerStream({ events, config }) { const squeeze = new Squeeze(events); diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss index 87006d9347de49..aaed52f8b120a9 100644 --- a/src/legacy/ui/public/_index.scss +++ b/src/legacy/ui/public/_index.scss @@ -17,9 +17,3 @@ @import './field_editor/index'; @import './style_compile/index'; @import '../../../plugins/management/public/components/index'; - -// The following are prefixed with "vis" - -// Can't import vis folder here because of cascading issues, it's imported in core_plugins/kibana -// @import './vis/index'; -@import './visualize/index'; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 33a7fdad065b45..f577a29ce90b91 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -453,6 +453,7 @@ export const npStart = { createAggConfigs: (indexPattern, configStates = []) => { return new AggConfigs(indexPattern, configStates, { typesRegistry: aggTypesRegistry.start(), + fieldFormats: getFieldFormatsRegistry(mockCoreStart), }); }, types: aggTypesRegistry.start(), diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index 21b80e827e4c25..5ae2e2348aaa15 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -22,6 +22,7 @@ import { IScope } from 'angular'; import { UiActionsStart, UiActionsSetup } from 'src/plugins/ui_actions/public'; import { EmbeddableStart, EmbeddableSetup } from 'src/plugins/embeddable/public'; import { createBrowserHistory } from 'history'; +import { VisTypeXyPluginSetup } from 'src/plugins/vis_type_xy/public'; import { DashboardStart } from '../../../../plugins/dashboard/public'; import { setSetupServices, setStartServices } from './set_services'; import { @@ -68,6 +69,7 @@ import { VisualizationsSetup, VisualizationsStart, } from '../../../../plugins/visualizations/public'; +import { VisTypeTimelionPluginStart } from '../../../../plugins/vis_type_timelion/public'; import { MapsLegacyPluginSetup } from '../../../../plugins/maps_legacy/public'; export interface PluginsSetup { @@ -93,6 +95,7 @@ export interface PluginsSetup { savedObjectsManagement: SavedObjectsManagementPluginSetup; mapsLegacy: MapsLegacyPluginSetup; indexPatternManagement: IndexPatternManagementSetup; + visTypeXy?: VisTypeXyPluginSetup; } export interface PluginsStart { @@ -114,6 +117,7 @@ export interface PluginsStart { telemetry?: TelemetryPluginStart; dashboard: DashboardStart; savedObjectsManagement: SavedObjectsManagementPluginStart; + visTypeTimelion: VisTypeTimelionPluginStart; indexPatternManagement: IndexPatternManagementStart; } diff --git a/src/legacy/ui/public/visualize/_index.scss b/src/legacy/ui/public/visualize/_index.scss deleted file mode 100644 index d9761f741353bc..00000000000000 --- a/src/legacy/ui/public/visualize/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import '../../../../plugins/visualizations/public/components/index'; diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 1093153edbbf7e..4557d911620a23 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -78,10 +78,10 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { '{{this}}', {{/each}} '{{regularBundlePath}}/commons.bundle.js', - {{!-- '{{regularBundlePath}}/plugin/data/data.plugin.js', --}} - '{{regularBundlePath}}/plugin/kibanaUtils/kibanaUtils.plugin.js', - '{{regularBundlePath}}/plugin/esUiShared/esUiShared.plugin.js', - '{{regularBundlePath}}/plugin/kibanaReact/kibanaReact.plugin.js' + {{!-- '{{regularBundlePath}}/plugin:data/data.plugin.js', --}} + '{{regularBundlePath}}/plugin:kibanaUtils/kibanaUtils.plugin.js', + '{{regularBundlePath}}/plugin:esUiShared/esUiShared.plugin.js', + '{{regularBundlePath}}/plugin:kibanaReact/kibanaReact.plugin.js' ], function () { load([ '{{regularBundlePath}}/{{appId}}.bundle.js', diff --git a/src/optimize/bundles_route/bundles_route.js b/src/optimize/bundles_route/bundles_route.js index 0c2e98b5acd63c..f4e3108f80a3b8 100644 --- a/src/optimize/bundles_route/bundles_route.js +++ b/src/optimize/bundles_route/bundles_route.js @@ -79,8 +79,8 @@ export function createBundlesRoute({ ), ...npUiPluginPublicDirs.map(({ id, path }) => buildRouteForBundles( - `${basePublicPath}/bundles/plugin/${id}/`, - `/bundles/plugin/${id}/`, + `${basePublicPath}/bundles/plugin:${id}/`, + `/bundles/plugin:${id}/`, path, fileHashCache ) diff --git a/src/plugins/console/server/__tests__/proxy_route/proxy_fallback.test.ts b/src/plugins/console/server/__tests__/proxy_route/proxy_fallback.test.ts new file mode 100644 index 00000000000000..b226bad11a01af --- /dev/null +++ b/src/plugins/console/server/__tests__/proxy_route/proxy_fallback.test.ts @@ -0,0 +1,64 @@ +/* + * 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 { duration } from 'moment'; +import { getProxyRouteHandlerDeps } from './mocks'; + +import { kibanaResponseFactory } from '../../../../../core/server'; +import { createHandler } from '../../routes/api/console/proxy/create_handler'; +import * as requestModule from '../../lib/proxy_request'; + +describe('Console Proxy Route', () => { + afterEach(async () => { + jest.resetAllMocks(); + }); + + describe('fallback behaviour', () => { + it('falls back to all configured endpoints regardless of error', async () => { + // Describe a situation where all three configured nodes reject + (requestModule.proxyRequest as jest.Mock).mockRejectedValueOnce(new Error('ECONNREFUSED')); + (requestModule.proxyRequest as jest.Mock).mockRejectedValueOnce(new Error('EHOSTUNREACH')); + (requestModule.proxyRequest as jest.Mock).mockRejectedValueOnce(new Error('ESOCKETTIMEDOUT')); + + const handler = createHandler( + getProxyRouteHandlerDeps({ + readLegacyESConfig: () => ({ + requestTimeout: duration(30000), + customHeaders: {}, + requestHeadersWhitelist: [], + hosts: ['http://localhost:9201', 'http://localhost:9202', 'http://localhost:9203'], + }), + }) + ); + + const response = await handler( + {} as any, + { + headers: {}, + query: { method: 'get', path: 'test' }, + } as any, + kibanaResponseFactory + ); + + expect(response.status).toBe(502); + // Return the message from the ES node we attempted last. + expect(response.payload.message).toBe('ESOCKETTIMEDOUT'); + }); + }); +}); diff --git a/src/plugins/console/server/lib/spec_definitions/js/ingest.ts b/src/plugins/console/server/lib/spec_definitions/js/ingest.ts index 1182dc075f42f2..20dbeda5e0b3df 100644 --- a/src/plugins/console/server/lib/spec_definitions/js/ingest.ts +++ b/src/plugins/console/server/lib/spec_definitions/js/ingest.ts @@ -57,6 +57,49 @@ const bytesProcessorDefinition = { }, }; +// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/ingest-circle-processor.html +const circleProcessorDefinition = { + circle: { + __template: { + field: '', + error_distance: '', + shape_type: '', + }, + field: '', + target_field: '', + error_distance: '', + shape_type: { + __one_of: ['geo_shape', 'shape'], + }, + ignore_missing: { + __one_of: [false, true], + }, + ...commonPipelineParams, + }, +}; + +// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/csv-processor.html +const csvProcessorDefinition = { + csv: { + __template: { + field: '', + target_fields: [''], + }, + field: '', + target_fields: [''], + separator: '', + quote: '', + empty_value: '', + trim: { + __one_of: [true, false], + }, + ignore_missing: { + __one_of: [false, true], + }, + ...commonPipelineParams, + }, +}; + // Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/convert-processor.html const convertProcessorDefinition = { convert: { @@ -174,6 +217,25 @@ const foreachProcessorDefinition = { }, }; +// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/geoip-processor.html +const geoipProcessorDefinition = { + geoip: { + __template: { + field: '', + }, + field: '', + target_field: '', + database_file: '', + properties: [''], + ignore_missing: { + __one_of: [false, true], + }, + first_only: { + __one_of: [false, true], + }, + }, +}; + // Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/grok-processor.html const grokProcessorDefinition = { grok: { @@ -209,6 +271,37 @@ const gsubProcessorDefinition = { }, }; +// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/htmlstrip-processor.html +const htmlStripProcessorDefinition = { + html_strip: { + __template: { + field: '', + }, + field: '', + target_field: '', + ignore_missing: { + __one_of: [false, true], + }, + ...commonPipelineParams, + }, +}; + +// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/inference-processor.html +const inferenceProcessorDefinition = { + inference: { + __template: { + model_id: '', + field_map: {}, + inference_config: {}, + }, + model_id: '', + field_map: {}, + inference_config: {}, + target_field: '', + ...commonPipelineParams, + }, +}; + // Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/join-processor.html const joinProcessorDefinition = { join: { @@ -338,6 +431,18 @@ const setProcessorDefinition = { }, }; +// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/ingest-node-set-security-user-processor.html +const setSecurityUserProcessorDefinition = { + set_security_user: { + __template: { + field: '', + }, + field: '', + properties: [''], + ...commonPipelineParams, + }, +}; + // Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/split-processor.html const splitProcessorDefinition = { split: { @@ -394,10 +499,43 @@ const uppercaseProcessorDefinition = { }, }; +// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/urldecode-processor.html +const urlDecodeProcessorDefinition = { + urldecode: { + __template: { + field: '', + }, + field: '', + target_field: '', + ignore_missing: { + __one_of: [false, true], + }, + ...commonPipelineParams, + }, +}; + +// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/user-agent-processor.html +const userAgentProcessorDefinition = { + user_agent: { + __template: { + field: '', + }, + field: '', + target_field: '', + regex_file: '', + properties: [''], + ignore_missing: { + __one_of: [false, true], + }, + }, +}; + const processorDefinition = { __one_of: [ appendProcessorDefinition, bytesProcessorDefinition, + csvProcessorDefinition, + circleProcessorDefinition, convertProcessorDefinition, dateProcessorDefinition, dateIndexNameProcessorDefinition, @@ -406,8 +544,11 @@ const processorDefinition = { dropProcessorDefinition, failProcessorDefinition, foreachProcessorDefinition, + geoipProcessorDefinition, grokProcessorDefinition, gsubProcessorDefinition, + htmlStripProcessorDefinition, + inferenceProcessorDefinition, joinProcessorDefinition, jsonProcessorDefinition, kvProcessorDefinition, @@ -417,10 +558,13 @@ const processorDefinition = { renameProcessorDefinition, scriptProcessorDefinition, setProcessorDefinition, + setSecurityUserProcessorDefinition, splitProcessorDefinition, sortProcessorDefinition, trimProcessorDefinition, uppercaseProcessorDefinition, + urlDecodeProcessorDefinition, + userAgentProcessorDefinition, ], }; diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json index 408b01c4cb8f5b..0da2c130b47cf3 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json @@ -59,7 +59,61 @@ } }, "transient": { - "__scope_link": ".persistent" + "cluster": { + "routing": { + "allocation.enable": { + "__one_of": ["all", "primaries", "new_primaries", "none"] + }, + "allocation.disk.threshold_enabled": { "__one_of": [false, true] }, + "allocation.disk.watermark.low": "85%", + "allocation.disk.watermark.high": "90%", + "allocation.disk.reroute_interval": "60s", + "allocation.exclude": { + "_ip": "", + "_name": "", + "_host": "", + "_id": "" + }, + "allocation.include": { + "_ip": "", + "_name": "", + "_host": "", + "_id": "" + }, + "allocation.require": { + "_ip": "", + "_name": "", + "_host": "", + "_id": "" + }, + "allocation.awareness.attributes": [], + "allocation.awareness.force": { + "*": { + "values": [] + } + }, + "allocation.allow_rebalance": { + "__one_of": [ + "always", + "indices_primaries_active", + "indices_all_active" + ] + }, + "allocation.cluster_concurrent_rebalance": 2, + "allocation.node_initial_primaries_recoveries": 4, + "allocation.node_concurrent_recoveries": 2, + "allocation.same_shard.host": { "__one_of": [false, true] } + } + }, + "indices": { + "breaker": { + "total.limit": "70%", + "fielddata.limit": "60%", + "fielddata.overhead": 1.03, + "request.limit": "40%", + "request.overhead": 1.0 + } + } } } } diff --git a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts index 50a9fcf03c2093..9446289ff03ea1 100644 --- a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts +++ b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts @@ -175,10 +175,9 @@ export const createHandler = ({ break; } catch (e) { + // If we reached here it means we hit a lower level network issue than just, for e.g., a 500. + // We try contacting another node in that case. log.error(e); - if (e.code !== 'ECONNREFUSED') { - return response.internalError(e); - } if (idx === hosts.length - 1) { log.warn(`Could not connect to any configured ES node [${hosts.join(', ')}]`); return response.customError({ diff --git a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap index e71e4f1b15134a..7210879c5eacca 100644 --- a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap +++ b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap @@ -667,14 +667,13 @@ exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = `