diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy index 0d9f5c9d92453e..025836a90204c5 100644 --- a/.ci/end2end.groovy +++ b/.ci/end2end.groovy @@ -41,7 +41,7 @@ pipeline { // Filter when to run based on the below reasons: // - On a PRs when: // - There are changes related to the APM UI project - // - only when the owners of those changes are members of the apm-ui team (new filter) + // - only when the owners of those changes are members of the given GitHub teams // - On merges to branches when: // - There are changes related to the APM UI project // - FORCE parameter is set to true. @@ -51,7 +51,7 @@ pipeline { apm_updated = isGitRegionMatch(patterns: [ "^x-pack/plugins/apm/.*" ]) } if (isPR()) { - def isMember = isMemberOf(user: env.CHANGE_AUTHOR, team: 'apm-ui') + def isMember = isMemberOf(user: env.CHANGE_AUTHOR, team: ['apm-ui', 'uptime']) setEnvVar('RUN_APM_E2E', params.FORCE || (apm_updated && isMember)) } else { setEnvVar('RUN_APM_E2E', params.FORCE || apm_updated) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5efbaba32e00a8..37aba3ca10aa16 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -64,11 +64,12 @@ /src/apm.js @watson @vigneshshanmugam # Client Side Monitoring (lives in APM directories but owned by Uptime) -/x-pack/plugins/apm/e2e/cypress/support/step_definitions/rum @elastic/uptime +/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm @elastic/uptime /x-pack/plugins/apm/public/application/csmApp.tsx @elastic/uptime /x-pack/plugins/apm/public/components/app/RumDashboard @elastic/uptime /x-pack/plugins/apm/server/lib/rum_client @elastic/uptime /x-pack/plugins/apm/server/routes/rum_client.ts @elastic/uptime +/x-pack/plugins/apm/server/projections/rum_overview.ts @elastic/uptime # Beats /x-pack/legacy/plugins/beats_management/ @elastic/beats @@ -85,6 +86,7 @@ # Exclude tutorial resources folder for now because they are not owned by Kibana app and most will move out soon /src/legacy/core_plugins/kibana/public/home/*.ts @elastic/kibana-core-ui /src/legacy/core_plugins/kibana/public/home/np_ready/ @elastic/kibana-core-ui +/x-pack/plugins/global_search_bar/ @elastic/kibana-core-ui # Observability UIs /x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui @@ -152,6 +154,7 @@ /x-pack/plugins/cloud/ @elastic/kibana-platform /x-pack/test/saved_objects_field_count/ @elastic/kibana-platform /packages/kbn-config-schema/ @elastic/kibana-platform +/packages/kbn-std/ @elastic/kibana-platform /src/legacy/server/config/ @elastic/kibana-platform /src/legacy/server/http/ @elastic/kibana-platform /src/legacy/server/logging/ @elastic/kibana-platform @@ -293,6 +296,7 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib /x-pack/plugins/infra/**/*.scss @elastic/observability-design /x-pack/plugins/ingest_manager/**/*.scss @elastic/observability-design /x-pack/plugins/observability/**/*.scss @elastic/observability-design +/x-pack/plugins/monitoring/**/*.scss @elastic/observability-design # Ent. Search design /x-pack/plugins/enterprise_search/**/*.scss @elastic/ent-search-design diff --git a/docs/developer/architecture/code-exploration.asciidoc b/docs/developer/architecture/code-exploration.asciidoc new file mode 100644 index 00000000000000..4a390336da34fc --- /dev/null +++ b/docs/developer/architecture/code-exploration.asciidoc @@ -0,0 +1,598 @@ +//// + +NOTE: + This is an automatically generated file. Please do not edit directly. Instead, run the + following from within the kibana repository: + + node scripts/build_plugin_list_docs + + You can update the template within packages/kbn-dev-utils/target/plugin_list/generate_plugin_list.js + +//// + +[[code-exploration]] +== Exploring Kibana code + +The goals of our folder heirarchy are: + +- Easy for developers to know where to add new services, plugins and applications. +- Easy for developers to know where to find the code from services, plugins and applications. +- Easy to browse and understand our folder structure. + +To that aim, we strive to: + +- Avoid too many files in any given folder. +- Choose clear, unambigious folder names. +- Organize by domain. +- Every folder should contain a README that describes the contents of that folder. + +[discrete] +[[kibana-services-applications]] +=== Services and Applications + +[discrete] +==== src/plugins + +- {kib-repo}blob/{branch}/src/plugins/advanced_settings[advancedSettings] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/apm_oss[apmOss] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/bfetch/README.md[bfetch] + +bfetch allows to batch HTTP requests and streams responses back. + + +- {kib-repo}blob/{branch}/src/plugins/charts/README.md[charts] + +The Charts plugin is a way to create easier integration of shared colors, themes, types and other utilities across all Kibana charts and visualizations. + + +- {kib-repo}blob/{branch}/src/plugins/console[console] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/dashboard/README.md[dashboard] + +Contains the dashboard application. + + +- {kib-repo}blob/{branch}/src/plugins/data/README.md[data] + +data plugin provides common data access services. + + +- {kib-repo}blob/{branch}/src/plugins/dev_tools/README.md[devTools] + +The ui/registry/dev_tools is removed in favor of the devTools plugin which exposes a register method in the setup contract. +Registering app works mostly the same as registering apps in core.application.register. +Routing will be handled by the id of the dev tool - your dev tool will be mounted when the URL matches /app/dev_tools#/. +This API doesn't support angular, for registering angular dev tools, bootstrap a local module on mount into the given HTML element. + + +- {kib-repo}blob/{branch}/src/plugins/discover/README.md[discover] + +Contains the Discover application and the saved search embeddable. + + +- {kib-repo}blob/{branch}/src/plugins/embeddable/README.md[embeddable] + +Embeddables are re-usable widgets that can be rendered in any environment or plugin. Developers can embed them directly in their plugin. End users can dynamically add them to any embeddable containers. + + +- {kib-repo}blob/{branch}/src/plugins/es_ui_shared/README.md[esUiShared] + +This plugin contains reusable code in the form of self-contained modules (or libraries). Each of these modules exports a set of functionality relevant to the domain of the module. + + +- {kib-repo}blob/{branch}/src/plugins/expressions/README.md[expressions] + +This plugin provides methods which will parse & execute an expression pipeline +string for you, as well as a series of registries for advanced users who might +want to incorporate their own functions, types, and renderers into the service +for use in their own application. + + +- {kib-repo}blob/{branch}/src/plugins/home/README.md[home] + +Moves the legacy ui/registry/feature_catalogue module for registering "features" that should be shown in the home page's feature catalogue to a service within a "home" plugin. The feature catalogue refered to here should not be confused with the "feature" plugin for registering features used to derive UI capabilities for feature controls. + + +- {kib-repo}blob/{branch}/src/plugins/index_pattern_management[indexPatternManagement] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/input_control_vis/README.md[inputControlVis] + +Contains the input control visualization allowing to place custom filter controls on a dashboard. + + +- {kib-repo}blob/{branch}/src/plugins/inspector/README.md[inspector] + +The inspector is a contextual tool to gain insights into different elements +in Kibana, e.g. visualizations. It has the form of a flyout panel. + + +- {kib-repo}blob/{branch}/src/plugins/kibana_legacy/README.md[kibanaLegacy] + +This plugin will contain several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform. + + +- {kib-repo}blob/{branch}/src/plugins/kibana_react/README.md[kibanaReact] + +Tools for building React applications in Kibana. + + +- {kib-repo}blob/{branch}/src/plugins/kibana_usage_collection/README.md[kibanaUsageCollection] + +This plugin registers the basic usage collectors from Kibana: + + +- {kib-repo}blob/{branch}/src/plugins/kibana_utils/README.md[kibanaUtils] + +Utilities for building Kibana plugins. + + +- {kib-repo}blob/{branch}/src/plugins/legacy_export[legacyExport] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/management[management] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/maps_legacy[mapsLegacy] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/navigation/README.md[navigation] + +The navigation plugins exports the TopNavMenu component. +It also provides a stateful version of it on the start contract. + + +- {kib-repo}blob/{branch}/src/plugins/newsfeed[newsfeed] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/region_map[regionMap] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/saved_objects[savedObjects] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/saved_objects_management[savedObjectsManagement] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/share/README.md[share] + +Replaces the legacy ui/share module for registering share context menus. + + +- {kib-repo}blob/{branch}/src/plugins/telemetry/README.md[telemetry] + +Telemetry allows Kibana features to have usage tracked in the wild. The general term "telemetry" refers to multiple things: + + +- {kib-repo}blob/{branch}/src/plugins/telemetry_collection_manager/README.md[telemetryCollectionManager] + +Telemetry's collection manager to go through all the telemetry sources when fetching it before reporting. + + +- {kib-repo}blob/{branch}/src/plugins/telemetry_management_section/README.md[telemetryManagementSection] + +This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry). + + +- {kib-repo}blob/{branch}/src/plugins/tile_map[tileMap] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/src/plugins/timelion/README.md[timelion] + +Contains the deprecated timelion application. For the timelion visualization, +which also contains the timelion APIs and backend, look at the vis_type_timelion plugin. + + +- {kib-repo}blob/{branch}/src/plugins/ui_actions/README.md[uiActions] + +An API for: + + +- {kib-repo}blob/{branch}/src/plugins/usage_collection/README.md[usageCollection] + +Usage Collection allows collecting usage data for other services to consume (telemetry and monitoring). +To integrate with the telemetry services for usage collection of your feature, there are 2 steps: + + +- {kib-repo}blob/{branch}/src/plugins/vis_type_markdown/README.md[visTypeMarkdown] + +The markdown visualization that can be used to place text panels on dashboards. + + +- {kib-repo}blob/{branch}/src/plugins/vis_type_metric/README.md[visTypeMetric] + +Contains the metric visualization. + + +- {kib-repo}blob/{branch}/src/plugins/vis_type_table/README.md[visTypeTable] + +Contains the data table visualization, that allows presenting data in a simple table format. + + +- {kib-repo}blob/{branch}/src/plugins/vis_type_tagcloud/README.md[visTypeTagcloud] + +Contains the tagcloud visualization. + + +- {kib-repo}blob/{branch}/src/plugins/vis_type_timelion/README.md[visTypeTimelion] + +Contains the timelion visualization and the timelion backend. + + +- {kib-repo}blob/{branch}/src/plugins/vis_type_timeseries/README.md[visTypeTimeseries] + +Contains everything around TSVB (the editor, visualizatin implementations and backends). + + +- {kib-repo}blob/{branch}/src/plugins/vis_type_vega/README.md[visTypeVega] + +Contains the Vega visualization. + + +- {kib-repo}blob/{branch}/src/plugins/vis_type_vislib/README.md[visTypeVislib] + +Contains the vislib visualizations. These are the classical area/line/bar, pie, gauge/goal and +heatmap charts. + + +- {kib-repo}blob/{branch}/src/plugins/vis_type_xy/README.md[visTypeXy] + +Contains the new xy-axis chart using the elastic-charts library, which will eventually +replace the vislib xy-axis (bar, area, line) charts. + + +- {kib-repo}blob/{branch}/src/plugins/visualizations/README.md[visualizations] + +Contains most of the visualization infrastructure, e.g. the visualization type registry or the +visualization embeddable. + + +- {kib-repo}blob/{branch}/src/plugins/visualize/README.md[visualize] + +Contains the visualize application which includes the listing page and the app frame, +which will load the visualization's editor. + + +[discrete] +==== x-pack/plugins + +- {kib-repo}blob/{branch}/x-pack/plugins/actions/README.md[actions] + +The Kibana actions plugin provides a framework to create executable actions. You can: + + +- {kib-repo}blob/{branch}/x-pack/plugins/alerting_builtins/README.md[alertingBuiltins] + +This plugin provides alertTypes shipped with Kibana for use with the +the alerts plugin. When enabled, it will register +the built-in alertTypes with the alerting plugin, register associated HTTP +routes, etc. + + +- {kib-repo}blob/{branch}/x-pack/plugins/alerts/README.md[alerts] + +The Kibana alerting plugin provides a common place to set up alerts. You can: + + +- {kib-repo}blob/{branch}/x-pack/plugins/apm/readme.md[apm] + +To access an elasticsearch instance that has live data you have two options: + + +- {kib-repo}blob/{branch}/x-pack/plugins/audit_trail[auditTrail] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/beats_management/readme.md[beatsManagement] + +Notes: +Failure to have auth enabled in Kibana will make for a broken UI. UI-based errors not yet in place + + +- {kib-repo}blob/{branch}/x-pack/plugins/canvas/README.md[canvas] + +"Never look back. The past is done. The future is a blank canvas." ― Suzy Kassem, Rise Up and Salute the Sun + + +- {kib-repo}blob/{branch}/x-pack/plugins/case/README.md[case] + +Experimental Feature + + +- {kib-repo}blob/{branch}/x-pack/plugins/cloud[cloud] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/code[code] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/console_extensions[consoleExtensions] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/cross_cluster_replication/README.md[crossClusterReplication] + +You can run a local cluster and simulate a remote cluster within a single Kibana directory. + + +- {kib-repo}blob/{branch}/x-pack/plugins/dashboard_enhanced/README.md[dashboardEnhanced] + +Contains the enhancements to the OSS dashboard app. + + +- {kib-repo}blob/{branch}/x-pack/plugins/dashboard_mode/README.md[dashboardMode] + +The deprecated dashboard only mode. + + +- {kib-repo}blob/{branch}/x-pack/plugins/data_enhanced[dataEnhanced] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/discover_enhanced/README.md[discoverEnhanced] + +Contains the enhancements to the OSS discover app. + + +- {kib-repo}blob/{branch}/x-pack/plugins/embeddable_enhanced[embeddableEnhanced] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/encrypted_saved_objects/README.md[encryptedSavedObjects] + +The purpose of this plugin is to provide a way to encrypt/decrypt attributes on the custom Saved Objects that works with +security and spaces filtering as well as performing audit logging. + + +- {kib-repo}blob/{branch}/x-pack/plugins/enterprise_search/README.md[enterpriseSearch] + +This plugin's goal is to provide a Kibana user interface to the Enterprise Search solution's products (App Search and Workplace Search). In it's current MVP state, the plugin provides the following with the goal of gathering user feedback and raising product awareness: + + +- {kib-repo}blob/{branch}/x-pack/plugins/event_log/README.md[eventLog] + +The purpose of this plugin is to provide a way to persist a history of events +occuring in Kibana, initially just for the Make It Action project - alerts +and actions. + + +- {kib-repo}blob/{branch}/x-pack/plugins/features[features] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/file_upload[fileUpload] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/global_search/README.md[globalSearch] + +The GlobalSearch plugin provides an easy way to search for various objects, such as applications +or dashboards from the Kibana instance, from both server and client-side plugins + + +- {kib-repo}blob/{branch}/x-pack/plugins/global_search_bar/README.md[globalSearchBar] + +The GlobalSearchBar plugin provides a search interface for navigating Kibana. (It is the UI to the GlobalSearch plugin.) + + +- {kib-repo}blob/{branch}/x-pack/plugins/global_search_providers[globalSearchProviders] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/graph/README.md[graph] + +This is the main source folder of the Graph plugin. It contains all of the Kibana server and client source code. x-pack/test/functional/apps/graph contains additional functional tests. + + +- {kib-repo}blob/{branch}/x-pack/plugins/grokdebugger/README.md[grokdebugger] + +- {kib-repo}blob/{branch}/x-pack/plugins/index_lifecycle_management/README.md[indexLifecycleManagement] + +You can test that the Frozen badge, phase filtering, and lifecycle information is surfaced in +Index Management by running this series of requests in Console: + + +- {kib-repo}blob/{branch}/x-pack/plugins/index_management[indexManagement] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/infra/README.md[infra] + +This is the home of the infra plugin, which aims to provide a solution for +the infrastructure monitoring use-case within Kibana. + + +- {kib-repo}blob/{branch}/x-pack/plugins/ingest_manager/README.md[ingestManager] + +Fleet needs to have Elasticsearch API keys enabled, and also to have TLS enabled on kibana, (if you want to run Kibana without TLS you can provide the following config flag --xpack.ingestManager.fleet.tlsCheckDisabled=false) + + +- {kib-repo}blob/{branch}/x-pack/plugins/ingest_pipelines/README.md[ingestPipelines] + +The ingest_pipelines plugin provides Kibana support for Elasticsearch's ingest nodes. Please refer to the Elasticsearch documentation for more details. + + +- {kib-repo}blob/{branch}/x-pack/plugins/lens/readme.md[lens] + +Run all tests from the x-pack root directory + + +- {kib-repo}blob/{branch}/x-pack/plugins/license_management[licenseManagement] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/licensing/README.md[licensing] + +The licensing plugin retrieves license data from Elasticsearch at regular configurable intervals. + + +- {kib-repo}blob/{branch}/x-pack/plugins/lists/README.md[lists] + +README.md for developers working on the backend lists on how to get started +using the CURL scripts in the scripts folder. + + +- {kib-repo}blob/{branch}/x-pack/plugins/logstash[logstash] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/maps/README.md[maps] + +Visualize geo data from Elasticsearch or 3rd party geo-services. + + +- {kib-repo}blob/{branch}/x-pack/plugins/maps_legacy_licensing/README.md[mapsLegacyLicensing] + +This plugin provides access to the detailed tile map services from Elastic. + + +- {kib-repo}blob/{branch}/x-pack/plugins/ml[ml] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/monitoring[monitoring] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/observability/README.md[observability] + +This plugin provides shared components and services for use across observability solutions, as well as the observability landing page UI. + + +- {kib-repo}blob/{branch}/x-pack/plugins/oss_telemetry[ossTelemetry] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/painless_lab[painlessLab] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/remote_clusters[remoteClusters] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/reporting/README.md[reporting] + +An awesome Kibana reporting plugin + + +- {kib-repo}blob/{branch}/x-pack/plugins/rollup/README.md[rollup] + +Welcome to the Kibana rollup plugin! This plugin provides Kibana support for Elasticsearch's rollup feature. Please refer to the Elasticsearch documentation to understand rollup indices and how to create rollup jobs. + + +- {kib-repo}blob/{branch}/x-pack/plugins/searchprofiler[searchprofiler] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/security/README.md[security] + +See Configuring security in Kibana. + + +- {kib-repo}blob/{branch}/x-pack/plugins/security_solution/README.md[securitySolution] + +Welcome to the Kibana Security Solution plugin! This README will go over getting started with development and testing. + + +- {kib-repo}blob/{branch}/x-pack/plugins/snapshot_restore/README.md[snapshotRestore] + +or + + +- {kib-repo}blob/{branch}/x-pack/plugins/spaces[spaces] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/task_manager[taskManager] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/telemetry_collection_xpack/README.md[telemetryCollectionXpack] + +Gathers all usage collection, retrieving them from both: OSS and X-Pack plugins. + + +- {kib-repo}blob/{branch}/x-pack/plugins/transform[transform] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/translations[translations] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/triggers_actions_ui/README.md[triggers_actions_ui] + +The Kibana alerts and actions UI plugin provides a user interface for managing alerts and actions. +As a developer you can reuse and extend built-in alerts and actions UI functionality: + + +- {kib-repo}blob/{branch}/x-pack/plugins/ui_actions_enhanced/README.md[uiActionsEnhanced] + +- {kib-repo}blob/{branch}/x-pack/plugins/upgrade_assistant[upgradeAssistant] + +WARNING: Missing README. + + +- {kib-repo}blob/{branch}/x-pack/plugins/uptime/README.md[uptime] + +The purpose of this plugin is to provide users of Heartbeat more visibility of what's happening +in their infrastructure. + + +- {kib-repo}blob/{branch}/x-pack/plugins/watcher/README.md[watcher] + +This plugins adopts some conventions in addition to or in place of conventions in Kibana (at the time of the plugin's creation): + diff --git a/docs/developer/best-practices/images/state_inside_the_link.png b/docs/developer/best-practices/images/state_inside_the_link.png new file mode 100644 index 00000000000000..833478ccbda687 Binary files /dev/null and b/docs/developer/best-practices/images/state_inside_the_link.png differ diff --git a/docs/developer/best-practices/index.asciidoc b/docs/developer/best-practices/index.asciidoc index 42cee6ef0e58a7..13ea010d0aa96a 100644 --- a/docs/developer/best-practices/index.asciidoc +++ b/docs/developer/best-practices/index.asciidoc @@ -122,6 +122,14 @@ In addition, if users are relying on state stored in your app’s URL as part of your public contract, keep in mind that you may also need to provide backwards compatibility for bookmarked URLs. +[discrete] +=== Routing, Navigation and URL + +The {kib} platform provides a set of tools to help developers build consistent experience around routing and browser navigation. +Some of that tooling is inside `core`, some is available as part of various plugins. + +<> to get an idea of available tools and common approaches for handling routing and browser navigation. + [discrete] === Testing & stability @@ -131,6 +139,8 @@ Review: * <> * <> +include::navigation.asciidoc[leveloffset=+1] + include::stability.asciidoc[leveloffset=+1] include::security.asciidoc[leveloffset=+1] diff --git a/docs/developer/best-practices/navigation.asciidoc b/docs/developer/best-practices/navigation.asciidoc new file mode 100644 index 00000000000000..d01f2c2aa0f950 --- /dev/null +++ b/docs/developer/best-practices/navigation.asciidoc @@ -0,0 +1,226 @@ +[[kibana-navigation]] +== Routing, Navigation and URL + +The {kib} platform provides a set of tools to help developers build consistent experience around routing and browser navigation. +Some of that tooling is inside `core`, some is available as part of various plugins. + +The purpose of this guide is to give a high-level overview of available tools and to explain common approaches for handling routing and browser navigation. + +This guide covers following topics: + +* <> +* <> +* <> +* <> +* <> +* <> + +[[deep-linking]] +=== Deep-linking into {kib} apps + +Assuming you want to link from your app to *Discover*. When building such URL there are two things to consider: + +1. Prepending a proper `basePath`. +2. Specifying *Discover* state. + +==== Prepending a proper `basePath` + +To prepend {kib}'s `basePath` use {kib-repo}tree/{branch}/docs/development/core/public/kibana-plugin-core-public.ibasepath.prepend.md[core.http.basePath.prepend] helper: + +[source,typescript jsx] +---- +const discoverUrl = core.http.basePath.prepend(`/discover`); + +console.log(discoverUrl); // http://localhost:5601/bpr/s/space/app/discover +---- + +==== Specifying state + +**Consider a {kib} app URL a part of app's plugin contract:** + +. Avoid hardcoding other app's URL in your app's code. +. Avoid generating other app's state and serializing it into URL query params. + +[source,typescript jsx] +---- +// Avoid relying on other app's state structure in your app's code: +const discoverUrlWithSomeState = core.http.basePath.prepend(`/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'2020-09-10T11:39:50.203Z',to:'2020-09-10T11:40:20.249Z'))&_a=(columns:!(_source),filters:!(),index:'90943e30-9a47-11e8-b64d-95841ca0b247',interval:auto,query:(language:kuery,query:''),sort:!())`); +---- + +Instead, each app should expose {kib-repo}tree/{branch}/src/plugins/share/public/url_generators/README.md[a URL generator]. +Other apps should use those URL generators for creating URLs. + +[source,typescript jsx] +---- +// Properly generated URL to *Discover* app. Generator code is owned by *Discover* app and available on *Discover*'s plugin contract. +const discoverUrl = discoverUrlGenerator.createUrl({filters, timeRange}); +---- + +To get a better idea, take a look at *Discover* URL generator {kib-repo}tree/{branch}/src/plugins/discover/public/url_generator.ts[implementation]. +It allows specifying various **Discover** app state pieces like: index pattern, filters, query, time range and more. + +There are two ways to access other's app URL generator in your code: + +1. From a plugin contract of a destination app *(preferred)*. +2. Using URL generator service instance on `share` plugin contract (in case an explicit plugin dependency is not possible). + +In case you want other apps to link to your app, then you should create a URL generator and expose it on your plugin's contract. + + +[[navigating-between-kibana-apps]] +=== Navigating between {kib} apps + +{kib} is a single page application and there is a set of simple rules developers should follow +to make sure there is no page reload when navigating from one place in {kib} to another. + +For example, navigation using native browser APIs would cause a full page reload. + +[source,js] +---- +const urlToADashboard = core.http.basePath.prepend(`/dashboard/my-dashboard`); + +// this would cause a full page reload: +window.location.href = urlToADashboard; +---- + +To navigate between different {kib} apps without a page reload there are APIs in `core`: + +* {kib-repo}tree/{branch}/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetoapp.md[core.application.navigateToApp] +* {kib-repo}tree/{branch}/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetourl.md[core.application.navigateToUrl] + +*Rendering a link to a different {kib} app on its own would also cause a full page reload:* + +[source,typescript jsx] +---- +const myLink = () => + Go to Dashboard; +---- + +A workaround could be to handle a click, prevent browser navigation and use `core.application.navigateToApp` API: + +[source,typescript jsx] +---- +const MySPALink = () => + { + e.preventDefault(); + core.application.navigateToApp('dashboard', { path: '/my-dashboard' }); + }} + > + Go to Dashboard + ; +---- + +As it would be too much boilerplate to do this for each {kib} link in your app, there is a handy wrapper that helps with it: +{kib-repo}tree/{branch}/src/plugins/kibana_react/public/app_links/redirect_app_link.tsx#L49[RedirectAppLinks]. + +[source,typescript jsx] +---- +const MyApp = () => + + {/*...*/} + {/* navigations using this link will happen in SPA friendly way */} + Go to Dashboard + {/*...*/} + +---- + +[[routing]] +=== Setting up internal app routing + +It is very common for {kib} apps to use React and React Router. +Common rules to follow in this scenario: + +* Set up `BrowserRouter` and not `HashRouter`. +* *Initialize your router with `history` instance provided by the `core`.* + +This is required to make sure `core` is aware of navigations triggered inside your app, so it could act accordingly when needed. + +* `Core`'s {kib-repo}tree/{branch}/docs/development/core/public/kibana-plugin-core-public.scopedhistory.md[ScopedHistory] instance. +* {kib-repo}tree/{branch}/docs/development/core/public/kibana-plugin-core-public.appmountparameters.history.md[Example usage] +* {kib-repo}tree/{branch}/test/plugin_functional/plugins/core_plugin_a/public/application.tsx#L120[Example plugin] + +Relative links will be resolved relative to your app's route (e.g.: `http://localhost5601/app/{your-app-id}`) +and setting up internal links in your app in SPA friendly way would look something like: + +[source,typescript jsx] +---- +import {Link} from 'react-router-dom'; + +const MyInternalLink = () => +---- + +[[history-and-location]] +=== Using history and browser location + +Try to avoid using `window.location` and `window.history` directly. + +Instead, consider using {kib-repo}tree/{branch}/docs/development/core/public/kibana-plugin-core-public.scopedhistory.md[ScopedHistory] +instance provided by `core`. + +* This way `core` will know about location changes triggered within your app, and it would act accordingly. +* Some plugins are listening to location changes. Triggering location change manually could lead to unpredictable and hard-to-catch bugs. + +Common use-case for using +`core`'s {kib-repo}tree/{branch}/docs/development/core/public/kibana-plugin-core-public.scopedhistory.md[ScopedHistory] directly: + +* Reading/writing query params or hash. +* Imperatively triggering internal navigations within your app. +* Listening to browser location changes. + + +[[state-sync]] +=== Syncing state with URL + +Historically {kib} apps store _a lot_ of application state in the URL. +The most common pattern that {kib} apps follow today is storing state in `_a` and `_g` query params in https://github.com/w33ble/rison-node#readme[rison] format. +[[query-params]] +Those query params follow the convention: + +* `_g` (*global*) - global UI state that should be shared and synced across multiple apps. common example from Analyze group apps: time range, refresh interval, *pinned* filters. +* `_a` (*application*) - UI state scoped to current app. + +NOTE: After migrating to KP platform we got navigations without page reloads. Since then there is no real need to follow `_g` and `_a` separation anymore. It's up you to decide if you want to follow this pattern or if you prefer a single query param or something else. The need for this separation earlier is explained in <>. + +There are utils to help you to implement such kind of state syncing. + +**When you should consider using state syncing utils:** + +* You want to sync your application state with URL in similar manner Analyze group applications do. +* You want to follow platform's <> out of the box. +* You want to support `state:storeInSessionStore` escape hatch for URL overflowing out of the box. +* You should also consider using them if you'd like to serialize state to different (not `rison`) format. Utils are composable, and you can implement your own `storage`. +* In case you want to sync part of your state with URL, but other part of it with browser storage. + +**When you shouldn't use state syncing utils:** + +* Adding a query param flag or simple key/value to the URL. + +Follow {kib-repo}tree/{branch}/src/plugins/kibana_utils/docs/state_sync#state-syncing-utilities[these] docs to learn more. + + +[[preserve-state]] +=== Preserving state between navigations + +Consider the scenario: + +1. You are in *Dashboard* app looking at a dashboard with some filters applied; +2. Navigate to *Discover* using in-app navigation; +3. Change the time filter' +4. Navigate to *Dashboard* using in-app navigation. + +You'd notice that you were navigated to *Dashboard* app with the *same state* that you left it with, +except that the time filter has changed to the one you applied on *Discover* app. + +Historically {kib} Analyze groups apps achieve that behavior relying on state in the URL. +If you'd have a closer look on a link in the navigation, +you'd notice that state is stored inside that link, and it also gets updated whenever relevant state changes happen: + +[role="screenshot"] +image:images/state_inside_the_link.png[State is stored inside the navigation link] + +This is where <> into `_a` and `_g` query params comes into play. What is considered a *global* state gets constantly updated in those navigation links. In the example above it was a time filter. +This is backed by {kib-repo}tree/{branch}/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts#L57[KbnUrlTracker] util. You can use it to achieve similar behavior. + +NOTE: After migrating to KP navigation works without page reloads and all plugins are loaded simultaneously. +Hence, likely there are simpler ways to preserve state of your application, unless you want to do it through URL. diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 275fdf8fb69ad6..7727cd322181fa 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -341,6 +341,10 @@ and actions. or dashboards from the Kibana instance, from both server and client-side plugins +|{kib-repo}blob/{branch}/x-pack/plugins/global_search_bar/README.md[globalSearchBar] +|The GlobalSearchBar plugin provides a search interface for navigating Kibana. (It is the UI to the GlobalSearch plugin.) + + |{kib-repo}blob/{branch}/x-pack/plugins/global_search_providers[globalSearchProviders] |WARNING: Missing README. diff --git a/docs/development/core/public/kibana-plugin-core-public.assertnever.md b/docs/development/core/public/kibana-plugin-core-public.assertnever.md deleted file mode 100644 index 8fefd4450d49b1..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.assertnever.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [assertNever](./kibana-plugin-core-public.assertnever.md) - -## assertNever() function - -Can be used in switch statements to ensure we perform exhaustive checks, see https://www.typescriptlang.org/docs/handbook/advanced-types.html\#exhaustiveness-checking - -Signature: - -```typescript -export declare function assertNever(x: never): never; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | never | | - -Returns: - -`never` - diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.md b/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.md index bca69adeef66b0..47365782599eda 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.md @@ -30,6 +30,7 @@ chrome.navControls.registerLeft({ | Method | Description | | --- | --- | -| [registerLeft(navControl)](./kibana-plugin-core-public.chromenavcontrols.registerleft.md) | Register a nav control to be presented on the left side of the chrome header. | -| [registerRight(navControl)](./kibana-plugin-core-public.chromenavcontrols.registerright.md) | Register a nav control to be presented on the right side of the chrome header. | +| [registerCenter(navControl)](./kibana-plugin-core-public.chromenavcontrols.registercenter.md) | Register a nav control to be presented on the top-center side of the chrome header. | +| [registerLeft(navControl)](./kibana-plugin-core-public.chromenavcontrols.registerleft.md) | Register a nav control to be presented on the bottom-left side of the chrome header. | +| [registerRight(navControl)](./kibana-plugin-core-public.chromenavcontrols.registerright.md) | Register a nav control to be presented on the top-right side of the chrome header. | diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registercenter.md b/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registercenter.md new file mode 100644 index 00000000000000..2f921050e58dd3 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registercenter.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeNavControls](./kibana-plugin-core-public.chromenavcontrols.md) > [registerCenter](./kibana-plugin-core-public.chromenavcontrols.registercenter.md) + +## ChromeNavControls.registerCenter() method + +Register a nav control to be presented on the top-center side of the chrome header. + +Signature: + +```typescript +registerCenter(navControl: ChromeNavControl): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| navControl | ChromeNavControl | | + +Returns: + +`void` + diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registerleft.md b/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registerleft.md index c5c78bf9fb1da9..514c44bd9d7102 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registerleft.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registerleft.md @@ -4,7 +4,7 @@ ## ChromeNavControls.registerLeft() method -Register a nav control to be presented on the left side of the chrome header. +Register a nav control to be presented on the bottom-left side of the chrome header. Signature: diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registerright.md b/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registerright.md index 12058f1d16ab98..eb56e0e38c6c9e 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registerright.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromenavcontrols.registerright.md @@ -4,7 +4,7 @@ ## ChromeNavControls.registerRight() method -Register a nav control to be presented on the right side of the chrome header. +Register a nav control to be presented on the top-right side of the chrome header. Signature: diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.getnavtype_.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.getnavtype_.md deleted file mode 100644 index 09864be43996da..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.chromestart.getnavtype_.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [getNavType$](./kibana-plugin-core-public.chromestart.getnavtype_.md) - -## ChromeStart.getNavType$() method - -Get the navigation type TODO \#64541 Can delete - -Signature: - -```typescript -getNavType$(): Observable; -``` -Returns: - -`Observable` - diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.md index e983ad50d2afe5..2594848ef0847f 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromestart.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromestart.md @@ -59,7 +59,6 @@ core.chrome.setHelpExtension(elem => { | [getHelpExtension$()](./kibana-plugin-core-public.chromestart.gethelpextension_.md) | Get an observable of the current custom help conttent | | [getIsNavDrawerLocked$()](./kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md) | Get an observable of the current locked state of the nav drawer. | | [getIsVisible$()](./kibana-plugin-core-public.chromestart.getisvisible_.md) | Get an observable of the current visibility state of the chrome. | -| [getNavType$()](./kibana-plugin-core-public.chromestart.getnavtype_.md) | Get the navigation type TODO \#64541 Can delete | | [removeApplicationClass(className)](./kibana-plugin-core-public.chromestart.removeapplicationclass.md) | Remove a className added with addApplicationClass(). If className is unknown it is ignored. | | [setAppTitle(appTitle)](./kibana-plugin-core-public.chromestart.setapptitle.md) | Sets the current app's title | | [setBadge(badge)](./kibana-plugin-core-public.chromestart.setbadge.md) | Override the current badge | diff --git a/docs/development/core/public/kibana-plugin-core-public.deepfreeze.md b/docs/development/core/public/kibana-plugin-core-public.deepfreeze.md deleted file mode 100644 index 7c879b659a8524..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.deepfreeze.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [deepFreeze](./kibana-plugin-core-public.deepfreeze.md) - -## deepFreeze() function - -Apply Object.freeze to a value recursively and convert the return type to Readonly variant recursively - -Signature: - -```typescript -export declare function deepFreeze(object: T): RecursiveReadonly; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| object | T | | - -Returns: - -`RecursiveReadonly` - diff --git a/docs/development/core/public/kibana-plugin-core-public.freezable.md b/docs/development/core/public/kibana-plugin-core-public.freezable.md deleted file mode 100644 index fee87dde25c287..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.freezable.md +++ /dev/null @@ -1,14 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [Freezable](./kibana-plugin-core-public.freezable.md) - -## Freezable type - - -Signature: - -```typescript -export declare type Freezable = { - [k: string]: any; -} | any[]; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.getflattenedobject.md b/docs/development/core/public/kibana-plugin-core-public.getflattenedobject.md deleted file mode 100644 index 3ef9b6bf703eb9..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.getflattenedobject.md +++ /dev/null @@ -1,30 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [getFlattenedObject](./kibana-plugin-core-public.getflattenedobject.md) - -## getFlattenedObject() function - -Flattens a deeply nested object to a map of dot-separated paths pointing to all primitive values \*\*and arrays\*\* from `rootValue`. - -example: getFlattenedObject({ a: { b: 1, c: \[2,3\] } }) // => { 'a.b': 1, 'a.c': \[2,3\] } - -Signature: - -```typescript -export declare function getFlattenedObject(rootValue: Record): { - [key: string]: any; -}; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| rootValue | Record<string, any> | | - -Returns: - -`{ - [key: string]: any; -}` - diff --git a/docs/development/core/public/kibana-plugin-core-public.isrelativeurl.md b/docs/development/core/public/kibana-plugin-core-public.isrelativeurl.md deleted file mode 100644 index 3c2ffa6340a977..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.isrelativeurl.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [isRelativeUrl](./kibana-plugin-core-public.isrelativeurl.md) - -## isRelativeUrl() function - -Determine if a url is relative. Any url including a protocol, hostname, or port is not considered relative. This means that absolute \*paths\* are considered to be relative \*urls\* - -Signature: - -```typescript -export declare function isRelativeUrl(candidatePath: string): boolean; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| candidatePath | string | | - -Returns: - -`boolean` - diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index 08b12190ef6383..f2bf72a597656c 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -27,16 +27,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AppNavLinkStatus](./kibana-plugin-core-public.appnavlinkstatus.md) | Status of the application's navLink. | | [AppStatus](./kibana-plugin-core-public.appstatus.md) | Accessibility status of an application. | -## Functions - -| Function | Description | -| --- | --- | -| [assertNever(x)](./kibana-plugin-core-public.assertnever.md) | Can be used in switch statements to ensure we perform exhaustive checks, see https://www.typescriptlang.org/docs/handbook/advanced-types.html\#exhaustiveness-checking | -| [deepFreeze(object)](./kibana-plugin-core-public.deepfreeze.md) | Apply Object.freeze to a value recursively and convert the return type to Readonly variant recursively | -| [getFlattenedObject(rootValue)](./kibana-plugin-core-public.getflattenedobject.md) | Flattens a deeply nested object to a map of dot-separated paths pointing to all primitive values \*\*and arrays\*\* from rootValue.example: getFlattenedObject({ a: { b: 1, c: \[2,3\] } }) // => { 'a.b': 1, 'a.c': \[2,3\] } | -| [isRelativeUrl(candidatePath)](./kibana-plugin-core-public.isrelativeurl.md) | Determine if a url is relative. Any url including a protocol, hostname, or port is not considered relative. This means that absolute \*paths\* are considered to be relative \*urls\* | -| [modifyUrl(url, urlModifier)](./kibana-plugin-core-public.modifyurl.md) | Takes a URL and a function that takes the meaningful parts of the URL as a key-value object, modifies some or all of the parts, and returns the modified parts formatted again as a url.Url Parts sent: - protocol - slashes (does the url have the //) - auth - hostname (just the name of the host, no port or auth information) - port - pathname (the path after the hostname, no query or hash, starts with a slash if there was a path) - query (always an object, even when no query on original url) - hashWhy? - The default url library in node produces several conflicting properties on the "parsed" output. Modifying any of these might lead to the modifications being ignored (depending on which property was modified) - It's not always clear whether to use path/pathname, host/hostname, so this tries to add helpful constraints | - ## Interfaces | Interface | Description | @@ -128,7 +118,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ToastOptions](./kibana-plugin-core-public.toastoptions.md) | Options available for [IToasts](./kibana-plugin-core-public.itoasts.md) APIs. | | [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) | UiSettings parameters defined by the plugins. | | [UiSettingsState](./kibana-plugin-core-public.uisettingsstate.md) | | -| [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) | We define our own typings because the current version of @types/node declares properties to be optional "hostname?: string". Although, parse call returns "hostname: null \| string". | | [UserProvidedValues](./kibana-plugin-core-public.userprovidedvalues.md) | Describes the values explicitly set by user. | ## Variables @@ -156,7 +145,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ChromeHelpExtensionMenuLink](./kibana-plugin-core-public.chromehelpextensionmenulink.md) | | | [ChromeNavLinkUpdateableFields](./kibana-plugin-core-public.chromenavlinkupdateablefields.md) | | | [FatalErrorsStart](./kibana-plugin-core-public.fatalerrorsstart.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. | -| [Freezable](./kibana-plugin-core-public.freezable.md) | | | [HandlerContextType](./kibana-plugin-core-public.handlercontexttype.md) | Extracts the type of the first argument of a [HandlerFunction](./kibana-plugin-core-public.handlerfunction.md) to represent the type of the context. | | [HandlerFunction](./kibana-plugin-core-public.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-public.icontextcontainer.md) | | [HandlerParameters](./kibana-plugin-core-public.handlerparameters.md) | Extracts the types of the additional arguments of a [HandlerFunction](./kibana-plugin-core-public.handlerfunction.md), excluding the [HandlerContextType](./kibana-plugin-core-public.handlercontexttype.md). | diff --git a/docs/development/core/public/kibana-plugin-core-public.modifyurl.md b/docs/development/core/public/kibana-plugin-core-public.modifyurl.md deleted file mode 100644 index b174f733a5c646..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.modifyurl.md +++ /dev/null @@ -1,31 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [modifyUrl](./kibana-plugin-core-public.modifyurl.md) - -## modifyUrl() function - -Takes a URL and a function that takes the meaningful parts of the URL as a key-value object, modifies some or all of the parts, and returns the modified parts formatted again as a url. - -Url Parts sent: - protocol - slashes (does the url have the //) - auth - hostname (just the name of the host, no port or auth information) - port - pathname (the path after the hostname, no query or hash, starts with a slash if there was a path) - query (always an object, even when no query on original url) - hash - -Why? - The default url library in node produces several conflicting properties on the "parsed" output. Modifying any of these might lead to the modifications being ignored (depending on which property was modified) - It's not always clear whether to use path/pathname, host/hostname, so this tries to add helpful constraints - -Signature: - -```typescript -export declare function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulParts) => Partial | void): string; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| url | string | | -| urlModifier | (urlParts: URLMeaningfulParts) => Partial<URLMeaningfulParts> | void | | - -Returns: - -`string` - -The modified and reformatted url - diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.auth.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.auth.md deleted file mode 100644 index 238dd668858969..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.auth.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [auth](./kibana-plugin-core-public.urlmeaningfulparts.auth.md) - -## URLMeaningfulParts.auth property - -Signature: - -```typescript -auth?: string | null; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hash.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hash.md deleted file mode 100644 index 161e7dc7ebfae9..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hash.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [hash](./kibana-plugin-core-public.urlmeaningfulparts.hash.md) - -## URLMeaningfulParts.hash property - -Signature: - -```typescript -hash?: string | null; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hostname.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hostname.md deleted file mode 100644 index f1884718337b5f..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.hostname.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [hostname](./kibana-plugin-core-public.urlmeaningfulparts.hostname.md) - -## URLMeaningfulParts.hostname property - -Signature: - -```typescript -hostname?: string | null; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.md deleted file mode 100644 index 2816d4c7df5413..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.md +++ /dev/null @@ -1,27 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) - -## URLMeaningfulParts interface - -We define our own typings because the current version of @types/node declares properties to be optional "hostname?: string". Although, parse call returns "hostname: null \| string". - -Signature: - -```typescript -export interface URLMeaningfulParts -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [auth](./kibana-plugin-core-public.urlmeaningfulparts.auth.md) | string | null | | -| [hash](./kibana-plugin-core-public.urlmeaningfulparts.hash.md) | string | null | | -| [hostname](./kibana-plugin-core-public.urlmeaningfulparts.hostname.md) | string | null | | -| [pathname](./kibana-plugin-core-public.urlmeaningfulparts.pathname.md) | string | null | | -| [port](./kibana-plugin-core-public.urlmeaningfulparts.port.md) | string | null | | -| [protocol](./kibana-plugin-core-public.urlmeaningfulparts.protocol.md) | string | null | | -| [query](./kibana-plugin-core-public.urlmeaningfulparts.query.md) | ParsedQuery | | -| [slashes](./kibana-plugin-core-public.urlmeaningfulparts.slashes.md) | boolean | null | | - diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.pathname.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.pathname.md deleted file mode 100644 index 5ad21f004481ce..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.pathname.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [pathname](./kibana-plugin-core-public.urlmeaningfulparts.pathname.md) - -## URLMeaningfulParts.pathname property - -Signature: - -```typescript -pathname?: string | null; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.port.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.port.md deleted file mode 100644 index 2e70da2f17421c..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.port.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [port](./kibana-plugin-core-public.urlmeaningfulparts.port.md) - -## URLMeaningfulParts.port property - -Signature: - -```typescript -port?: string | null; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.protocol.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.protocol.md deleted file mode 100644 index cedc7f0b878e37..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.protocol.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [protocol](./kibana-plugin-core-public.urlmeaningfulparts.protocol.md) - -## URLMeaningfulParts.protocol property - -Signature: - -```typescript -protocol?: string | null; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.query.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.query.md deleted file mode 100644 index a9541efe0882aa..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.query.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [query](./kibana-plugin-core-public.urlmeaningfulparts.query.md) - -## URLMeaningfulParts.query property - -Signature: - -```typescript -query: ParsedQuery; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.slashes.md b/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.slashes.md deleted file mode 100644 index cb28a25f9e1625..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.urlmeaningfulparts.slashes.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [URLMeaningfulParts](./kibana-plugin-core-public.urlmeaningfulparts.md) > [slashes](./kibana-plugin-core-public.urlmeaningfulparts.slashes.md) - -## URLMeaningfulParts.slashes property - -Signature: - -```typescript -slashes?: boolean | null; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.assertnever.md b/docs/development/core/server/kibana-plugin-core-server.assertnever.md deleted file mode 100644 index c13c88df9b9bf2..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.assertnever.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [assertNever](./kibana-plugin-core-server.assertnever.md) - -## assertNever() function - -Can be used in switch statements to ensure we perform exhaustive checks, see https://www.typescriptlang.org/docs/handbook/advanced-types.html\#exhaustiveness-checking - -Signature: - -```typescript -export declare function assertNever(x: never): never; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | never | | - -Returns: - -`never` - diff --git a/docs/development/core/server/kibana-plugin-core-server.deepfreeze.md b/docs/development/core/server/kibana-plugin-core-server.deepfreeze.md deleted file mode 100644 index 946050bff0585c..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.deepfreeze.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [deepFreeze](./kibana-plugin-core-server.deepfreeze.md) - -## deepFreeze() function - -Apply Object.freeze to a value recursively and convert the return type to Readonly variant recursively - -Signature: - -```typescript -export declare function deepFreeze(object: T): RecursiveReadonly; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| object | T | | - -Returns: - -`RecursiveReadonly` - diff --git a/docs/development/core/server/kibana-plugin-core-server.freezable.md b/docs/development/core/server/kibana-plugin-core-server.freezable.md deleted file mode 100644 index 32ba89e8370c1e..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.freezable.md +++ /dev/null @@ -1,14 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Freezable](./kibana-plugin-core-server.freezable.md) - -## Freezable type - - -Signature: - -```typescript -export declare type Freezable = { - [k: string]: any; -} | any[]; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.getflattenedobject.md b/docs/development/core/server/kibana-plugin-core-server.getflattenedobject.md deleted file mode 100644 index 2e7850ca579f6b..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.getflattenedobject.md +++ /dev/null @@ -1,30 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [getFlattenedObject](./kibana-plugin-core-server.getflattenedobject.md) - -## getFlattenedObject() function - -Flattens a deeply nested object to a map of dot-separated paths pointing to all primitive values \*\*and arrays\*\* from `rootValue`. - -example: getFlattenedObject({ a: { b: 1, c: \[2,3\] } }) // => { 'a.b': 1, 'a.c': \[2,3\] } - -Signature: - -```typescript -export declare function getFlattenedObject(rootValue: Record): { - [key: string]: any; -}; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| rootValue | Record<string, any> | | - -Returns: - -`{ - [key: string]: any; -}` - diff --git a/docs/development/core/server/kibana-plugin-core-server.isrelativeurl.md b/docs/development/core/server/kibana-plugin-core-server.isrelativeurl.md deleted file mode 100644 index bff9eb05419bec..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.isrelativeurl.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [isRelativeUrl](./kibana-plugin-core-server.isrelativeurl.md) - -## isRelativeUrl() function - -Determine if a url is relative. Any url including a protocol, hostname, or port is not considered relative. This means that absolute \*paths\* are considered to be relative \*urls\* - -Signature: - -```typescript -export declare function isRelativeUrl(candidatePath: string): boolean; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| candidatePath | string | | - -Returns: - -`boolean` - diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index c16600d1d0492f..30b98b9f0553ed 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -42,13 +42,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | Function | Description | | --- | --- | -| [assertNever(x)](./kibana-plugin-core-server.assertnever.md) | Can be used in switch statements to ensure we perform exhaustive checks, see https://www.typescriptlang.org/docs/handbook/advanced-types.html\#exhaustiveness-checking | -| [deepFreeze(object)](./kibana-plugin-core-server.deepfreeze.md) | Apply Object.freeze to a value recursively and convert the return type to Readonly variant recursively | | [exportSavedObjectsToStream({ types, objects, search, savedObjectsClient, exportSizeLimit, includeReferencesDeep, excludeExportDetails, namespace, })](./kibana-plugin-core-server.exportsavedobjectstostream.md) | Generates sorted saved object stream to be used for export. See the [options](./kibana-plugin-core-server.savedobjectsexportoptions.md) for more detailed information. | -| [getFlattenedObject(rootValue)](./kibana-plugin-core-server.getflattenedobject.md) | Flattens a deeply nested object to a map of dot-separated paths pointing to all primitive values \*\*and arrays\*\* from rootValue.example: getFlattenedObject({ a: { b: 1, c: \[2,3\] } }) // => { 'a.b': 1, 'a.c': \[2,3\] } | | [importSavedObjectsFromStream({ readStream, objectLimit, overwrite, createNewCopies, savedObjectsClient, typeRegistry, namespace, })](./kibana-plugin-core-server.importsavedobjectsfromstream.md) | Import saved objects from given stream. See the [options](./kibana-plugin-core-server.savedobjectsimportoptions.md) for more detailed information. | -| [isRelativeUrl(candidatePath)](./kibana-plugin-core-server.isrelativeurl.md) | Determine if a url is relative. Any url including a protocol, hostname, or port is not considered relative. This means that absolute \*paths\* are considered to be relative \*urls\* | -| [modifyUrl(url, urlModifier)](./kibana-plugin-core-server.modifyurl.md) | Takes a URL and a function that takes the meaningful parts of the URL as a key-value object, modifies some or all of the parts, and returns the modified parts formatted again as a url.Url Parts sent: - protocol - slashes (does the url have the //) - auth - hostname (just the name of the host, no port or auth information) - port - pathname (the path after the hostname, no query or hash, starts with a slash if there was a path) - query (always an object, even when no query on original url) - hashWhy? - The default url library in node produces several conflicting properties on the "parsed" output. Modifying any of these might lead to the modifications being ignored (depending on which property was modified) - It's not always clear whether to use path/pathname, host/hostname, so this tries to add helpful constraints | | [resolveSavedObjectsImportErrors({ readStream, objectLimit, retries, savedObjectsClient, typeRegistry, namespace, createNewCopies, })](./kibana-plugin-core-server.resolvesavedobjectsimporterrors.md) | Resolve and return saved object import errors. See the [options](./kibana-plugin-core-server.savedobjectsresolveimporterrorsoptions.md) for more detailed informations. | ## Interfaces @@ -217,7 +212,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) | UiSettings parameters defined by the plugins. | | [UiSettingsServiceSetup](./kibana-plugin-core-server.uisettingsservicesetup.md) | | | [UiSettingsServiceStart](./kibana-plugin-core-server.uisettingsservicestart.md) | | -| [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) | We define our own typings because the current version of @types/node declares properties to be optional "hostname?: string". Although, parse call returns "hostname: null \| string". | | [UserProvidedValues](./kibana-plugin-core-server.userprovidedvalues.md) | Describes the values explicitly set by user. | ## Variables @@ -246,7 +240,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [DestructiveRouteMethod](./kibana-plugin-core-server.destructiveroutemethod.md) | Set of HTTP methods changing the state of the server. | | [ElasticsearchClient](./kibana-plugin-core-server.elasticsearchclient.md) | Client used to query the elasticsearch cluster. | | [ElasticsearchClientConfig](./kibana-plugin-core-server.elasticsearchclientconfig.md) | Configuration options to be used to create a [cluster client](./kibana-plugin-core-server.iclusterclient.md) using the [createClient API](./kibana-plugin-core-server.elasticsearchservicestart.createclient.md) | -| [Freezable](./kibana-plugin-core-server.freezable.md) | | | [GetAuthHeaders](./kibana-plugin-core-server.getauthheaders.md) | Get headers to authenticate a user against Elasticsearch. | | [GetAuthState](./kibana-plugin-core-server.getauthstate.md) | Gets authentication state for a request. Returned by auth interceptor. | | [HandlerContextType](./kibana-plugin-core-server.handlercontexttype.md) | Extracts the type of the first argument of a [HandlerFunction](./kibana-plugin-core-server.handlerfunction.md) to represent the type of the context. | diff --git a/docs/development/core/server/kibana-plugin-core-server.modifyurl.md b/docs/development/core/server/kibana-plugin-core-server.modifyurl.md deleted file mode 100644 index fc0bc354a3ca37..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.modifyurl.md +++ /dev/null @@ -1,31 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [modifyUrl](./kibana-plugin-core-server.modifyurl.md) - -## modifyUrl() function - -Takes a URL and a function that takes the meaningful parts of the URL as a key-value object, modifies some or all of the parts, and returns the modified parts formatted again as a url. - -Url Parts sent: - protocol - slashes (does the url have the //) - auth - hostname (just the name of the host, no port or auth information) - port - pathname (the path after the hostname, no query or hash, starts with a slash if there was a path) - query (always an object, even when no query on original url) - hash - -Why? - The default url library in node produces several conflicting properties on the "parsed" output. Modifying any of these might lead to the modifications being ignored (depending on which property was modified) - It's not always clear whether to use path/pathname, host/hostname, so this tries to add helpful constraints - -Signature: - -```typescript -export declare function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulParts) => Partial | void): string; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| url | string | | -| urlModifier | (urlParts: URLMeaningfulParts) => Partial<URLMeaningfulParts> | void | | - -Returns: - -`string` - -The modified and reformatted url - diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.auth.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.auth.md deleted file mode 100644 index 0422738669a708..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.auth.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [auth](./kibana-plugin-core-server.urlmeaningfulparts.auth.md) - -## URLMeaningfulParts.auth property - -Signature: - -```typescript -auth?: string | null; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hash.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hash.md deleted file mode 100644 index 13a3f4a9c95c80..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hash.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [hash](./kibana-plugin-core-server.urlmeaningfulparts.hash.md) - -## URLMeaningfulParts.hash property - -Signature: - -```typescript -hash?: string | null; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hostname.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hostname.md deleted file mode 100644 index 6631f6f6744c5d..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.hostname.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [hostname](./kibana-plugin-core-server.urlmeaningfulparts.hostname.md) - -## URLMeaningfulParts.hostname property - -Signature: - -```typescript -hostname?: string | null; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.md deleted file mode 100644 index 257f7b4b634ab3..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.md +++ /dev/null @@ -1,27 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) - -## URLMeaningfulParts interface - -We define our own typings because the current version of @types/node declares properties to be optional "hostname?: string". Although, parse call returns "hostname: null \| string". - -Signature: - -```typescript -export interface URLMeaningfulParts -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [auth](./kibana-plugin-core-server.urlmeaningfulparts.auth.md) | string | null | | -| [hash](./kibana-plugin-core-server.urlmeaningfulparts.hash.md) | string | null | | -| [hostname](./kibana-plugin-core-server.urlmeaningfulparts.hostname.md) | string | null | | -| [pathname](./kibana-plugin-core-server.urlmeaningfulparts.pathname.md) | string | null | | -| [port](./kibana-plugin-core-server.urlmeaningfulparts.port.md) | string | null | | -| [protocol](./kibana-plugin-core-server.urlmeaningfulparts.protocol.md) | string | null | | -| [query](./kibana-plugin-core-server.urlmeaningfulparts.query.md) | ParsedQuery | | -| [slashes](./kibana-plugin-core-server.urlmeaningfulparts.slashes.md) | boolean | null | | - diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.pathname.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.pathname.md deleted file mode 100644 index 8fee8c8e146ca3..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.pathname.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [pathname](./kibana-plugin-core-server.urlmeaningfulparts.pathname.md) - -## URLMeaningfulParts.pathname property - -Signature: - -```typescript -pathname?: string | null; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.port.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.port.md deleted file mode 100644 index dcf3517d92ba20..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.port.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [port](./kibana-plugin-core-server.urlmeaningfulparts.port.md) - -## URLMeaningfulParts.port property - -Signature: - -```typescript -port?: string | null; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.protocol.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.protocol.md deleted file mode 100644 index 914dcd4e8a8a55..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.protocol.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [protocol](./kibana-plugin-core-server.urlmeaningfulparts.protocol.md) - -## URLMeaningfulParts.protocol property - -Signature: - -```typescript -protocol?: string | null; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.query.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.query.md deleted file mode 100644 index 358adcfd3d1800..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.query.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [query](./kibana-plugin-core-server.urlmeaningfulparts.query.md) - -## URLMeaningfulParts.query property - -Signature: - -```typescript -query: ParsedQuery; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.slashes.md b/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.slashes.md deleted file mode 100644 index d5b598167f2f29..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.urlmeaningfulparts.slashes.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [URLMeaningfulParts](./kibana-plugin-core-server.urlmeaningfulparts.md) > [slashes](./kibana-plugin-core-server.urlmeaningfulparts.slashes.md) - -## URLMeaningfulParts.slashes property - -Signature: - -```typescript -slashes?: boolean | null; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md index cf171d9ee9f374..e85747b8cc3d7d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md @@ -7,5 +7,5 @@ Signature: ```typescript -QueryStringInput: React.FC> +QueryStringInput: React.FC> ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md index 73ba8eb66040ba..496e1ae9677d80 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md @@ -15,13 +15,13 @@ Using `createSearchSource`, the instance can be re-created. ```typescript serialize(): { searchSourceJSON: string; - references: import("../../../../../core/public").SavedObjectReference[]; + references: import("../../../../../core/types").SavedObjectReference[]; }; ``` Returns: `{ searchSourceJSON: string; - references: import("../../../../../core/public").SavedObjectReference[]; + references: import("../../../../../core/types").SavedObjectReference[]; }` diff --git a/examples/alerting_example/tsconfig.json b/examples/alerting_example/tsconfig.json index 09c130aca46429..214e4b78a9a701 100644 --- a/examples/alerting_example/tsconfig.json +++ b/examples/alerting_example/tsconfig.json @@ -11,5 +11,8 @@ "common/**/*.ts", "../../typings/**/*", ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/bfetch_explorer/tsconfig.json b/examples/bfetch_explorer/tsconfig.json index 798a9c222c5ab7..86b35c5e4943fd 100644 --- a/examples/bfetch_explorer/tsconfig.json +++ b/examples/bfetch_explorer/tsconfig.json @@ -11,5 +11,8 @@ "server/**/*.ts", "../../typings/**/*", ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/dashboard_embeddable_examples/tsconfig.json b/examples/dashboard_embeddable_examples/tsconfig.json index 798a9c222c5ab7..86b35c5e4943fd 100644 --- a/examples/dashboard_embeddable_examples/tsconfig.json +++ b/examples/dashboard_embeddable_examples/tsconfig.json @@ -11,5 +11,8 @@ "server/**/*.ts", "../../typings/**/*", ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/developer_examples/tsconfig.json b/examples/developer_examples/tsconfig.json index 798a9c222c5ab7..86b35c5e4943fd 100644 --- a/examples/developer_examples/tsconfig.json +++ b/examples/developer_examples/tsconfig.json @@ -11,5 +11,8 @@ "server/**/*.ts", "../../typings/**/*", ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/embeddable_examples/tsconfig.json b/examples/embeddable_examples/tsconfig.json index caeed2c1a434fa..78098339c16f5a 100644 --- a/examples/embeddable_examples/tsconfig.json +++ b/examples/embeddable_examples/tsconfig.json @@ -12,5 +12,8 @@ "server/**/*.ts", "../../typings/**/*" ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/embeddable_explorer/tsconfig.json b/examples/embeddable_explorer/tsconfig.json index 798a9c222c5ab7..86b35c5e4943fd 100644 --- a/examples/embeddable_explorer/tsconfig.json +++ b/examples/embeddable_explorer/tsconfig.json @@ -11,5 +11,8 @@ "server/**/*.ts", "../../typings/**/*", ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/routing_example/tsconfig.json b/examples/routing_example/tsconfig.json index 761a5c4da65ba9..54ac800019f82c 100644 --- a/examples/routing_example/tsconfig.json +++ b/examples/routing_example/tsconfig.json @@ -12,5 +12,8 @@ "common/**/*.ts", "../../typings/**/*", ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/search_examples/tsconfig.json b/examples/search_examples/tsconfig.json index 8bec69ca40ccc0..2b7d86d76a8a52 100644 --- a/examples/search_examples/tsconfig.json +++ b/examples/search_examples/tsconfig.json @@ -12,5 +12,8 @@ "server/**/*.ts", "../../typings/**/*", ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/state_containers_examples/tsconfig.json b/examples/state_containers_examples/tsconfig.json index 007322e2d95257..6cfb9f9dc23217 100644 --- a/examples/state_containers_examples/tsconfig.json +++ b/examples/state_containers_examples/tsconfig.json @@ -12,5 +12,8 @@ "common/**/*.ts", "../../typings/**/*" ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/ui_action_examples/tsconfig.json b/examples/ui_action_examples/tsconfig.json index 798a9c222c5ab7..86b35c5e4943fd 100644 --- a/examples/ui_action_examples/tsconfig.json +++ b/examples/ui_action_examples/tsconfig.json @@ -11,5 +11,8 @@ "server/**/*.ts", "../../typings/**/*", ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/ui_actions_explorer/tsconfig.json b/examples/ui_actions_explorer/tsconfig.json index 119209114a7bb8..782b9cd57fa7b0 100644 --- a/examples/ui_actions_explorer/tsconfig.json +++ b/examples/ui_actions_explorer/tsconfig.json @@ -10,5 +10,8 @@ "public/**/*.tsx", "../../typings/**/*", ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/url_generators_examples/tsconfig.json b/examples/url_generators_examples/tsconfig.json index 327b4642a8e7f8..8aef3328b42222 100644 --- a/examples/url_generators_examples/tsconfig.json +++ b/examples/url_generators_examples/tsconfig.json @@ -11,5 +11,8 @@ "server/**/*.ts", "../../typings/**/*" ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/examples/url_generators_explorer/tsconfig.json b/examples/url_generators_explorer/tsconfig.json index 327b4642a8e7f8..8aef3328b42222 100644 --- a/examples/url_generators_explorer/tsconfig.json +++ b/examples/url_generators_explorer/tsconfig.json @@ -11,5 +11,8 @@ "server/**/*.ts", "../../typings/**/*" ], - "exclude": [] + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" } + ] } diff --git a/package.json b/package.json index 7468a49d569596..d4ebfefc87270f 100644 --- a/package.json +++ b/package.json @@ -141,6 +141,7 @@ "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", "@kbn/pm": "1.0.0", + "@kbn/std": "1.0.0", "@kbn/telemetry-tools": "1.0.0", "@kbn/test-subj-selector": "0.2.1", "@kbn/ui-framework": "1.0.0", diff --git a/packages/kbn-std/README.md b/packages/kbn-std/README.md new file mode 100644 index 00000000000000..dfd98287ada4b5 --- /dev/null +++ b/packages/kbn-std/README.md @@ -0,0 +1,3 @@ +# `@kbn/std` — Kibana standard library + +This package is a set of utilities that can be used both on server-side and client-side. \ No newline at end of file diff --git a/packages/kbn-std/package.json b/packages/kbn-std/package.json new file mode 100644 index 00000000000000..4c67706b45d275 --- /dev/null +++ b/packages/kbn-std/package.json @@ -0,0 +1,21 @@ +{ + "name": "@kbn/std", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "version": "1.0.0", + "license": "Apache-2.0", + "private": true, + "scripts": { + "build": "tsc", + "kbn:bootstrap": "yarn build" + }, + "devDependencies": { + "typescript": "4.0.2", + "tsd": "^0.13.1" + }, + "dependencies": { + "@kbn/utility-types": "1.0.0", + "lodash": "^4.17.15", + "query-string": "5.1.1" + } +} diff --git a/src/core/utils/__snapshots__/get.test.ts.snap b/packages/kbn-std/src/__snapshots__/get.test.ts.snap similarity index 100% rename from src/core/utils/__snapshots__/get.test.ts.snap rename to packages/kbn-std/src/__snapshots__/get.test.ts.snap diff --git a/src/core/utils/assert_never.ts b/packages/kbn-std/src/assert_never.ts similarity index 100% rename from src/core/utils/assert_never.ts rename to packages/kbn-std/src/assert_never.ts diff --git a/src/core/utils/deep_freeze.test.ts b/packages/kbn-std/src/deep_freeze.test.ts similarity index 100% rename from src/core/utils/deep_freeze.test.ts rename to packages/kbn-std/src/deep_freeze.test.ts diff --git a/src/core/utils/deep_freeze.ts b/packages/kbn-std/src/deep_freeze.ts similarity index 100% rename from src/core/utils/deep_freeze.ts rename to packages/kbn-std/src/deep_freeze.ts diff --git a/src/core/utils/get.test.ts b/packages/kbn-std/src/get.test.ts similarity index 100% rename from src/core/utils/get.test.ts rename to packages/kbn-std/src/get.test.ts diff --git a/src/core/utils/get.ts b/packages/kbn-std/src/get.ts similarity index 100% rename from src/core/utils/get.ts rename to packages/kbn-std/src/get.ts diff --git a/src/core/utils/get_flattened_object.test.ts b/packages/kbn-std/src/get_flattened_object.test.ts similarity index 100% rename from src/core/utils/get_flattened_object.test.ts rename to packages/kbn-std/src/get_flattened_object.test.ts diff --git a/src/core/utils/get_flattened_object.ts b/packages/kbn-std/src/get_flattened_object.ts similarity index 100% rename from src/core/utils/get_flattened_object.ts rename to packages/kbn-std/src/get_flattened_object.ts diff --git a/packages/kbn-std/src/index.ts b/packages/kbn-std/src/index.ts new file mode 100644 index 00000000000000..7cf70a0e28e2cf --- /dev/null +++ b/packages/kbn-std/src/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 { assertNever } from './assert_never'; +export { deepFreeze, Freezable } from './deep_freeze'; +export { get } from './get'; +export { mapToObject } from './map_to_object'; +export { merge } from './merge'; +export { pick } from './pick'; +export { withTimeout } from './promise'; +export { isRelativeUrl, modifyUrl, URLMeaningfulParts } from './url'; +export { unset } from './unset'; +export { getFlattenedObject } from './get_flattened_object'; diff --git a/src/core/utils/map_to_object.ts b/packages/kbn-std/src/map_to_object.ts similarity index 100% rename from src/core/utils/map_to_object.ts rename to packages/kbn-std/src/map_to_object.ts diff --git a/src/core/utils/map_utils.test.ts b/packages/kbn-std/src/map_utils.test.ts similarity index 100% rename from src/core/utils/map_utils.test.ts rename to packages/kbn-std/src/map_utils.test.ts diff --git a/src/core/utils/map_utils.ts b/packages/kbn-std/src/map_utils.ts similarity index 100% rename from src/core/utils/map_utils.ts rename to packages/kbn-std/src/map_utils.ts diff --git a/src/core/utils/merge.test.ts b/packages/kbn-std/src/merge.test.ts similarity index 100% rename from src/core/utils/merge.test.ts rename to packages/kbn-std/src/merge.test.ts diff --git a/src/core/utils/merge.ts b/packages/kbn-std/src/merge.ts similarity index 98% rename from src/core/utils/merge.ts rename to packages/kbn-std/src/merge.ts index 43878c27b1e199..c0de50544a34e1 100644 --- a/src/core/utils/merge.ts +++ b/packages/kbn-std/src/merge.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { isPlainObject } from 'lodash'; +import isPlainObject from 'lodash/isPlainObject'; /** * Deeply merges two objects, omitting undefined values, and not deeply merging Arrays. * diff --git a/src/core/utils/pick.ts b/packages/kbn-std/src/pick.ts similarity index 100% rename from src/core/utils/pick.ts rename to packages/kbn-std/src/pick.ts diff --git a/src/core/utils/promise.test.ts b/packages/kbn-std/src/promise.test.ts similarity index 100% rename from src/core/utils/promise.test.ts rename to packages/kbn-std/src/promise.test.ts diff --git a/src/core/utils/promise.ts b/packages/kbn-std/src/promise.ts similarity index 100% rename from src/core/utils/promise.ts rename to packages/kbn-std/src/promise.ts diff --git a/src/core/utils/unset.test.ts b/packages/kbn-std/src/unset.test.ts similarity index 100% rename from src/core/utils/unset.test.ts rename to packages/kbn-std/src/unset.test.ts diff --git a/src/core/utils/unset.ts b/packages/kbn-std/src/unset.ts similarity index 100% rename from src/core/utils/unset.ts rename to packages/kbn-std/src/unset.ts diff --git a/src/core/utils/url.test.ts b/packages/kbn-std/src/url.test.ts similarity index 100% rename from src/core/utils/url.test.ts rename to packages/kbn-std/src/url.test.ts diff --git a/src/core/utils/url.ts b/packages/kbn-std/src/url.ts similarity index 100% rename from src/core/utils/url.ts rename to packages/kbn-std/src/url.ts diff --git a/packages/kbn-std/tsconfig.json b/packages/kbn-std/tsconfig.json new file mode 100644 index 00000000000000..5c86ad17a90e96 --- /dev/null +++ b/packages/kbn-std/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "./target", + "outDir": "./target", + "stripInternal": true, + "declarationMap": true, + "types": ["jest", "node"] + }, + "include": [ + "./src/**/*.ts", + "../../typings/query_string.d.ts" + ], + "exclude": ["target"] +} diff --git a/packages/kbn-std/yarn.lock b/packages/kbn-std/yarn.lock new file mode 120000 index 00000000000000..3f82ebc9cdbae3 --- /dev/null +++ b/packages/kbn-std/yarn.lock @@ -0,0 +1 @@ +../../yarn.lock \ No newline at end of file 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 e1d3bf1a8d9016..701171876ad2c4 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 @@ -262,7 +262,7 @@ export const schema = Joi.object() // settings for the find service layout: Joi.object() .keys({ - fixedHeaderHeight: Joi.number().default(50), + fixedHeaderHeight: Joi.number().default(100), }) .default(), diff --git a/packages/kbn-utility-types/index.ts b/packages/kbn-utility-types/index.ts index 6ccfeb8ab052c9..d3fb5bdf36194a 100644 --- a/packages/kbn-utility-types/index.ts +++ b/packages/kbn-utility-types/index.ts @@ -98,3 +98,15 @@ export type PublicKeys = keyof T; * Returns an object with public keys only. */ export type PublicContract = Pick>; + +/** + * Returns public method names + */ +export type MethodKeysOf = { + [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never; +}[keyof T]; + +/** + * Returns an object with public methods only. + */ +export type PublicMethodsOf = Pick>; diff --git a/packages/kbn-utility-types/tsconfig.json b/packages/kbn-utility-types/tsconfig.json index 03cace5b9cb2c3..79cf423fe78ace 100644 --- a/packages/kbn-utility-types/tsconfig.json +++ b/packages/kbn-utility-types/tsconfig.json @@ -7,7 +7,6 @@ "stripInternal": true, "declarationMap": true, "types": [ - "jest", "node" ] }, diff --git a/src/core/public/_variables.scss b/src/core/public/_variables.scss new file mode 100644 index 00000000000000..8c054e770bd4b7 --- /dev/null +++ b/src/core/public/_variables.scss @@ -0,0 +1,3 @@ +@import '@elastic/eui/src/global_styling/variables/header'; + +$kbnHeaderOffset: $euiHeaderHeightCompensation * 2; diff --git a/src/core/public/application/capabilities/capabilities_service.mock.ts b/src/core/public/application/capabilities/capabilities_service.mock.ts index 54aaa31e08859e..ee378547626669 100644 --- a/src/core/public/application/capabilities/capabilities_service.mock.ts +++ b/src/core/public/application/capabilities/capabilities_service.mock.ts @@ -16,8 +16,10 @@ * specific language governing permissions and limitations * under the License. */ + +import { deepFreeze } from '@kbn/std'; +import type { PublicMethodsOf } from '@kbn/utility-types'; import { CapabilitiesService, CapabilitiesStart } from './capabilities_service'; -import { deepFreeze } from '../../../utils'; const createStartContractMock = (): jest.Mocked => ({ capabilities: deepFreeze({ diff --git a/src/core/public/application/capabilities/capabilities_service.tsx b/src/core/public/application/capabilities/capabilities_service.tsx index 7304a8e5a66bc1..1164164aec4c59 100644 --- a/src/core/public/application/capabilities/capabilities_service.tsx +++ b/src/core/public/application/capabilities/capabilities_service.tsx @@ -17,9 +17,9 @@ * under the License. */ import { RecursiveReadonly } from '@kbn/utility-types'; +import { deepFreeze } from '@kbn/std'; import { Capabilities } from '../../../types/capabilities'; -import { deepFreeze } from '../../../utils'; import { HttpStart } from '../../http'; interface StartDeps { diff --git a/src/core/public/application/test_types.ts b/src/core/public/application/test_types.ts index 64012f0c0b6c15..6d29b77d92f365 100644 --- a/src/core/public/application/test_types.ts +++ b/src/core/public/application/test_types.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { AppUnmount, Mounter } from './types'; import { ApplicationService } from './application_service'; diff --git a/src/core/public/application/ui/app_container.tsx b/src/core/public/application/ui/app_container.tsx index f668cf851da55f..089d1cf3f3ced8 100644 --- a/src/core/public/application/ui/app_container.tsx +++ b/src/core/public/application/ui/app_container.tsx @@ -94,8 +94,10 @@ export const AppContainer: FunctionComponent = ({ // eslint-disable-next-line no-console console.error(e); } finally { - setShowSpinner(false); - setIsMounting(false); + if (elementRef.current) { + setShowSpinner(false); + setIsMounting(false); + } } }; diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index 5862ee7175f717..9cd96763d2e791 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -17,14 +17,8 @@ * under the License. */ import { BehaviorSubject } from 'rxjs'; -import { - ChromeBadge, - ChromeBrand, - ChromeBreadcrumb, - ChromeService, - InternalChromeStart, - NavType, -} from './'; +import type { PublicMethodsOf } from '@kbn/utility-types'; +import { ChromeBadge, ChromeBrand, ChromeBreadcrumb, ChromeService, InternalChromeStart } from './'; const createStartContractMock = () => { const startContract: DeeplyMockedKeys = { @@ -50,8 +44,10 @@ const createStartContractMock = () => { }, navControls: { registerLeft: jest.fn(), + registerCenter: jest.fn(), registerRight: jest.fn(), getLeft$: jest.fn(), + getCenter$: jest.fn(), getRight$: jest.fn(), }, setAppTitle: jest.fn(), @@ -70,7 +66,6 @@ const createStartContractMock = () => { setHelpExtension: jest.fn(), setHelpSupportUrl: jest.fn(), getIsNavDrawerLocked$: jest.fn(), - getNavType$: jest.fn(), getCustomNavLink$: jest.fn(), setCustomNavLink: jest.fn(), }; @@ -83,7 +78,6 @@ const createStartContractMock = () => { startContract.getCustomNavLink$.mockReturnValue(new BehaviorSubject(undefined)); startContract.getHelpExtension$.mockReturnValue(new BehaviorSubject(undefined)); startContract.getIsNavDrawerLocked$.mockReturnValue(new BehaviorSubject(false)); - startContract.getNavType$.mockReturnValue(new BehaviorSubject('modern' as NavType)); return startContract; }; diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index b96c34cd9fbe84..b01f120b81305d 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -37,7 +37,6 @@ import { ChromeNavControls, NavControlsService } from './nav_controls'; import { ChromeNavLinks, NavLinksService, ChromeNavLink } from './nav_links'; import { ChromeRecentlyAccessed, RecentlyAccessedService } from './recently_accessed'; import { Header } from './ui'; -import { NavType } from './ui/header'; import { ChromeHelpExtensionMenuLink } from './ui/header/header_help_menu'; export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle }; @@ -172,10 +171,6 @@ export class ChromeService { const getIsNavDrawerLocked$ = isNavDrawerLocked$.pipe(takeUntil(this.stop$)); - // TODO #64541 - // Can delete - const getNavType$ = uiSettings.get$('pageNavigation').pipe(takeUntil(this.stop$)); - const isIE = () => { const ua = window.navigator.userAgent; const msie = ua.indexOf('MSIE '); // IE 10 or older @@ -241,10 +236,10 @@ export class ChromeService { navLinks$={navLinks.getNavLinks$()} recentlyAccessed$={recentlyAccessed.get$()} navControlsLeft$={navControls.getLeft$()} + navControlsCenter$={navControls.getCenter$()} navControlsRight$={navControls.getRight$()} onIsLockedUpdate={setIsNavDrawerLocked} isLocked$={getIsNavDrawerLocked$} - navType$={getNavType$} /> ), @@ -305,8 +300,6 @@ export class ChromeService { getIsNavDrawerLocked$: () => getIsNavDrawerLocked$, - getNavType$: () => getNavType$, - getCustomNavLink$: () => customNavLink$.pipe(takeUntil(this.stop$)), setCustomNavLink: (customNavLink?: ChromeNavLink) => { @@ -468,13 +461,6 @@ export interface ChromeStart { * Get an observable of the current locked state of the nav drawer. */ getIsNavDrawerLocked$(): Observable; - - /** - * Get the navigation type - * TODO #64541 - * Can delete - */ - getNavType$(): Observable; } /** @internal */ diff --git a/src/core/public/chrome/nav_controls/nav_controls_service.ts b/src/core/public/chrome/nav_controls/nav_controls_service.ts index 167948e01cb362..2638f40c77dc4a 100644 --- a/src/core/public/chrome/nav_controls/nav_controls_service.ts +++ b/src/core/public/chrome/nav_controls/nav_controls_service.ts @@ -45,14 +45,18 @@ export interface ChromeNavControl { * @public */ export interface ChromeNavControls { - /** Register a nav control to be presented on the left side of the chrome header. */ + /** Register a nav control to be presented on the bottom-left side of the chrome header. */ registerLeft(navControl: ChromeNavControl): void; - /** Register a nav control to be presented on the right side of the chrome header. */ + /** Register a nav control to be presented on the top-right side of the chrome header. */ registerRight(navControl: ChromeNavControl): void; + /** Register a nav control to be presented on the top-center side of the chrome header. */ + registerCenter(navControl: ChromeNavControl): void; /** @internal */ getLeft$(): Observable; /** @internal */ getRight$(): Observable; + /** @internal */ + getCenter$(): Observable; } /** @internal */ @@ -62,6 +66,7 @@ export class NavControlsService { public start() { const navControlsLeft$ = new BehaviorSubject>(new Set()); const navControlsRight$ = new BehaviorSubject>(new Set()); + const navControlsCenter$ = new BehaviorSubject>(new Set()); return { // In the future, registration should be moved to the setup phase. This @@ -72,6 +77,9 @@ export class NavControlsService { registerRight: (navControl: ChromeNavControl) => navControlsRight$.next(new Set([...navControlsRight$.value.values(), navControl])), + registerCenter: (navControl: ChromeNavControl) => + navControlsCenter$.next(new Set([...navControlsCenter$.value.values(), navControl])), + getLeft$: () => navControlsLeft$.pipe( map((controls) => sortBy([...controls.values()], 'order')), @@ -82,6 +90,11 @@ export class NavControlsService { map((controls) => sortBy([...controls.values()], 'order')), takeUntil(this.stop$) ), + getCenter$: () => + navControlsCenter$.pipe( + map((controls) => sortBy([...controls.values()], 'order')), + takeUntil(this.stop$) + ), }; } diff --git a/src/core/public/chrome/nav_links/nav_link.ts b/src/core/public/chrome/nav_links/nav_link.ts index 4b82e0ced45057..80f0265819c40c 100644 --- a/src/core/public/chrome/nav_links/nav_link.ts +++ b/src/core/public/chrome/nav_links/nav_link.ts @@ -17,7 +17,7 @@ * under the License. */ -import { pick } from '../../../utils'; +import { pick } from '@kbn/std'; import { AppCategory } from '../../'; /** diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index a770ece8496e4c..86cacfe98f7672 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -121,7 +121,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` homeHref="/" id="collapsibe-nav" isLocked={false} - isOpen={true} + isNavOpen={true} navLinks$={ BehaviorSubject { "_isScalar": false, @@ -2105,7 +2105,7 @@ exports[`CollapsibleNav renders the default nav 1`] = ` homeHref="/" id="collapsibe-nav" isLocked={false} - isOpen={false} + isNavOpen={false} navLinks$={ BehaviorSubject { "_isScalar": false, @@ -2339,6 +2339,7 @@ exports[`CollapsibleNav renders the default nav 2`] = ` homeHref="/" id="collapsibe-nav" isLocked={false} + isNavOpen={false} isOpen={true} navLinks$={ BehaviorSubject { @@ -2454,461 +2455,9 @@ exports[`CollapsibleNav renders the default nav 2`] = ` data-test-subj="collapsibleNav" id="collapsibe-nav" isDocked={false} - isOpen={true} + isOpen={false} onClose={[Function]} - > - - - -
- -
-
- + /> `; @@ -3025,6 +2574,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` homeHref="/" id="collapsibe-nav" isLocked={true} + isNavOpen={false} isOpen={true} navLinks$={ BehaviorSubject { @@ -3140,7 +2690,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` data-test-subj="collapsibleNav" id="collapsibe-nav" isDocked={true} - isOpen={true} + isOpen={false} onClose={[Function]} > - -
-
-
- - -`; - -exports[`Header renders 2`] = ` -
+ -
- -
- -
- -
- - - -
-
- -
+ - - - -
- - - - -
- - , + ], + }, + Object { + "borders": "none", + "items": Array [ + -
- - - - - - - - - , ], - "thrownError": null, - } - } - /> - -
- -
+ }, + Object { + "borders": "none", + "items": Array [ , + , + ], + }, + ] + } + theme="dark" + > +
+ +
+ + + + +
+ +
+ +
+
+
+
+ +
+ +
+ - - - - } - closePopover={[Function]} - data-test-subj="helpMenuButton" - display="inlineBlock" - hasArrow={true} - id="headerHelpMenu" - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - repositionOnScroll={true} - > - -
-
- - - -
-
-
-
- -
-
-
- -
-
-
- - - - -
-
-`; - -exports[`Header renders 3`] = ` -
- -
-
-
- -
- -
- -
- -
- - - -
-
- -
- - - - -
- - - - -
- - -
- - - - - - - - - - -
- -
- - - - - - } - closePopover={[Function]} - data-test-subj="helpMenuButton" - display="inlineBlock" - hasArrow={true} - id="headerHelpMenu" - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - repositionOnScroll={true} - > - -
-
- - - -
-
-
-
-
-
-
-
- -
-
-
- - - - - -
- -
-
-
-
-
-
-`; - -exports[`Header renders 4`] = ` -
- -
-
-
- -
- -
- -
- - -
- - - -
-
-
- -
- - - - -
- - - - -
- - -
- - - - - - - - - - -
- -
- - + + + + } + closePopover={[Function]} + data-test-subj="helpMenuButton" + display="inlineBlock" + hasArrow={true} + id="headerHelpMenu" + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + repositionOnScroll={true} + > + +
+
+ + + +
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+ + +
+ +
+ +
+ + + +
+
+ +
+ + +
+ + + + + + + + + + +
+ +
+ - - - - } - closePopover={[Function]} - data-test-subj="helpMenuButton" - display="inlineBlock" - hasArrow={true} - id="headerHelpMenu" - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - repositionOnScroll={true} - > - -
-
- - - -
-
-
-
- - -
-
- -
-
-
- - + +
+ +
+ +
+ +
+ - - + - - - - + close + + + + + + + + + + + + `; diff --git a/src/core/public/chrome/ui/header/_index.scss b/src/core/public/chrome/ui/header/_index.scss index e3b73abbcabc20..44cd8642783252 100644 --- a/src/core/public/chrome/ui/header/_index.scss +++ b/src/core/public/chrome/ui/header/_index.scss @@ -1,14 +1,5 @@ @include euiHeaderAffordForFixed; -// TODO #64541 -// Delete this block -.chrHeaderWrapper:not(.headerWrapper) { - width: 100%; - position: fixed; - top: 0; - z-index: 10; -} - .chrHeaderHelpMenu__version { text-transform: none; } diff --git a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx index d4325e0caf88c4..e33e76a45580e7 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx @@ -59,7 +59,7 @@ function mockProps() { basePath: httpServiceMock.createSetupContract({ basePath: '/test' }).basePath, id: 'collapsibe-nav', isLocked: false, - isOpen: false, + isNavOpen: false, homeHref: '/', navLinks$: new BehaviorSubject([]), recentlyAccessed$: new BehaviorSubject([]), @@ -123,7 +123,7 @@ describe('CollapsibleNav', () => { const component = mount( { const component = mount( @@ -147,9 +147,9 @@ describe('CollapsibleNav', () => { clickGroup(component, 'kibana'); clickGroup(component, 'recentlyViewed'); expectShownNavLinksCount(component, 1); - component.setProps({ isOpen: false }); + component.setProps({ isNavOpen: false }); expectNavIsClosed(component); - component.setProps({ isOpen: true }); + component.setProps({ isNavOpen: true }); expectShownNavLinksCount(component, 1); }); @@ -160,14 +160,14 @@ describe('CollapsibleNav', () => { const component = mount( ); component.setProps({ closeNav: () => { - component.setProps({ isOpen: false }); + component.setProps({ isNavOpen: false }); onClose(); }, }); @@ -175,11 +175,11 @@ describe('CollapsibleNav', () => { component.find('[data-test-subj="collapsibleNavGroup-recentlyViewed"] a').simulate('click'); expect(onClose.callCount).toEqual(1); expectNavIsClosed(component); - component.setProps({ isOpen: true }); + component.setProps({ isNavOpen: true }); component.find('[data-test-subj="collapsibleNavGroup-kibana"] a').simulate('click'); expect(onClose.callCount).toEqual(2); expectNavIsClosed(component); - component.setProps({ isOpen: true }); + component.setProps({ isNavOpen: true }); component.find('[data-test-subj="collapsibleNavGroup-noCategory"] a').simulate('click'); expect(onClose.callCount).toEqual(3); expectNavIsClosed(component); diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index a5f42c09495626..01cdb9c38881a5 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -79,7 +79,7 @@ interface Props { basePath: HttpStart['basePath']; id: string; isLocked: boolean; - isOpen: boolean; + isNavOpen: boolean; homeHref: string; navLinks$: Rx.Observable; recentlyAccessed$: Rx.Observable; @@ -94,7 +94,7 @@ export function CollapsibleNav({ basePath, id, isLocked, - isOpen, + isNavOpen, homeHref, storage = window.localStorage, onIsLockedUpdate, @@ -129,7 +129,7 @@ export function CollapsibleNav({ aria-label={i18n.translate('core.ui.primaryNav.screenReaderLabel', { defaultMessage: 'Primary', })} - isOpen={isOpen} + isOpen={isNavOpen} isDocked={isLocked} onClose={closeNav} > diff --git a/src/core/public/chrome/ui/header/header.test.tsx b/src/core/public/chrome/ui/header/header.test.tsx index 04eb256f30f37c..7309b9af493889 100644 --- a/src/core/public/chrome/ui/header/header.test.tsx +++ b/src/core/public/chrome/ui/header/header.test.tsx @@ -21,7 +21,6 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { BehaviorSubject } from 'rxjs'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { NavType } from '.'; import { httpServiceMock } from '../../../http/http_service.mock'; import { applicationServiceMock } from '../../../mocks'; import { Header } from './header'; @@ -51,10 +50,10 @@ function mockProps() { helpExtension$: new BehaviorSubject(undefined), helpSupportUrl$: new BehaviorSubject(''), navControlsLeft$: new BehaviorSubject([]), + navControlsCenter$: new BehaviorSubject([]), navControlsRight$: new BehaviorSubject([]), basePath: http.basePath, isLocked$: new BehaviorSubject(false), - navType$: new BehaviorSubject('modern' as NavType), loadingCount$: new BehaviorSubject(0), onIsLockedUpdate: () => {}, }; @@ -71,7 +70,6 @@ describe('Header', () => { const isVisible$ = new BehaviorSubject(false); const breadcrumbs$ = new BehaviorSubject([{ text: 'test' }]); const isLocked$ = new BehaviorSubject(false); - const navType$ = new BehaviorSubject('modern' as NavType); const navLinks$ = new BehaviorSubject([ { id: 'kibana', title: 'kibana', baseUrl: '', href: '' }, ]); @@ -92,22 +90,19 @@ describe('Header', () => { navLinks$={navLinks$} recentlyAccessed$={recentlyAccessed$} isLocked$={isLocked$} - navType$={navType$} customNavLink$={customNavLink$} /> ); - expect(component).toMatchSnapshot(); + expect(component.find('EuiHeader').exists()).toBeFalsy(); act(() => isVisible$.next(true)); component.update(); - expect(component).toMatchSnapshot(); + expect(component.find('EuiHeader').exists()).toBeTruthy(); + expect(component.find('nav[aria-label="Primary"]').exists()).toBeFalsy(); act(() => isLocked$.next(true)); component.update(); - expect(component).toMatchSnapshot(); - - act(() => navType$.next('legacy' as NavType)); - component.update(); + expect(component.find('nav[aria-label="Primary"]').exists()).toBeTruthy(); expect(component).toMatchSnapshot(); }); }); diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index c0b3fc72930dc4..7ec03ea4c6da6e 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -23,8 +23,6 @@ import { EuiHeaderSectionItem, EuiHeaderSectionItemButton, EuiIcon, - EuiNavDrawer, - EuiShowFor, htmlIdGenerator, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -43,14 +41,14 @@ import { import { InternalApplicationStart } from '../../../application/types'; import { HttpStart } from '../../../http'; import { ChromeHelpExtension } from '../../chrome_service'; -import { NavType, OnIsLockedUpdate } from './'; +import { OnIsLockedUpdate } from './'; import { CollapsibleNav } from './collapsible_nav'; import { HeaderBadge } from './header_badge'; import { HeaderBreadcrumbs } from './header_breadcrumbs'; import { HeaderHelpMenu } from './header_help_menu'; import { HeaderLogo } from './header_logo'; import { HeaderNavControls } from './header_nav_controls'; -import { NavDrawer } from './nav_drawer'; +import { HeaderActionMenu } from './header_action_menu'; export interface HeaderProps { kibanaVersion: string; @@ -68,27 +66,14 @@ export interface HeaderProps { helpExtension$: Observable; helpSupportUrl$: Observable; navControlsLeft$: Observable; + navControlsCenter$: Observable; navControlsRight$: Observable; basePath: HttpStart['basePath']; isLocked$: Observable; - navType$: Observable; loadingCount$: ReturnType; onIsLockedUpdate: OnIsLockedUpdate; } -function renderMenuTrigger(toggleOpen: () => void) { - return ( - - - - ); -} - export function Header({ kibanaVersion, kibanaDocLink, @@ -98,125 +83,116 @@ export function Header({ homeHref, ...observables }: HeaderProps) { - const isVisible = useObservable(observables.isVisible$, true); - const navType = useObservable(observables.navType$, 'modern'); + const isVisible = useObservable(observables.isVisible$, false); const isLocked = useObservable(observables.isLocked$, false); - const [isOpen, setIsOpen] = useState(false); + const [isNavOpen, setIsNavOpen] = useState(false); if (!isVisible) { return ; } - const navDrawerRef = createRef(); const toggleCollapsibleNavRef = createRef(); const navId = htmlIdGenerator()(); - const className = classnames( - 'chrHeaderWrapper', // TODO #64541 - delete this - 'hide-for-sharing', - { - 'chrHeaderWrapper--navIsLocked': isLocked, - headerWrapper: navType === 'modern', - } - ); + const className = classnames('hide-for-sharing', 'headerGlobalNav'); return ( <>
- - - {navType === 'modern' ? ( +
+ , + ], + borders: 'none', + }, + { + ...(observables.navControlsCenter$ && { + items: [], + }), + borders: 'none', + }, + { + items: [ + , + , + ], + borders: 'none', + }, + ]} + /> + + + setIsOpen(!isOpen)} - aria-expanded={isOpen} - aria-pressed={isOpen} + onClick={() => setIsNavOpen(!isNavOpen)} + aria-expanded={isNavOpen} + aria-pressed={isNavOpen} aria-controls={navId} ref={toggleCollapsibleNavRef} > - ) : ( - // TODO #64541 - // Delete this block - - - {renderMenuTrigger(() => navDrawerRef.current?.toggleOpen())} - - - )} - - - + - - + + - + - + - - - - + + + + + + +
- -
-
- {navType === 'modern' ? ( - { - setIsOpen(false); - if (toggleCollapsibleNavRef.current) { - toggleCollapsibleNavRef.current.focus(); - } - }} - customNavLink$={observables.customNavLink$} - /> - ) : ( - // TODO #64541 - // Delete this block - - )} + { + setIsNavOpen(false); + if (toggleCollapsibleNavRef.current) { + toggleCollapsibleNavRef.current.focus(); + } + }} + customNavLink$={observables.customNavLink$} + />
); diff --git a/src/core/public/chrome/ui/header/header_action_menu.test.tsx b/src/core/public/chrome/ui/header/header_action_menu.test.tsx new file mode 100644 index 00000000000000..a124c8ab669690 --- /dev/null +++ b/src/core/public/chrome/ui/header/header_action_menu.test.tsx @@ -0,0 +1,139 @@ +/* + * 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 { mount, ReactWrapper } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { BehaviorSubject } from 'rxjs'; +import { HeaderActionMenu } from './header_action_menu'; +import { MountPoint, UnmountCallback } from '../../../types'; + +type MockedUnmount = jest.MockedFunction; + +describe('HeaderActionMenu', () => { + let component: ReactWrapper; + let menuMount$: BehaviorSubject; + let unmounts: Record; + + beforeEach(() => { + menuMount$ = new BehaviorSubject(undefined); + unmounts = {}; + }); + + const refresh = () => { + new Promise(async (resolve) => { + if (component) { + act(() => { + component.update(); + }); + } + setImmediate(() => resolve(component)); // flushes any pending promises + }); + }; + + const createMountPoint = (id: string, content: string = id): MountPoint => ( + root + ): MockedUnmount => { + const container = document.createElement('DIV'); + // eslint-disable-next-line no-unsanitized/property + container.innerHTML = content; + root.appendChild(container); + const unmount = jest.fn(() => container.remove()); + unmounts[id] = unmount; + return unmount; + }; + + it('mounts the current value of the provided observable', async () => { + component = mount(); + + act(() => { + menuMount$.next(createMountPoint('FOO')); + }); + await refresh(); + + expect(component.html()).toMatchInlineSnapshot( + `"
FOO
"` + ); + }); + + it('clears the content of the component when emitting undefined', async () => { + component = mount(); + + act(() => { + menuMount$.next(createMountPoint('FOO')); + }); + await refresh(); + + expect(component.html()).toMatchInlineSnapshot( + `"
FOO
"` + ); + + act(() => { + menuMount$.next(undefined); + }); + await refresh(); + + expect(component.html()).toMatchInlineSnapshot( + `"
"` + ); + }); + + it('updates the dom when a new mount point is emitted', async () => { + component = mount(); + + act(() => { + menuMount$.next(createMountPoint('FOO')); + }); + await refresh(); + + expect(component.html()).toMatchInlineSnapshot( + `"
FOO
"` + ); + + act(() => { + menuMount$.next(createMountPoint('BAR')); + }); + await refresh(); + + expect(component.html()).toMatchInlineSnapshot( + `"
BAR
"` + ); + }); + + it('calls the previous mount point `unmount` when mounting a new mount point', async () => { + component = mount(); + + act(() => { + menuMount$.next(createMountPoint('FOO')); + }); + await refresh(); + + expect(Object.keys(unmounts)).toEqual(['FOO']); + expect(unmounts.FOO).not.toHaveBeenCalled(); + + act(() => { + menuMount$.next(createMountPoint('BAR')); + }); + await refresh(); + + expect(Object.keys(unmounts)).toEqual(['FOO', 'BAR']); + expect(unmounts.FOO).toHaveBeenCalledTimes(1); + expect(unmounts.BAR).not.toHaveBeenCalled(); + }); +}); diff --git a/src/core/public/chrome/ui/header/header_action_menu.tsx b/src/core/public/chrome/ui/header/header_action_menu.tsx new file mode 100644 index 00000000000000..3a7a09608ba66e --- /dev/null +++ b/src/core/public/chrome/ui/header/header_action_menu.tsx @@ -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 React, { FC, useRef, useLayoutEffect, useState } from 'react'; +import { Observable } from 'rxjs'; +import { MountPoint, UnmountCallback } from '../../../types'; + +interface HeaderActionMenuProps { + actionMenu$: Observable; +} + +export const HeaderActionMenu: FC = ({ actionMenu$ }) => { + // useObservable relies on useState under the hood. The signature is type SetStateAction = S | ((prevState: S) => S); + // As we got a Observable here, React's setState setter assume he's getting a `(prevState: S) => S` signature, + // therefore executing the mount method, causing everything to crash. + // piping the observable before calling `useObservable` causes the effect to always having a new reference, as + // the piped observable is a new instance on every render, causing infinite loops. + // this is why we use `useLayoutEffect` manually here. + const [mounter, setMounter] = useState<{ mount: MountPoint | undefined }>({ mount: undefined }); + useLayoutEffect(() => { + const s = actionMenu$.subscribe((value) => { + setMounter({ mount: value }); + }); + return () => s.unsubscribe(); + }, [actionMenu$]); + + const elementRef = useRef(null); + const unmountRef = useRef(null); + + useLayoutEffect(() => { + if (unmountRef.current) { + unmountRef.current(); + unmountRef.current = null; + } + + if (mounter.mount && elementRef.current) { + try { + unmountRef.current = mounter.mount(elementRef.current); + } catch (e) { + // TODO: use client-side logger when feature is implemented + // eslint-disable-next-line no-console + console.error(e); + } + } + }, [mounter]); + + return
; +}; diff --git a/src/core/public/chrome/ui/header/header_logo.tsx b/src/core/public/chrome/ui/header/header_logo.tsx index 9bec946b6b76e2..dee93ecb1a804f 100644 --- a/src/core/public/chrome/ui/header/header_logo.tsx +++ b/src/core/public/chrome/ui/header/header_logo.tsx @@ -105,6 +105,8 @@ export function HeaderLogo({ href, navigateToApp, ...observables }: Props) { aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel', { defaultMessage: 'Go to home page', })} - /> + > + Elastic + ); } diff --git a/src/core/public/chrome/ui/header/header_nav_controls.tsx b/src/core/public/chrome/ui/header/header_nav_controls.tsx index 0941f7b27b6625..8d9d8097fd8e3f 100644 --- a/src/core/public/chrome/ui/header/header_nav_controls.tsx +++ b/src/core/public/chrome/ui/header/header_nav_controls.tsx @@ -26,7 +26,7 @@ import { HeaderExtension } from './header_extension'; interface Props { navControls$: Observable; - side: 'left' | 'right'; + side?: 'left' | 'right'; } export function HeaderNavControls({ navControls$, side }: Props) { @@ -41,7 +41,10 @@ export function HeaderNavControls({ navControls$, side }: Props) { return ( <> {navControls.map((navControl: ChromeNavControl, index: number) => ( - + ))} diff --git a/src/core/public/chrome/ui/header/nav_drawer.tsx b/src/core/public/chrome/ui/header/nav_drawer.tsx deleted file mode 100644 index fc080fbafc3030..00000000000000 --- a/src/core/public/chrome/ui/header/nav_drawer.tsx +++ /dev/null @@ -1,83 +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 { EuiHorizontalRule, EuiNavDrawer, EuiNavDrawerGroup } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { useObservable } from 'react-use'; -import { Observable } from 'rxjs'; -import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem, CoreStart } from '../../..'; -import { InternalApplicationStart } from '../../../application/types'; -import { HttpStart } from '../../../http'; -import { OnIsLockedUpdate } from './'; -import { createEuiListItem, createRecentNavLink } from './nav_link'; -import { RecentLinks } from './recent_links'; - -export interface Props { - appId$: InternalApplicationStart['currentAppId$']; - basePath: HttpStart['basePath']; - isLocked?: boolean; - navLinks$: Observable; - recentlyAccessed$: Observable; - navigateToApp: CoreStart['application']['navigateToApp']; - onIsLockedUpdate?: OnIsLockedUpdate; -} - -function NavDrawerRenderer( - { isLocked, onIsLockedUpdate, basePath, navigateToApp, ...observables }: Props, - ref: React.Ref -) { - const appId = useObservable(observables.appId$, ''); - const navLinks = useObservable(observables.navLinks$, []).filter((link) => !link.hidden); - const recentNavLinks = useObservable(observables.recentlyAccessed$, []).map((link) => - createRecentNavLink(link, navLinks, basePath) - ); - - return ( - - {RecentLinks({ recentNavLinks })} - - - createEuiListItem({ - link, - appId, - basePath, - navigateToApp, - dataTestSubj: 'navDrawerAppsMenuLink', - }) - )} - aria-label={i18n.translate('core.ui.primaryNavList.screenReaderLabel', { - defaultMessage: 'Primary navigation links', - })} - /> - - ); -} - -export const NavDrawer = React.forwardRef(NavDrawerRenderer); diff --git a/src/core/public/context/context_service.mock.ts b/src/core/public/context/context_service.mock.ts index eb55ced69dc048..3f9f647e1f7d00 100644 --- a/src/core/public/context/context_service.mock.ts +++ b/src/core/public/context/context_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { ContextService, ContextSetup } from './context_service'; import { contextMock } from '../../utils/context.mock'; diff --git a/src/core/public/core_app/core_app.mock.ts b/src/core/public/core_app/core_app.mock.ts index b0e3871a40bf5d..b2af81b3630a30 100644 --- a/src/core/public/core_app/core_app.mock.ts +++ b/src/core/public/core_app/core_app.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { CoreApp } from './core_app'; type CoreAppContract = PublicMethodsOf; diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 006d0036f7a129..a1d6f9c988b270 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -17,6 +17,7 @@ * under the License. */ +import { pick } from '@kbn/std'; import { CoreId } from '../server'; import { PackageInfo, EnvironmentMode } from '../server/types'; import { CoreSetup, CoreStart } from '.'; @@ -35,7 +36,6 @@ import { OverlayService } from './overlays'; import { PluginsService } from './plugins'; import { UiSettingsService } from './ui_settings'; import { ApplicationService } from './application'; -import { pick } from '../utils/'; import { DocLinksService } from './doc_links'; import { RenderingService } from './rendering'; import { SavedObjectsService } from './saved_objects'; diff --git a/src/core/public/doc_links/doc_links_service.mock.ts b/src/core/public/doc_links/doc_links_service.mock.ts index 105c13f96cef65..07e29edad8974b 100644 --- a/src/core/public/doc_links/doc_links_service.mock.ts +++ b/src/core/public/doc_links/doc_links_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock'; import { DocLinksService, DocLinksStart } from './doc_links_service'; diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index fae7a272c96359..47f58a3a9fcbfb 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -17,8 +17,8 @@ * under the License. */ +import { deepFreeze } from '@kbn/std'; import { InjectedMetadataSetup } from '../injected_metadata'; -import { deepFreeze } from '../../utils'; interface StartDeps { injectedMetadata: InjectedMetadataSetup; diff --git a/src/core/public/fatal_errors/fatal_errors_service.mock.ts b/src/core/public/fatal_errors/fatal_errors_service.mock.ts index 6d9876f787fa97..326ce9a3923e9f 100644 --- a/src/core/public/fatal_errors/fatal_errors_service.mock.ts +++ b/src/core/public/fatal_errors/fatal_errors_service.mock.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { PublicMethodsOf } from '@kbn/utility-types'; import { FatalErrorsService, FatalErrorsSetup } from './fatal_errors_service'; const createSetupContractMock = () => { diff --git a/src/core/public/http/base_path.ts b/src/core/public/http/base_path.ts index ac85d71c793fe5..5d9eb51023b78f 100644 --- a/src/core/public/http/base_path.ts +++ b/src/core/public/http/base_path.ts @@ -35,7 +35,7 @@ * under the License. */ -import { modifyUrl } from '../../utils'; +import { modifyUrl } from '@kbn/std'; export class BasePath { constructor( diff --git a/src/core/public/http/http_service.mock.ts b/src/core/public/http/http_service.mock.ts index 1111fd39ec78e6..68533159765fb1 100644 --- a/src/core/public/http/http_service.mock.ts +++ b/src/core/public/http/http_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { HttpService } from './http_service'; import { HttpSetup } from './types'; import { BehaviorSubject } from 'rxjs'; diff --git a/src/core/public/http/loading_count_service.mock.ts b/src/core/public/http/loading_count_service.mock.ts index 79928aa4b160de..2ff635e815296e 100644 --- a/src/core/public/http/loading_count_service.mock.ts +++ b/src/core/public/http/loading_count_service.mock.ts @@ -19,6 +19,7 @@ import { LoadingCountSetup, LoadingCountService } from './loading_count_service'; import { BehaviorSubject } from 'rxjs'; +import type { PublicMethodsOf } from '@kbn/utility-types'; const createSetupContractMock = () => { const setupContract: jest.Mocked = { diff --git a/src/core/public/i18n/i18n_service.mock.ts b/src/core/public/i18n/i18n_service.mock.ts index 1a9ca0869d7c15..a91710fbed7806 100644 --- a/src/core/public/i18n/i18n_service.mock.ts +++ b/src/core/public/i18n/i18n_service.mock.ts @@ -18,6 +18,8 @@ */ import React from 'react'; +import type { PublicMethodsOf } from '@kbn/utility-types'; + import { I18nService, I18nStart } from './i18n_service'; const PassThroughComponent = ({ children }: { children: React.ReactNode }) => children; diff --git a/src/core/public/index.scss b/src/core/public/index.scss index c2ad2841d5a773..6ba9254e5d381f 100644 --- a/src/core/public/index.scss +++ b/src/core/public/index.scss @@ -1,6 +1,6 @@ +@import './variables'; @import './core'; @import './chrome/index'; @import './overlays/index'; @import './rendering/index'; @import './styles/index'; - diff --git a/src/core/public/index.ts b/src/core/public/index.ts index a9774dafd2340a..24d19e2d32074e 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -68,7 +68,6 @@ import { UiSettingsState, IUiSettingsClient } from './ui_settings'; import { ApplicationSetup, Capabilities, ApplicationStart } from './application'; import { DocLinksStart } from './doc_links'; import { SavedObjectsStart } from './saved_objects'; -export { PackageInfo, EnvironmentMode } from '../server/types'; import { IContextContainer, IContextProvider, @@ -78,17 +77,9 @@ import { HandlerParameters, } from './context'; +export { PackageInfo, EnvironmentMode } from '../server/types'; export { CoreContext, CoreSystem } from './core_system'; -export { - DEFAULT_APP_CATEGORIES, - getFlattenedObject, - URLMeaningfulParts, - modifyUrl, - isRelativeUrl, - Freezable, - deepFreeze, - assertNever, -} from '../utils'; +export { DEFAULT_APP_CATEGORIES } from '../utils'; export { AppCategory, UiSettingsParams, diff --git a/src/core/public/injected_metadata/injected_metadata_service.mock.ts b/src/core/public/injected_metadata/injected_metadata_service.mock.ts index 3bb4358406246b..33d04eedebb072 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.mock.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.mock.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { PublicMethodsOf } from '@kbn/utility-types'; import { InjectedMetadataService, InjectedMetadataSetup } from './injected_metadata_service'; const createSetupContractMock = () => { diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts index 23630a5bcf228b..5b51bc823d166e 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.ts @@ -18,6 +18,7 @@ */ import { get } from 'lodash'; +import { deepFreeze } from '@kbn/std'; import { DiscoveredPlugin, PluginName } from '../../server'; import { EnvironmentMode, @@ -25,7 +26,6 @@ import { UiSettingsParams, UserProvidedValues, } from '../../server/types'; -import { deepFreeze } from '../../utils/'; import { AppCategory } from '../'; export interface InjectedPluginMetadata { diff --git a/src/core/public/integrations/integrations_service.mock.ts b/src/core/public/integrations/integrations_service.mock.ts index 4f6ca0fb684597..88458ebb747fe0 100644 --- a/src/core/public/integrations/integrations_service.mock.ts +++ b/src/core/public/integrations/integrations_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { IntegrationsService } from './integrations_service'; type IntegrationsServiceContract = PublicMethodsOf; diff --git a/src/core/public/notifications/notifications_service.mock.ts b/src/core/public/notifications/notifications_service.mock.ts index 464f47e20aa3b0..990ab479d35c3c 100644 --- a/src/core/public/notifications/notifications_service.mock.ts +++ b/src/core/public/notifications/notifications_service.mock.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { PublicMethodsOf } from '@kbn/utility-types'; import { NotificationsService, NotificationsSetup, diff --git a/src/core/public/overlays/banners/banners_service.mock.ts b/src/core/public/overlays/banners/banners_service.mock.ts index 14041b2720877f..acd38af383d677 100644 --- a/src/core/public/overlays/banners/banners_service.mock.ts +++ b/src/core/public/overlays/banners/banners_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { OverlayBannersStart, OverlayBannersService } from './banners_service'; const createStartContractMock = () => { diff --git a/src/core/public/overlays/flyout/flyout_service.mock.ts b/src/core/public/overlays/flyout/flyout_service.mock.ts index 91544500713d6f..90dcb308de1b9e 100644 --- a/src/core/public/overlays/flyout/flyout_service.mock.ts +++ b/src/core/public/overlays/flyout/flyout_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { FlyoutService, OverlayFlyoutStart } from './flyout_service'; const createStartContractMock = () => { diff --git a/src/core/public/overlays/modal/modal_service.mock.ts b/src/core/public/overlays/modal/modal_service.mock.ts index 5ac49874dcf933..0af207d168fae3 100644 --- a/src/core/public/overlays/modal/modal_service.mock.ts +++ b/src/core/public/overlays/modal/modal_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { ModalService, OverlayModalStart } from './modal_service'; const createStartContractMock = () => { diff --git a/src/core/public/overlays/overlay_service.mock.ts b/src/core/public/overlays/overlay_service.mock.ts index e29247494034f9..66ba36b20b45cd 100644 --- a/src/core/public/overlays/overlay_service.mock.ts +++ b/src/core/public/overlays/overlay_service.mock.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { PublicMethodsOf } from '@kbn/utility-types'; import { OverlayService, OverlayStart } from './overlay_service'; import { overlayBannersServiceMock } from './banners/banners_service.mock'; import { overlayFlyoutServiceMock } from './flyout/flyout_service.mock'; diff --git a/src/core/public/plugins/plugins_service.mock.ts b/src/core/public/plugins/plugins_service.mock.ts index 900f20422b8260..0625d2c3ec4b24 100644 --- a/src/core/public/plugins/plugins_service.mock.ts +++ b/src/core/public/plugins/plugins_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { PluginsService, PluginsServiceSetup } from './plugins_service'; const createSetupContractMock = () => { diff --git a/src/core/public/plugins/plugins_service.ts b/src/core/public/plugins/plugins_service.ts index f9bc40ca526019..87219f4c0bdd3b 100644 --- a/src/core/public/plugins/plugins_service.ts +++ b/src/core/public/plugins/plugins_service.ts @@ -17,6 +17,7 @@ * under the License. */ +import { withTimeout } from '@kbn/std'; import { PluginName, PluginOpaqueId } from '../../server'; import { CoreService } from '../../types'; import { CoreContext } from '../core_system'; @@ -28,7 +29,6 @@ import { } from './plugin_context'; import { InternalCoreSetup, InternalCoreStart } from '../core_system'; import { InjectedPluginMetadata } from '../injected_metadata'; -import { withTimeout } from '../../utils'; const Sec = 1000; /** @internal */ diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index d90b8f780b6744..d0b9e115bd524f 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -7,7 +7,6 @@ import { Action } from 'history'; import { ApiResponse } from '@elastic/elasticsearch/lib/Transport'; import Boom from 'boom'; -import { ErrorToastOptions as ErrorToastOptions_2 } from 'src/core/public/notifications'; import { EuiBreadcrumb } from '@elastic/eui'; import { EuiButtonEmptyProps } from '@elastic/eui'; import { EuiConfirmModalProps } from '@elastic/eui'; @@ -22,15 +21,13 @@ import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; import { MaybePromise } from '@kbn/utility-types'; import { Observable } from 'rxjs'; -import { ParsedQuery } from 'query-string'; import { Path } from 'history'; +import { PublicMethodsOf } from '@kbn/utility-types'; import { PublicUiSettingsParams as PublicUiSettingsParams_2 } from 'src/core/server/types'; import React from 'react'; import { RecursiveReadonly } from '@kbn/utility-types'; import * as Rx from 'rxjs'; -import { SavedObject as SavedObject_2 } from 'src/core/server'; import { ShallowPromise } from '@kbn/utility-types'; -import { ToastInputFields as ToastInputFields_2 } from 'src/core/public/notifications'; import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; @@ -187,9 +184,6 @@ export type AppUpdatableFields = Pick Partial | undefined; -// @public -export function assertNever(x: never): never; - // @public export interface Capabilities { [key: string]: Record>; @@ -272,10 +266,13 @@ export interface ChromeNavControl { // @public export interface ChromeNavControls { + // @internal (undocumented) + getCenter$(): Observable; // @internal (undocumented) getLeft$(): Observable; // @internal (undocumented) getRight$(): Observable; + registerCenter(navControl: ChromeNavControl): void; registerLeft(navControl: ChromeNavControl): void; registerRight(navControl: ChromeNavControl): void; } @@ -345,7 +342,6 @@ export interface ChromeStart { getHelpExtension$(): Observable; getIsNavDrawerLocked$(): Observable; getIsVisible$(): Observable; - getNavType$(): Observable; navControls: ChromeNavControls; navLinks: ChromeNavLinks; recentlyAccessed: ChromeRecentlyAccessed; @@ -444,9 +440,6 @@ export class CoreSystem { stop(): void; } -// @public -export function deepFreeze(object: T): RecursiveReadonly; - // @internal (undocumented) export const DEFAULT_APP_CATEGORIES: Readonly<{ kibana: { @@ -616,16 +609,6 @@ export interface FatalErrorsSetup { // @public export type FatalErrorsStart = FatalErrorsSetup; -// @public (undocumented) -export type Freezable = { - [k: string]: any; -} | any[]; - -// @public -export function getFlattenedObject(rootValue: Record): { - [key: string]: any; -}; - // @public export type HandlerContextType> = T extends HandlerFunction ? U : never; @@ -837,9 +820,6 @@ export interface ImageValidation { }; } -// @public -export function isRelativeUrl(candidatePath: string): boolean; - // @public export type IToasts = Pick; @@ -868,9 +848,6 @@ export interface IUiSettingsClient { set: (key: string, value: any) => Promise; } -// @public -export function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulParts) => Partial | void): string; - // @public export type MountPoint = (element: T) => UnmountCallback; @@ -1447,26 +1424,6 @@ export type UnmountCallback = () => void; // @public export const URL_MAX_LENGTH: number; -// @public -export interface URLMeaningfulParts { - // (undocumented) - auth?: string | null; - // (undocumented) - hash?: string | null; - // (undocumented) - hostname?: string | null; - // (undocumented) - pathname?: string | null; - // (undocumented) - port?: string | null; - // (undocumented) - protocol?: string | null; - // (undocumented) - query: ParsedQuery; - // (undocumented) - slashes?: boolean | null; -} - // @public export interface UserProvidedValues { // (undocumented) diff --git a/src/core/public/rendering/_base.scss b/src/core/public/rendering/_base.scss index 211e9c03beea50..b806ac270331d2 100644 --- a/src/core/public/rendering/_base.scss +++ b/src/core/public/rendering/_base.scss @@ -1,5 +1,4 @@ -@import '@elastic/eui/src/global_styling/variables/header'; -@import '@elastic/eui/src/components/nav_drawer/variables'; +@include euiHeaderAffordForFixed($kbnHeaderOffset); /** * stretch the root element of the Kibana application to set the base-size that @@ -12,74 +11,11 @@ min-height: 100%; } -// TODO #64541 -// Delete this block -.chrHeaderWrapper:not(.headerWrapper) ~ .app-wrapper { +.app-wrapper { display: flex; flex-flow: column nowrap; - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - z-index: 5; margin: 0 auto; - - &:not(.hidden-chrome) { - top: $euiHeaderChildSize; - left: $euiHeaderChildSize; - - // HOTFIX: Temporary fix for flyouts not inside portals - // SASSTODO: Find an actual solution - .euiFlyout { - top: $euiHeaderChildSize; - height: calc(100% - #{$euiHeaderChildSize}); - } - - @include euiBreakpoint('xs', 's') { - left: 0; - } - } - - /** - * 1. Dirty, but we need to override the .kbnGlobalNav-isOpen state - * when we're looking at the log-in screen. - */ - &.hidden-chrome { - left: 0 !important; /* 1 */ - } - - .navbar-right { - margin-right: 0; - } -} - -// TODO #64541 -// Delete this block -@include euiBreakpoint('xl') { - .chrHeaderWrapper--navIsLocked:not(.headerWrapper) { - ~ .app-wrapper:not(.hidden-chrome) { - // Shrink the content from the left so it's no longer overlapped by the nav drawer (ALWAYS) - left: $euiNavDrawerWidthExpanded !important; // sass-lint:disable-line no-important - transition: left $euiAnimSpeedFast $euiAnimSlightResistance; - } - } -} - -// TODO #64541 -// Remove .headerWrapper and header conditionals -.headerWrapper ~ .app-wrapper, -:not(header) ~ .app-wrapper { - display: flex; - flex-flow: column nowrap; - margin: 0 auto; - min-height: calc(100vh - #{$euiHeaderHeightCompensation}); - - @include internetExplorerOnly { - // IE specific bug with min-height in flex container, described in the next link - // https://github.com/philipwalton/flexbugs#3-min-height-on-a-flex-container-wont-apply-to-its-flex-items - height: calc(100vh - #{$euiHeaderHeightCompensation}); - } + min-height: calc(100vh - #{$kbnHeaderOffset}); &.hidden-chrome { min-height: 100vh; diff --git a/src/core/public/rendering/rendering_service.mock.ts b/src/core/public/rendering/rendering_service.mock.ts index bb4723e69ab5e8..803a7dae91a408 100644 --- a/src/core/public/rendering/rendering_service.mock.ts +++ b/src/core/public/rendering/rendering_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { RenderingService } from './rendering_service'; type RenderingServiceContract = PublicMethodsOf; diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index 351020004b0e70..5a8949ca2f55ff 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -19,6 +19,7 @@ import { cloneDeep, pick, throttle } from 'lodash'; import { resolve as resolveUrl } from 'url'; +import type { PublicMethodsOf } from '@kbn/utility-types'; import { SavedObject, diff --git a/src/core/public/styles/_base.scss b/src/core/public/styles/_base.scss index 427c6b7735435a..bfb07c1b51427d 100644 --- a/src/core/public/styles/_base.scss +++ b/src/core/public/styles/_base.scss @@ -7,17 +7,6 @@ // Application Layout -// chrome-context -// TODO #64541 -// Delete this block -.chrHeaderWrapper:not(.headerWrapper) .content { - display: flex; - flex-flow: row nowrap; - width: 100%; - height: 100%; - overflow: hidden; -} - .application, .app-container { > * { diff --git a/src/core/public/ui_settings/ui_settings_service.mock.ts b/src/core/public/ui_settings/ui_settings_service.mock.ts index e1f7eeff934719..1921c26e579719 100644 --- a/src/core/public/ui_settings/ui_settings_service.mock.ts +++ b/src/core/public/ui_settings/ui_settings_service.mock.ts @@ -17,6 +17,7 @@ * under the License. */ import * as Rx from 'rxjs'; +import type { PublicMethodsOf } from '@kbn/utility-types'; import { UiSettingsService } from './'; import { IUiSettingsClient } from './types'; diff --git a/src/core/server/audit_trail/audit_trail_service.mock.ts b/src/core/server/audit_trail/audit_trail_service.mock.ts index d63b3539e5cdc7..4c9c064840750c 100644 --- a/src/core/server/audit_trail/audit_trail_service.mock.ts +++ b/src/core/server/audit_trail/audit_trail_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { AuditTrailSetup, AuditTrailStart, Auditor } from './types'; import { AuditTrailService } from './audit_trail_service'; diff --git a/src/core/server/capabilities/capabilities_service.mock.ts b/src/core/server/capabilities/capabilities_service.mock.ts index 3f31eca8339d8d..7d134f9592dc72 100644 --- a/src/core/server/capabilities/capabilities_service.mock.ts +++ b/src/core/server/capabilities/capabilities_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { CapabilitiesService, CapabilitiesSetup, CapabilitiesStart } from './capabilities_service'; const createSetupContractMock = () => { diff --git a/src/core/server/config/config_service.ts b/src/core/server/config/config_service.ts index bceba420bb6ce3..d77ee980b0491d 100644 --- a/src/core/server/config/config_service.ts +++ b/src/core/server/config/config_service.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { Type } from '@kbn/config-schema'; import { isEqual } from 'lodash'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; diff --git a/src/core/server/config/deprecation/deprecation_factory.ts b/src/core/server/config/deprecation/deprecation_factory.ts index cbc9984924c5dd..0598347d2cffcc 100644 --- a/src/core/server/config/deprecation/deprecation_factory.ts +++ b/src/core/server/config/deprecation/deprecation_factory.ts @@ -17,10 +17,10 @@ * under the License. */ -import { set } from '@elastic/safer-lodash-set'; import { get } from 'lodash'; +import { set } from '@elastic/safer-lodash-set'; +import { unset } from '@kbn/std'; import { ConfigDeprecation, ConfigDeprecationLogger, ConfigDeprecationFactory } from './types'; -import { unset } from '../../../utils'; const _rename = ( config: Record, diff --git a/src/core/server/config/object_to_config_adapter.ts b/src/core/server/config/object_to_config_adapter.ts index 50b31722dceeb2..c4d6ac02ccf052 100644 --- a/src/core/server/config/object_to_config_adapter.ts +++ b/src/core/server/config/object_to_config_adapter.ts @@ -17,10 +17,10 @@ * under the License. */ -import { set } from '@elastic/safer-lodash-set'; import { cloneDeep, get, has } from 'lodash'; +import { set } from '@elastic/safer-lodash-set'; +import { getFlattenedObject } from '@kbn/std'; -import { getFlattenedObject } from '../../utils'; import { Config, ConfigPath } from './'; /** diff --git a/src/core/server/config/raw_config_service.mock.ts b/src/core/server/config/raw_config_service.mock.ts index fdcb17395aaadd..73a3b5cc9e4d0c 100644 --- a/src/core/server/config/raw_config_service.mock.ts +++ b/src/core/server/config/raw_config_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { RawConfigService } from './raw_config_service'; import { Observable, of } from 'rxjs'; diff --git a/src/core/server/context/context_service.mock.ts b/src/core/server/context/context_service.mock.ts index eb55ced69dc048..a8d895acad6245 100644 --- a/src/core/server/context/context_service.mock.ts +++ b/src/core/server/context/context_service.mock.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { PublicMethodsOf } from '@kbn/utility-types'; import { ContextService, ContextSetup } from './context_service'; import { contextMock } from '../../utils/context.mock'; diff --git a/src/core/server/core_context.mock.ts b/src/core/server/core_context.mock.ts index f870d30528df42..2b887358818e3c 100644 --- a/src/core/server/core_context.mock.ts +++ b/src/core/server/core_context.mock.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ - import { CoreContext } from './core_context'; import { getEnvOptions } from './config/__mocks__/env'; import { Env, IConfigService } from './config'; diff --git a/src/core/server/elasticsearch/client/mocks.ts b/src/core/server/elasticsearch/client/mocks.ts index 2f2ca08fee6f20..6fb3dc090bfb4f 100644 --- a/src/core/server/elasticsearch/client/mocks.ts +++ b/src/core/server/elasticsearch/client/mocks.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ - import { Client, ApiResponse } from '@elastic/elasticsearch'; import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; import { ElasticsearchClient } from './types'; @@ -75,7 +74,7 @@ export type ElasticsearchClientMock = DeeplyMockedKeys; const createClientMock = (): ElasticsearchClientMock => (createInternalClientMock() as unknown) as ElasticsearchClientMock; -interface ScopedClusterClientMock { +export interface ScopedClusterClientMock { asInternalUser: ElasticsearchClientMock; asCurrentUser: ElasticsearchClientMock; } diff --git a/src/core/server/elasticsearch/elasticsearch_service.mock.ts b/src/core/server/elasticsearch/elasticsearch_service.mock.ts index 26186efc286bfa..ad80928d2fe5ef 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.mock.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.mock.ts @@ -18,6 +18,8 @@ */ import { BehaviorSubject } from 'rxjs'; +import type { PublicMethodsOf } from '@kbn/utility-types'; + import { ILegacyClusterClient, ILegacyCustomClusterClient } from './legacy'; import { elasticsearchClientMock, @@ -32,7 +34,7 @@ import { InternalElasticsearchServiceSetup, ElasticsearchStatusMeta } from './ty import { NodesVersionCompatibility } from './version_check/ensure_es_version'; import { ServiceStatus, ServiceStatusLevels } from '../status'; -interface MockedElasticSearchServiceSetup { +export interface MockedElasticSearchServiceSetup { legacy: { config$: BehaviorSubject; createClient: jest.Mock; diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index 2cc065aaaaeb14..5d07840e8bda79 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -19,9 +19,9 @@ import { Observable, Subject } from 'rxjs'; import { first, map, shareReplay, takeUntil } from 'rxjs/operators'; +import { merge } from '@kbn/std'; import { CoreService } from '../../types'; -import { merge } from '../../utils'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; import { diff --git a/src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts b/src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts index 3dbc1c2c647a99..6896c0a2e301f2 100644 --- a/src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts +++ b/src/core/server/elasticsearch/legacy/elasticsearch_client_config.ts @@ -22,7 +22,7 @@ import { cloneDeep } from 'lodash'; import { Duration } from 'moment'; import { checkServerIdentity } from 'tls'; import url from 'url'; -import { pick } from '../../../utils'; +import { pick } from '@kbn/std'; import { Logger } from '../../logging'; import { ElasticsearchConfig } from '../elasticsearch_config'; diff --git a/src/core/server/environment/environment_service.mock.ts b/src/core/server/environment/environment_service.mock.ts index 8bf726b4a6388b..a956e369ba4a72 100644 --- a/src/core/server/environment/environment_service.mock.ts +++ b/src/core/server/environment/environment_service.mock.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { PublicMethodsOf } from '@kbn/utility-types'; import { EnvironmentService, InternalEnvironmentServiceSetup } from './environment_service'; diff --git a/src/core/server/environment/environment_service.test.ts b/src/core/server/environment/environment_service.test.ts index 06fd250ebe4f93..f6cffb5e26a9e1 100644 --- a/src/core/server/environment/environment_service.test.ts +++ b/src/core/server/environment/environment_service.test.ts @@ -21,6 +21,7 @@ import { BehaviorSubject } from 'rxjs'; import { EnvironmentService } from './environment_service'; import { resolveInstanceUuid } from './resolve_uuid'; import { createDataFolder } from './create_data_folder'; +import { writePidFile } from './write_pid_file'; import { CoreContext } from '../core_context'; import { configServiceMock } from '../config/config_service.mock'; @@ -35,12 +36,20 @@ jest.mock('./create_data_folder', () => ({ createDataFolder: jest.fn(), })); +jest.mock('./write_pid_file', () => ({ + writePidFile: jest.fn(), +})); + const pathConfig = { data: 'data-folder', }; const serverConfig = { uuid: 'SOME_UUID', }; +const pidConfig = { + file: '/pid/file', + exclusive: 'false', +}; const getConfigService = () => { const configService = configServiceMock.create(); @@ -51,6 +60,9 @@ const getConfigService = () => { if (path === 'server') { return new BehaviorSubject(serverConfig); } + if (path === 'pid') { + return new BehaviorSubject(pidConfig); + } return new BehaviorSubject({}); }); return configService; @@ -76,7 +88,7 @@ describe('UuidService', () => { expect(resolveInstanceUuid).toHaveBeenCalledWith({ pathConfig, serverConfig, - logger: logger.get('uuid'), + logger: logger.get('environment'), }); }); @@ -86,7 +98,17 @@ describe('UuidService', () => { expect(createDataFolder).toHaveBeenCalledTimes(1); expect(createDataFolder).toHaveBeenCalledWith({ pathConfig, - logger: logger.get('uuid'), + logger: logger.get('environment'), + }); + }); + + it('calls writePidFile with correct parameters', async () => { + const service = new EnvironmentService(coreContext); + await service.setup(); + expect(writePidFile).toHaveBeenCalledTimes(1); + expect(writePidFile).toHaveBeenCalledWith({ + pidConfig, + logger: logger.get('environment'), }); }); diff --git a/src/core/server/environment/environment_service.ts b/src/core/server/environment/environment_service.ts index 6a0b1122c70534..caa4f34bcfaa77 100644 --- a/src/core/server/environment/environment_service.ts +++ b/src/core/server/environment/environment_service.ts @@ -23,8 +23,10 @@ import { Logger } from '../logging'; import { IConfigService } from '../config'; import { PathConfigType, config as pathConfigDef } from '../path'; import { HttpConfigType, config as httpConfigDef } from '../http'; +import { PidConfigType, config as pidConfigDef } from './pid_config'; import { resolveInstanceUuid } from './resolve_uuid'; import { createDataFolder } from './create_data_folder'; +import { writePidFile } from './write_pid_file'; /** * @internal @@ -43,17 +45,24 @@ export class EnvironmentService { private uuid: string = ''; constructor(core: CoreContext) { - this.log = core.logger.get('uuid'); + this.log = core.logger.get('environment'); this.configService = core.configService; } public async setup() { - const [pathConfig, serverConfig] = await Promise.all([ + const [pathConfig, serverConfig, pidConfig] = await Promise.all([ this.configService.atPath(pathConfigDef.path).pipe(take(1)).toPromise(), this.configService.atPath(httpConfigDef.path).pipe(take(1)).toPromise(), + this.configService.atPath(pidConfigDef.path).pipe(take(1)).toPromise(), ]); + // was present in the legacy `pid` file. + process.on('unhandledRejection', (reason) => { + this.log.warn(`Detected an unhandled Promise rejection.\n${reason}`); + }); + await createDataFolder({ pathConfig, logger: this.log }); + await writePidFile({ pidConfig, logger: this.log }); this.uuid = await resolveInstanceUuid({ pathConfig, diff --git a/src/core/server/environment/fs.ts b/src/core/server/environment/fs.ts index dc040ccb736159..b79c70dbee280d 100644 --- a/src/core/server/environment/fs.ts +++ b/src/core/server/environment/fs.ts @@ -23,3 +23,4 @@ import { promisify } from 'util'; export const readFile = promisify(Fs.readFile); export const writeFile = promisify(Fs.writeFile); export const mkdir = promisify(Fs.mkdir); +export const exists = promisify(Fs.exists); diff --git a/src/core/server/environment/index.ts b/src/core/server/environment/index.ts index 57a26d5ea3c797..92b57c6af2fff2 100644 --- a/src/core/server/environment/index.ts +++ b/src/core/server/environment/index.ts @@ -18,3 +18,4 @@ */ export { EnvironmentService, InternalEnvironmentServiceSetup } from './environment_service'; +export { config, PidConfigType } from './pid_config'; diff --git a/src/core/server/environment/pid_config.ts b/src/core/server/environment/pid_config.ts new file mode 100644 index 00000000000000..ee9963016717e2 --- /dev/null +++ b/src/core/server/environment/pid_config.ts @@ -0,0 +1,30 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { TypeOf, schema } from '@kbn/config-schema'; + +export const config = { + path: 'pid', + schema: schema.object({ + file: schema.maybe(schema.string()), + exclusive: schema.boolean({ defaultValue: false }), + }), +}; + +export type PidConfigType = TypeOf; diff --git a/src/core/server/environment/write_pid_file.test.ts b/src/core/server/environment/write_pid_file.test.ts new file mode 100644 index 00000000000000..f9eb78a4869701 --- /dev/null +++ b/src/core/server/environment/write_pid_file.test.ts @@ -0,0 +1,144 @@ +/* + * 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 { writeFile, exists } from './fs'; +import { writePidFile } from './write_pid_file'; +import { loggingSystemMock } from '../logging/logging_system.mock'; + +jest.mock('./fs', () => ({ + writeFile: jest.fn(), + exists: jest.fn(), +})); + +const writeFileMock = writeFile as jest.MockedFunction; +const existsMock = exists as jest.MockedFunction; + +const pid = String(process.pid); + +describe('writePidFile', () => { + let logger: ReturnType; + + beforeEach(() => { + logger = loggingSystemMock.createLogger(); + jest.spyOn(process, 'once'); + + writeFileMock.mockImplementation(() => Promise.resolve()); + existsMock.mockImplementation(() => Promise.resolve(false)); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + const allLogs = () => + Object.entries(loggingSystemMock.collect(logger)).reduce((messages, [key, value]) => { + return [...messages, ...(key === 'log' ? [] : (value as any[]).map(([msg]) => [key, msg]))]; + }, [] as any[]); + + it('does nothing if `pid.file` is not set', async () => { + await writePidFile({ + pidConfig: { + file: undefined, + exclusive: false, + }, + logger, + }); + expect(writeFile).not.toHaveBeenCalled(); + expect(process.once).not.toHaveBeenCalled(); + expect(allLogs()).toMatchInlineSnapshot(`Array []`); + }); + + it('writes the pid file to `pid.file`', async () => { + existsMock.mockResolvedValue(false); + + await writePidFile({ + pidConfig: { + file: '/pid-file', + exclusive: false, + }, + logger, + }); + + expect(writeFile).toHaveBeenCalledTimes(1); + expect(writeFile).toHaveBeenCalledWith('/pid-file', pid); + + expect(process.once).toHaveBeenCalledTimes(2); + expect(process.once).toHaveBeenCalledWith('exit', expect.any(Function)); + expect(process.once).toHaveBeenCalledWith('SIGINT', expect.any(Function)); + + expect(allLogs()).toMatchInlineSnapshot(` + Array [ + Array [ + "debug", + "wrote pid file to /pid-file", + ], + ] + `); + }); + + it('throws an error if the file exists and `pid.exclusive is true`', async () => { + existsMock.mockResolvedValue(true); + + await expect( + writePidFile({ + pidConfig: { + file: '/pid-file', + exclusive: true, + }, + logger, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"pid file already exists at /pid-file"`); + + expect(writeFile).not.toHaveBeenCalled(); + expect(process.once).not.toHaveBeenCalled(); + expect(allLogs()).toMatchInlineSnapshot(`Array []`); + }); + + it('logs a warning if the file exists and `pid.exclusive` is false', async () => { + existsMock.mockResolvedValue(true); + + await writePidFile({ + pidConfig: { + file: '/pid-file', + exclusive: false, + }, + logger, + }); + + expect(writeFile).toHaveBeenCalledTimes(1); + expect(writeFile).toHaveBeenCalledWith('/pid-file', pid); + + expect(process.once).toHaveBeenCalledTimes(2); + expect(process.once).toHaveBeenCalledWith('exit', expect.any(Function)); + expect(process.once).toHaveBeenCalledWith('SIGINT', expect.any(Function)); + + expect(allLogs()).toMatchInlineSnapshot(` + Array [ + Array [ + "debug", + "wrote pid file to /pid-file", + ], + Array [ + "warn", + "pid file already exists at /pid-file", + ], + ] + `); + }); +}); diff --git a/src/core/server/environment/write_pid_file.ts b/src/core/server/environment/write_pid_file.ts new file mode 100644 index 00000000000000..6ee20af02d7b0f --- /dev/null +++ b/src/core/server/environment/write_pid_file.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 { unlinkSync as unlink } from 'fs'; +import once from 'lodash/once'; +import { Logger } from '../logging'; +import { writeFile, exists } from './fs'; +import { PidConfigType } from './pid_config'; + +export const writePidFile = async ({ + pidConfig, + logger, +}: { + pidConfig: PidConfigType; + logger: Logger; +}) => { + const path = pidConfig.file; + if (!path) { + return; + } + + const pid = String(process.pid); + + if (await exists(path)) { + const message = `pid file already exists at ${path}`; + if (pidConfig.exclusive) { + throw new Error(message); + } else { + logger.warn(message, { path, pid }); + } + } + + await writeFile(path, pid); + + logger.debug(`wrote pid file to ${path}`, { path, pid }); + + const clean = once(() => { + unlink(path); + }); + + process.once('exit', clean); // for "natural" exits + process.once('SIGINT', () => { + // for Ctrl-C exits + clean(); + // resend SIGINT + process.kill(process.pid, 'SIGINT'); + }); +}; diff --git a/src/core/server/http/base_path_service.ts b/src/core/server/http/base_path_service.ts index 093d73b2da3bf3..059eb36f42dd5b 100644 --- a/src/core/server/http/base_path_service.ts +++ b/src/core/server/http/base_path_service.ts @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -import { ensureRawRequest, KibanaRequest, LegacyRequest } from './router'; +import { modifyUrl } from '@kbn/std'; -import { modifyUrl } from '../../utils'; +import { ensureRawRequest, KibanaRequest, LegacyRequest } from './router'; /** * Access or manipulate the Kibana base path diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 676cee1954c59a..3e38f6a6d384dd 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -18,6 +18,8 @@ */ import { Server } from 'hapi'; +import type { PublicMethodsOf } from '@kbn/utility-types'; + import { CspConfig } from '../csp'; import { mockRouter, RouterMock } from './router/router.mock'; import { configMock } from '../config/config.mock'; diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index c2fd6539181719..82b141c8e50dd0 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -20,9 +20,9 @@ import { Observable, Subscription, combineLatest } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { Server } from 'hapi'; +import { pick } from '@kbn/std'; import { CoreService } from '../../types'; -import { pick } from '../../utils'; import { Logger, LoggerFactory } from '../logging'; import { ContextSetup } from '../context'; import { Env } from '../config'; diff --git a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts index a1401ba73813b3..8d70b5c3ad119d 100644 --- a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts +++ b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts @@ -20,7 +20,6 @@ import supertest from 'supertest'; import { BehaviorSubject } from 'rxjs'; import { ByteSizeValue } from '@kbn/config-schema'; -import pkg from '../../../../../package.json'; import { createHttpServer } from '../test_utils'; import { HttpService } from '../http_service'; @@ -30,6 +29,9 @@ import { IRouter, RouteRegistrar } from '../router'; import { configServiceMock } from '../../config/config_service.mock'; import { contextServiceMock } from '../../context/context_service.mock'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const pkg = require('../../../../../package.json'); + const actualVersion = pkg.version; const versionHeader = 'kbn-version'; const xsrfHeader = 'kbn-xsrf'; diff --git a/src/core/server/http/router/headers.ts b/src/core/server/http/router/headers.ts index f27f5e937b2c04..498dd153a43dd7 100644 --- a/src/core/server/http/router/headers.ts +++ b/src/core/server/http/router/headers.ts @@ -17,8 +17,7 @@ * under the License. */ import { IncomingHttpHeaders } from 'http'; - -import { pick } from '../../../utils'; +import { pick } from '@kbn/std'; /** * Creates a Union type of all known keys of a given interface. diff --git a/src/core/server/http/router/request.ts b/src/core/server/http/router/request.ts index 76f8761a7e998b..e04f8585981b52 100644 --- a/src/core/server/http/router/request.ts +++ b/src/core/server/http/router/request.ts @@ -23,8 +23,8 @@ import { Request, RouteOptionsApp, ApplicationState } from 'hapi'; import { Observable, fromEvent, merge } from 'rxjs'; import { shareReplay, first, takeUntil } from 'rxjs/operators'; import { RecursiveReadonly } from '@kbn/utility-types'; +import { deepFreeze } from '@kbn/std'; -import { deepFreeze } from '../../../utils'; import { Headers } from './headers'; import { RouteMethod, RouteConfigOptions, validBodyOutput, isSafeMethod } from './route'; import { KibanaSocket, IKibanaSocket } from './socket'; diff --git a/src/core/server/index.ts b/src/core/server/index.ts index d127471348d9f7..01797d073ae2e4 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -322,16 +322,7 @@ export { MetricsServiceSetup, } from './metrics'; -export { - DEFAULT_APP_CATEGORIES, - getFlattenedObject, - URLMeaningfulParts, - modifyUrl, - isRelativeUrl, - Freezable, - deepFreeze, - assertNever, -} from '../utils'; +export { DEFAULT_APP_CATEGORIES } from '../utils'; export { SavedObject, diff --git a/src/core/server/legacy/cli.js b/src/core/server/legacy/cli.js new file mode 100644 index 00000000000000..28e14d28eecd37 --- /dev/null +++ b/src/core/server/legacy/cli.js @@ -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 { startRepl } from '../../../cli/repl'; diff --git a/src/core/server/legacy/cluster_manager.js b/src/core/server/legacy/cluster_manager.js new file mode 100644 index 00000000000000..3c51fd6869a093 --- /dev/null +++ b/src/core/server/legacy/cluster_manager.js @@ -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 { ClusterManager } from '../../../cli/cluster/cluster_manager'; diff --git a/src/core/server/legacy/config/get_unused_config_keys.ts b/src/core/server/legacy/config/get_unused_config_keys.ts index d10c574f049743..c15c3b270df056 100644 --- a/src/core/server/legacy/config/get_unused_config_keys.ts +++ b/src/core/server/legacy/config/get_unused_config_keys.ts @@ -18,8 +18,8 @@ */ import { difference } from 'lodash'; +import { getFlattenedObject } from '@kbn/std'; import { unset } from '../../../../legacy/utils'; -import { getFlattenedObject } from '../../../utils'; import { hasConfigPathIntersection } from '../../config'; import { LegacyPluginSpec, LegacyConfig, LegacyVars } from '../types'; diff --git a/src/core/server/legacy/legacy_service.mock.ts b/src/core/server/legacy/legacy_service.mock.ts index c27f5be04d965f..ab501bd6bb53b0 100644 --- a/src/core/server/legacy/legacy_service.mock.ts +++ b/src/core/server/legacy/legacy_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { LegacyService } from './legacy_service'; import { LegacyConfig, LegacyServiceDiscoverPlugins, LegacyServiceSetupDeps } from './types'; diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index d0492ea88c5e8a..f3ce89f83a610c 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -18,8 +18,7 @@ */ jest.mock('../../../legacy/server/kbn_server'); -jest.mock('../../../cli/cluster/cluster_manager'); - +jest.mock('./cluster_manager'); import { findLegacyPluginSpecsMock, logLegacyThirdPartyPluginDeprecationWarningMock, @@ -27,7 +26,8 @@ import { import { BehaviorSubject, throwError } from 'rxjs'; -import { ClusterManager as MockClusterManager } from '../../../cli/cluster/cluster_manager'; +// @ts-expect-error js file to remove TS dependency on cli +import { ClusterManager as MockClusterManager } from './cluster_manager'; import KbnServer from '../../../legacy/server/kbn_server'; import { Config, Env, ObjectToConfigAdapter } from '../config'; import { getEnvOptions } from '../config/__mocks__/env'; diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 6e6d5cfc24340e..d07c81b8b5ebe3 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { combineLatest, ConnectableObservable, EMPTY, Observable, Subscription } from 'rxjs'; import { first, map, publishReplay, tap } from 'rxjs/operators'; @@ -233,7 +233,7 @@ export class LegacyService implements CoreService { : EMPTY; // eslint-disable-next-line @typescript-eslint/no-var-requires - const { ClusterManager } = require('../../../cli/cluster/cluster_manager'); + const { ClusterManager } = require('./cluster_manager'); return new ClusterManager( this.coreContext.env.cliArgs, config, @@ -368,7 +368,7 @@ export class LegacyService implements CoreService { // We only want one REPL. if (this.coreContext.env.cliArgs.repl && process.env.kbnWorkerType === 'server') { // eslint-disable-next-line @typescript-eslint/no-var-requires - require('../../../cli/repl').startRepl(kbnServer); + require('./cli').startRepl(kbnServer); } const { autoListen } = await this.httpConfig$.pipe(first()).toPromise(); diff --git a/src/core/server/legacy/plugins/collect_ui_exports.js b/src/core/server/legacy/plugins/collect_ui_exports.js new file mode 100644 index 00000000000000..842ab554d79d14 --- /dev/null +++ b/src/core/server/legacy/plugins/collect_ui_exports.js @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +export { collectUiExports } from '../../../../legacy/ui/ui_exports/collect_ui_exports'; diff --git a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts b/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts index 82e04496ffc3e6..cb4277b130a88c 100644 --- a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts +++ b/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts @@ -25,12 +25,12 @@ import { defaultConfig, // @ts-expect-error } from '../../../../legacy/plugin_discovery/find_plugin_specs.js'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { collectUiExports as collectLegacyUiExports } from '../../../../legacy/ui/ui_exports/collect_ui_exports'; +// @ts-expect-error +import { collectUiExports as collectLegacyUiExports } from './collect_ui_exports'; import { LoggerFactory } from '../../logging'; import { PackageInfo } from '../../config'; -import { LegacyPluginSpec, LegacyPluginPack, LegacyConfig } from '../types'; +import { LegacyUiExports, LegacyPluginSpec, LegacyPluginPack, LegacyConfig } from '../types'; export async function findLegacyPluginSpecs( settings: unknown, @@ -123,7 +123,7 @@ export async function findLegacyPluginSpecs( spec$.pipe(toArray()), log$.pipe(toArray()) ).toPromise(); - const uiExports = collectLegacyUiExports(pluginSpecs); + const uiExports: LegacyUiExports = collectLegacyUiExports(pluginSpecs); return { disabledPluginSpecs, diff --git a/src/core/server/logging/appenders/appenders.ts b/src/core/server/logging/appenders/appenders.ts index edfce4988275ac..9c19ee2bd8be54 100644 --- a/src/core/server/logging/appenders/appenders.ts +++ b/src/core/server/logging/appenders/appenders.ts @@ -18,8 +18,8 @@ */ import { schema } from '@kbn/config-schema'; +import { assertNever } from '@kbn/std'; -import { assertNever } from '../../../utils'; import { LegacyAppender, LegacyAppenderConfig, diff --git a/src/core/server/logging/layouts/layouts.ts b/src/core/server/logging/layouts/layouts.ts index 124c007bae1040..03e8adbee6311f 100644 --- a/src/core/server/logging/layouts/layouts.ts +++ b/src/core/server/logging/layouts/layouts.ts @@ -18,8 +18,8 @@ */ import { schema } from '@kbn/config-schema'; +import { assertNever } from '@kbn/std'; -import { assertNever } from '../../../utils'; import { LogRecord } from '../log_record'; import { JsonLayout, JsonLayoutConfigType } from './json_layout'; import { PatternLayout, PatternLayoutConfigType } from './pattern_layout'; diff --git a/src/core/server/logging/log_level.ts b/src/core/server/logging/log_level.ts index 577239ddae8e55..165e56e632d6db 100644 --- a/src/core/server/logging/log_level.ts +++ b/src/core/server/logging/log_level.ts @@ -17,7 +17,7 @@ * under the License. */ -import { assertNever } from '../../utils'; +import { assertNever } from '@kbn/std'; /** * Possible log level string values. diff --git a/src/core/server/logging/logging_service.mock.ts b/src/core/server/logging/logging_service.mock.ts index 21edbe670eaecd..6a721e134feeda 100644 --- a/src/core/server/logging/logging_service.mock.ts +++ b/src/core/server/logging/logging_service.mock.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { PublicMethodsOf } from '@kbn/utility-types'; import { LoggingService, diff --git a/src/core/server/logging/logging_system.ts b/src/core/server/logging/logging_system.ts index 8aadab83bf7163..a3970b17209508 100644 --- a/src/core/server/logging/logging_system.ts +++ b/src/core/server/logging/logging_system.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { PublicMethodsOf } from '@kbn/utility-types'; import { Appenders, DisposableAppender } from './appenders/appenders'; import { BufferAppender } from './appenders/buffer/buffer_appender'; import { LogLevel } from './log_level'; diff --git a/src/core/server/metrics/metrics_service.mock.ts b/src/core/server/metrics/metrics_service.mock.ts index 2af653004a479e..caa7acc001db30 100644 --- a/src/core/server/metrics/metrics_service.mock.ts +++ b/src/core/server/metrics/metrics_service.mock.ts @@ -17,6 +17,8 @@ * under the License. */ import { BehaviorSubject } from 'rxjs'; +import type { PublicMethodsOf } from '@kbn/utility-types'; + import { MetricsService } from './metrics_service'; import { InternalMetricsServiceSetup, diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index af0b0e19b32275..55e63941523ee2 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -19,6 +19,8 @@ import { map, shareReplay } from 'rxjs/operators'; import { combineLatest } from 'rxjs'; +import { pick, deepFreeze } from '@kbn/std'; + import { CoreContext } from '../core_context'; import { PluginWrapper } from './plugin'; import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service'; @@ -34,7 +36,6 @@ import { ElasticsearchConfigType, config as elasticsearchConfig, } from '../elasticsearch/elasticsearch_config'; -import { pick, deepFreeze } from '../../utils'; import { CoreSetup, CoreStart } from '..'; export interface InstanceInfo { diff --git a/src/core/server/plugins/plugins_service.mock.ts b/src/core/server/plugins/plugins_service.mock.ts index a40566767ddaef..14d6de889dd424 100644 --- a/src/core/server/plugins/plugins_service.mock.ts +++ b/src/core/server/plugins/plugins_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { PluginsService, PluginsServiceSetup } from './plugins_service'; type PluginsServiceMock = jest.Mocked>; diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index 30cd47c9d44e17..e8fe42ee491ca5 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -20,9 +20,10 @@ import Path from 'path'; import { Observable } from 'rxjs'; import { filter, first, map, mergeMap, tap, toArray } from 'rxjs/operators'; +import { pick } from '@kbn/std'; + import { CoreService } from '../../types'; import { CoreContext } from '../core_context'; - import { Logger } from '../logging'; import { discover, PluginDiscoveryError, PluginDiscoveryErrorType } from './discovery'; import { PluginWrapper } from './plugin'; @@ -31,7 +32,6 @@ import { PluginsConfig, PluginsConfigType } from './plugins_config'; import { PluginsSystem } from './plugins_system'; import { InternalCoreSetup, InternalCoreStart } from '../internal_types'; import { IConfigService } from '../config'; -import { pick } from '../../utils'; import { InternalEnvironmentServiceSetup } from '../environment'; /** @internal */ diff --git a/src/core/server/plugins/plugins_system.ts b/src/core/server/plugins/plugins_system.ts index b2acd9a6fd04bb..72d2cfe158b37b 100644 --- a/src/core/server/plugins/plugins_system.ts +++ b/src/core/server/plugins/plugins_system.ts @@ -17,13 +17,13 @@ * under the License. */ +import { withTimeout } from '@kbn/std'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; import { PluginWrapper } from './plugin'; import { DiscoveredPlugin, PluginName } from './types'; import { createPluginSetupContext, createPluginStartContext } from './plugin_context'; import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service'; -import { withTimeout } from '../../utils'; import { PluginDependencies } from '.'; const Sec = 1000; diff --git a/src/core/server/rendering/__mocks__/rendering_service.ts b/src/core/server/rendering/__mocks__/rendering_service.ts index 2e35568873c9a7..179a09b8619b02 100644 --- a/src/core/server/rendering/__mocks__/rendering_service.ts +++ b/src/core/server/rendering/__mocks__/rendering_service.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { RenderingService as Service } from '../rendering_service'; import { InternalRenderingServiceSetup } from '../types'; import { mockRenderingServiceParams } from './params'; diff --git a/src/core/server/saved_objects/es_query.js b/src/core/server/saved_objects/es_query.js new file mode 100644 index 00000000000000..68d582e3cae09f --- /dev/null +++ b/src/core/server/saved_objects/es_query.js @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +// a temporary file to remove circular deps in TS code between platform & data plugin +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +export { esKuery } from '../../../plugins/data/server'; 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 fae33bc050deea..23d8c4518d3ab0 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 @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import type { PublicMethodsOf } from '@kbn/utility-types'; import { KibanaMigrator, KibanaMigratorStatus } from './kibana_migrator'; import { buildActiveMappings } from '../core'; diff --git a/src/core/server/saved_objects/saved_objects_service.mock.ts b/src/core/server/saved_objects/saved_objects_service.mock.ts index e3d44c20dd190d..bd76658c21731b 100644 --- a/src/core/server/saved_objects/saved_objects_service.mock.ts +++ b/src/core/server/saved_objects/saved_objects_service.mock.ts @@ -18,6 +18,7 @@ */ import { BehaviorSubject } from 'rxjs'; +import type { PublicMethodsOf } from '@kbn/utility-types'; import { SavedObjectsService, 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 d0035294226ea9..bb840e459bf225 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.ts +++ b/src/core/server/saved_objects/saved_objects_type_registry.ts @@ -17,7 +17,7 @@ * under the License. */ -import { deepFreeze } from '../../utils'; +import { deepFreeze } from '@kbn/std'; import { SavedObjectsType } from './types'; /** diff --git a/src/core/server/saved_objects/service/lib/filter_utils.test.ts b/src/core/server/saved_objects/service/lib/filter_utils.test.ts index 60e8aa0afdda4c..0608035ce51a27 100644 --- a/src/core/server/saved_objects/service/lib/filter_utils.test.ts +++ b/src/core/server/saved_objects/service/lib/filter_utils.test.ts @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { esKuery } from '../../../../../plugins/data/server'; +// @ts-expect-error no ts +import { esKuery } from '../../es_query'; import { validateFilterKueryNode, validateConvertFilterToKueryNode } from './filter_utils'; diff --git a/src/core/server/saved_objects/service/lib/filter_utils.ts b/src/core/server/saved_objects/service/lib/filter_utils.ts index d19f06d74e4193..be36807f0d02b8 100644 --- a/src/core/server/saved_objects/service/lib/filter_utils.ts +++ b/src/core/server/saved_objects/service/lib/filter_utils.ts @@ -21,8 +21,9 @@ import { set } from '@elastic/safer-lodash-set'; import { get } from 'lodash'; import { SavedObjectsErrorHelpers } from './errors'; import { IndexMapping } from '../../mappings'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { esKuery, KueryNode } from '../../../../../plugins/data/server'; +// @ts-expect-error no ts +import { esKuery } from '../../es_query'; +type KueryNode = any; const astFunctionType = ['is', 'range', 'nested']; diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js index 7d30875b90796a..352ce4c1c16eb2 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -25,8 +25,8 @@ import { encodeHitVersion } from '../../version'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { DocumentMigrator } from '../../migrations/core/document_migrator'; import { elasticsearchClientMock } from '../../../elasticsearch/client/mocks'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { nodeTypes } from '../../../../../plugins/data/common/es_query'; +import { esKuery } from '../../es_query'; +const { nodeTypes } = esKuery; jest.mock('./search_dsl/search_dsl', () => ({ getSearchDsl: jest.fn() })); diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts index 85c47029e36d58..4adc92df318058 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts @@ -17,8 +17,9 @@ * under the License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { esKuery, KueryNode } from '../../../../../../plugins/data/server'; +// @ts-expect-error no ts +import { esKuery } from '../../../es_query'; +type KueryNode = any; import { typeRegistryMock } from '../../../saved_objects_type_registry.mock'; import { getQueryParams } from './query_params'; diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts index 3ff72a86c2f894..642d51c70766e4 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { esKuery, KueryNode } from '../../../../../../plugins/data/server'; +// @ts-expect-error no ts +import { esKuery } from '../../../es_query'; +type KueryNode = any; import { getRootPropertiesObjects, IndexMapping } from '../../../mappings'; import { ISavedObjectTypeRegistry } from '../../../saved_objects_type_registry'; diff --git a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts index ddf20606800c8e..aa79a10b2a9bef 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts @@ -22,10 +22,10 @@ import Boom from 'boom'; import { IndexMapping } from '../../../mappings'; import { getQueryParams } from './query_params'; import { getSortingParams } from './sorting_params'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { KueryNode } from '../../../../../../plugins/data/server'; import { ISavedObjectTypeRegistry } from '../../../saved_objects_type_registry'; +type KueryNode = any; + interface GetSearchDslOptions { type: string | string[]; search?: string; diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index 50c118ca64ffb3..1885f5ec50139b 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -35,8 +35,7 @@ export { import { SavedObject } from '../../types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { KueryNode } from '../../../plugins/data/common'; +type KueryNode = any; export { SavedObjectAttributes, diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index ec457704e89c7d..97276a1425d535 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -40,7 +40,6 @@ import { DeleteScriptParams } from 'elasticsearch'; import { DeleteTemplateParams } from 'elasticsearch'; import { DetailedPeerCertificate } from 'tls'; import { Duration } from 'moment'; -import { ErrorToastOptions } from 'src/core/public/notifications'; import { ExistsParams } from 'elasticsearch'; import { ExplainParams } from 'elasticsearch'; import { FieldStatsParams } from 'elasticsearch'; @@ -106,7 +105,6 @@ import { NodesInfoParams } from 'elasticsearch'; import { NodesStatsParams } from 'elasticsearch'; import { ObjectType } from '@kbn/config-schema'; import { Observable } from 'rxjs'; -import { ParsedQuery } from 'query-string'; import { PeerCertificate } from 'tls'; import { PingParams } from 'elasticsearch'; import { PutScriptParams } from 'elasticsearch'; @@ -119,7 +117,6 @@ import { RenderSearchTemplateParams } from 'elasticsearch'; import { Request } from 'hapi'; import { ResponseObject } from 'hapi'; import { ResponseToolkit } from 'hapi'; -import { SavedObject as SavedObject_2 } from 'src/core/server'; import { SchemaTypeError } from '@kbn/config-schema'; import { ScrollParams } from 'elasticsearch'; import { SearchParams } from 'elasticsearch'; @@ -143,7 +140,6 @@ import { TasksCancelParams } from 'elasticsearch'; import { TasksGetParams } from 'elasticsearch'; import { TasksListParams } from 'elasticsearch'; import { TermvectorsParams } from 'elasticsearch'; -import { ToastInputFields } from 'src/core/public/notifications'; import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; @@ -160,9 +156,6 @@ import { Url } from 'url'; // @public (undocumented) export type AppenderConfigType = ConsoleAppenderConfig | FileAppenderConfig | LegacyAppenderConfig; -// @public -export function assertNever(x: never): never; - // @public @deprecated (undocumented) export interface AssistanceAPIResponse { // (undocumented) @@ -504,9 +497,6 @@ export interface CustomHttpResponseOptions(object: T): RecursiveReadonly; - // @internal (undocumented) export const DEFAULT_APP_CATEGORIES: Readonly<{ kibana: { @@ -716,11 +706,6 @@ export interface FakeRequest { headers: Headers; } -// @public (undocumented) -export type Freezable = { - [k: string]: any; -} | any[]; - // @public export type GetAuthHeaders = (request: KibanaRequest | LegacyRequest) => AuthHeaders | undefined; @@ -730,11 +715,6 @@ export type GetAuthState = (request: KibanaRequest | LegacyRequest) state: T; }; -// @public -export function getFlattenedObject(rootValue: Record): { - [key: string]: any; -}; - // @public (undocumented) export interface GetResponse { // (undocumented) @@ -966,9 +946,6 @@ export interface IScopedClusterClient { readonly asInternalUser: ElasticsearchClient; } -// @public -export function isRelativeUrl(candidatePath: string): boolean; - // @public export interface IUiSettingsClient { get: (key: string) => Promise; @@ -1543,9 +1520,6 @@ export type MIGRATION_ASSISTANCE_INDEX_ACTION = 'upgrade' | 'reindex'; // @public @deprecated (undocumented) export type MIGRATION_DEPRECATION_LEVEL = 'none' | 'info' | 'warning' | 'critical'; -// @public -export function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulParts) => Partial | void): string; - // @public export type MutatingOperationRefreshSetting = boolean | 'wait_for'; @@ -2848,26 +2822,6 @@ export interface UiSettingsServiceStart { // @public export type UiSettingsType = 'undefined' | 'json' | 'markdown' | 'number' | 'select' | 'boolean' | 'string' | 'array' | 'image'; -// @public -export interface URLMeaningfulParts { - // (undocumented) - auth?: string | null; - // (undocumented) - hash?: string | null; - // (undocumented) - hostname?: string | null; - // (undocumented) - pathname?: string | null; - // (undocumented) - port?: string | null; - // (undocumented) - protocol?: string | null; - // (undocumented) - query: ParsedQuery; - // (undocumented) - slashes?: boolean | null; -} - // @public export interface UserProvidedValues { // (undocumented) diff --git a/src/core/server/server.ts b/src/core/server/server.ts index a02b0f51b559f3..8015b184f61308 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -17,6 +17,7 @@ * under the License. */ +import { mapToObject } from '@kbn/std'; import { ConfigService, Env, RawConfigurationProvider, coreDeprecationProvider } from './config'; import { CoreApp } from './core_app'; import { AuditTrailService } from './audit_trail'; @@ -31,7 +32,7 @@ import { PluginsService, config as pluginsConfig } from './plugins'; import { SavedObjectsService } from '../server/saved_objects'; import { MetricsService, opsConfig } from './metrics'; import { CapabilitiesService } from './capabilities'; -import { EnvironmentService } from './environment'; +import { EnvironmentService, config as pidConfig } from './environment'; import { StatusService } from './status/status_service'; import { config as cspConfig } from './csp'; @@ -44,7 +45,6 @@ import { config as kibanaConfig } from './kibana_config'; import { savedObjectsConfig, savedObjectsMigrationConfig } from './saved_objects'; import { config as uiSettingsConfig } from './ui_settings'; import { config as statusConfig } from './status'; -import { mapToObject } from '../utils'; import { ContextService } from './context'; import { RequestHandlerContext } from '.'; import { InternalCoreSetup, InternalCoreStart, ServiceConfigDescriptor } from './internal_types'; @@ -310,6 +310,7 @@ export class Server { uiSettingsConfig, opsConfig, statusConfig, + pidConfig, ]; this.configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider); diff --git a/src/core/server/status/status_service.mock.ts b/src/core/server/status/status_service.mock.ts index 42b3eecdca310f..930ee2970cf55b 100644 --- a/src/core/server/status/status_service.mock.ts +++ b/src/core/server/status/status_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { StatusService } from './status_service'; import { InternalStatusServiceSetup, diff --git a/src/core/server/status/types.ts b/src/core/server/status/types.ts index f884b80316fa81..9fa33a8c6d40c0 100644 --- a/src/core/server/status/types.ts +++ b/src/core/server/status/types.ts @@ -18,7 +18,7 @@ */ import { Observable } from 'rxjs'; -import { deepFreeze } from '../../utils'; +import { deepFreeze } from '@kbn/std'; import { PluginName } from '../plugins'; /** diff --git a/src/core/server/ui_settings/settings/navigation.ts b/src/core/server/ui_settings/settings/navigation.ts index 6483e86a1395ab..ec825a2779f38a 100644 --- a/src/core/server/ui_settings/settings/navigation.ts +++ b/src/core/server/ui_settings/settings/navigation.ts @@ -19,8 +19,8 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; +import { isRelativeUrl } from '@kbn/std'; import { UiSettingsParams } from '../../../types'; -import { isRelativeUrl } from '../../../utils'; export const getNavigationSettings = (): Record => { return { diff --git a/src/core/server/ui_settings/ui_settings_service.mock.ts b/src/core/server/ui_settings/ui_settings_service.mock.ts index 83cea6d7ab3e22..b1ed0dd188cdec 100644 --- a/src/core/server/ui_settings/ui_settings_service.mock.ts +++ b/src/core/server/ui_settings/ui_settings_service.mock.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { IUiSettingsClient, InternalUiSettingsServiceSetup, diff --git a/src/core/server/ui_settings/ui_settings_service.ts b/src/core/server/ui_settings/ui_settings_service.ts index 8598cf7a622872..25062490f5b6b5 100644 --- a/src/core/server/ui_settings/ui_settings_service.ts +++ b/src/core/server/ui_settings/ui_settings_service.ts @@ -19,10 +19,11 @@ import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; +import { mapToObject } from '@kbn/std'; + import { CoreService } from '../../types'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; - import { SavedObjectsClientContract } from '../saved_objects/types'; import { InternalSavedObjectsServiceSetup } from '../saved_objects'; import { InternalHttpServiceSetup } from '../http'; @@ -33,7 +34,6 @@ import { InternalUiSettingsServiceStart, UiSettingsParams, } from './types'; -import { mapToObject } from '../../utils/'; import { uiSettingsType } from './saved_objects'; import { registerRoutes } from './routes'; import { getCoreSettings } from './settings'; diff --git a/src/core/tsconfig.json b/src/core/tsconfig.json new file mode 100644 index 00000000000000..b8780321e11dd8 --- /dev/null +++ b/src/core/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "public/**/*", + "server/**/*", + "types/**/*", + "test_helpers/**/*", + "utils/**/*", + "index.ts", + "typings.ts" + ], + "references": [ + { "path": "../test_utils/" } + ] +} diff --git a/src/core/typings.ts b/src/core/typings.ts new file mode 100644 index 00000000000000..a84e1c01d2bd2d --- /dev/null +++ b/src/core/typings.ts @@ -0,0 +1,55 @@ +/* + * 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. + */ + +declare module 'query-string' { + type ArrayFormat = 'bracket' | 'index' | 'none'; + + export interface ParseOptions { + arrayFormat?: ArrayFormat; + sort: ((itemLeft: string, itemRight: string) => number) | false; + } + + export interface ParsedQuery { + [key: string]: T | T[] | null | undefined; + } + + export function parse(str: string, options?: ParseOptions): ParsedQuery; + + export function parseUrl(str: string, options?: ParseOptions): { url: string; query: any }; + + export interface StringifyOptions { + strict?: boolean; + encode?: boolean; + arrayFormat?: ArrayFormat; + sort: ((itemLeft: string, itemRight: string) => number) | false; + } + + export function stringify(obj: object, options?: StringifyOptions): string; + + export function extract(str: string): string; +} + +type DeeplyMockedKeys = { + [P in keyof T]: T[P] extends (...args: any[]) => any + ? jest.MockInstance, Parameters> + : DeeplyMockedKeys; +} & + T; + +type MockedKeys = { [P in keyof T]: jest.Mocked }; diff --git a/src/core/utils/context.ts b/src/core/utils/context.ts index 941bbceb0cd92f..f28d3330b8e36e 100644 --- a/src/core/utils/context.ts +++ b/src/core/utils/context.ts @@ -19,8 +19,8 @@ import { flatten } from 'lodash'; import { ShallowPromise } from '@kbn/utility-types'; -import { pick } from '.'; -import { CoreId, PluginOpaqueId } from '../server'; +import { pick } from '@kbn/std'; +import type { CoreId, PluginOpaqueId } from '../server'; /** * Make all properties in T optional, except for the properties whose keys are in the union K diff --git a/src/core/utils/index.ts b/src/core/utils/index.ts index a6df0992f6cc67..c620e4e5ee155e 100644 --- a/src/core/utils/index.ts +++ b/src/core/utils/index.ts @@ -17,15 +17,12 @@ * under the License. */ -export * from './assert_never'; -export * from './context'; -export * from './deep_freeze'; -export * from './get'; -export * from './map_to_object'; -export * from './merge'; -export * from './pick'; -export * from './promise'; -export * from './url'; -export * from './unset'; -export * from './get_flattened_object'; -export * from './default_app_categories'; +export { + ContextContainer, + HandlerContextType, + HandlerFunction, + HandlerParameters, + IContextContainer, + IContextProvider, +} from './context'; +export { DEFAULT_APP_CATEGORIES } from './default_app_categories'; diff --git a/src/dev/ci_setup/setup.sh b/src/dev/ci_setup/setup.sh index 3351170c29e01a..aabc1e75b90255 100755 --- a/src/dev/ci_setup/setup.sh +++ b/src/dev/ci_setup/setup.sh @@ -16,12 +16,6 @@ echo " -- TEST_ES_SNAPSHOT_VERSION='$TEST_ES_SNAPSHOT_VERSION'" echo " -- installing node.js dependencies" yarn kbn bootstrap --prefer-offline -### -### ensure Chromedriver install hook is triggered -### when modules are up-to-date -### -node node_modules/chromedriver/install.js - ### ### Download es snapshots ### diff --git a/src/dev/ci_setup/setup_env.sh b/src/dev/ci_setup/setup_env.sh index 5757d72f995827..72ec73ad810e6d 100644 --- a/src/dev/ci_setup/setup_env.sh +++ b/src/dev/ci_setup/setup_env.sh @@ -134,13 +134,13 @@ export CYPRESS_DOWNLOAD_MIRROR="https://us-central1-elastic-kibana-184716.cloudf export CHECKS_REPORTER_ACTIVE=false # This is mainly for release-manager builds, which run in an environment that doesn't have Chrome installed -# if [[ "$(which google-chrome-stable)" || "$(which google-chrome)" ]]; then -# echo "Chrome detected, setting DETECT_CHROMEDRIVER_VERSION=true" -# export DETECT_CHROMEDRIVER_VERSION=true -# export CHROMEDRIVER_FORCE_DOWNLOAD=true -# else -# echo "Chrome not detected, installing default chromedriver binary for the package version" -# fi +if [[ "$(which google-chrome-stable)" || "$(which google-chrome)" ]]; then + echo "Chrome detected, setting DETECT_CHROMEDRIVER_VERSION=true" + export DETECT_CHROMEDRIVER_VERSION=true + export CHROMEDRIVER_FORCE_DOWNLOAD=true +else + echo "Chrome not detected, installing default chromedriver binary for the package version" +fi ### only run on pr jobs for elastic/kibana, checks-reporter doesn't work for other repos if [[ "$ghprbPullId" && "$ghprbGhRepository" == 'elastic/kibana' ]] ; then diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index e18c82b5b9e96e..6fa07190dc43e2 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -25,6 +25,7 @@ import { Project } from './project'; export const PROJECTS = [ new Project(resolve(REPO_ROOT, 'tsconfig.json')), new Project(resolve(REPO_ROOT, 'src/test_utils/tsconfig.json')), + new Project(resolve(REPO_ROOT, 'src/core/tsconfig.json')), 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' }), diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts index e1fca23274a5aa..00968b7259a303 100644 --- a/src/dev/typescript/run_type_check_cli.ts +++ b/src/dev/typescript/run_type_check_cli.ts @@ -98,7 +98,7 @@ export async function runTypeCheckCli() { } execInProjects(log, projects, process.execPath, (project) => [ - '--max-old-space-size=4096', + '--max-old-space-size=5120', require.resolve('typescript/bin/tsc'), ...['--project', project.tsConfigPath], ...tscArgs, diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index ce7a500a00dc8f..f8736fb30f90e9 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -42,10 +42,7 @@ export default () => basePathProxyTarget: Joi.number().default(5603), }).default(), - pid: Joi.object({ - file: Joi.string(), - exclusive: Joi.boolean().default(false), - }).default(), + pid: HANDLED_IN_NEW_PLATFORM, csp: HANDLED_IN_NEW_PLATFORM, diff --git a/src/legacy/server/i18n/index.ts b/src/legacy/server/i18n/index.ts index e895f83fe69017..cb86c3220bec12 100644 --- a/src/legacy/server/i18n/index.ts +++ b/src/legacy/server/i18n/index.ts @@ -21,6 +21,7 @@ import { i18n, i18nLoader } from '@kbn/i18n'; import { basename } from 'path'; import { Server } from 'hapi'; import { fromRoot } from '../../../core/server/utils'; +import type { UsageCollectionSetup } from '../../../plugins/usage_collection/server'; import { getTranslationPaths } from './get_translations_path'; import { I18N_RC } from './constants'; import KbnServer, { KibanaConfig } from '../kbn_server'; @@ -64,7 +65,10 @@ export async function i18nMixin(kbnServer: KbnServer, server: Server, config: Ki server.decorate('server', 'getTranslationsFilePaths', getTranslationsFilePaths); if (kbnServer.newPlatform.setup.plugins.usageCollection) { - registerLocalizationUsageCollector(kbnServer.newPlatform.setup.plugins.usageCollection, { + const { usageCollection } = kbnServer.newPlatform.setup.plugins as { + usageCollection: UsageCollectionSetup; + }; + registerLocalizationUsageCollector(usageCollection, { getLocale: () => config.get('i18n.locale') as string, getTranslationsFilePaths, }); diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index 663542618375a3..8827dc53c52750 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -19,7 +19,6 @@ import { Server } from 'hapi'; -import { TelemetryCollectionManagerPluginSetup } from 'src/plugins/telemetry_collection_manager/server'; import { CoreSetup, CoreStart, @@ -35,8 +34,6 @@ import { LegacyConfig, ILegacyInternals } from '../../core/server/legacy'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { UiPlugins } from '../../core/server/plugins'; import { ElasticsearchPlugin } from '../core_plugins/elasticsearch'; -import { UsageCollectionSetup } from '../../plugins/usage_collection/server'; -import { HomeServerPluginSetup } from '../../plugins/home/server'; // lot of legacy code was assuming this type only had these two methods export type KibanaConfig = Pick; @@ -60,9 +57,6 @@ declare module 'hapi' { type KbnMixinFunc = (kbnServer: KbnServer, server: Server, config: any) => Promise | void; export interface PluginsSetup { - usageCollection: UsageCollectionSetup; - telemetryCollectionManager: TelemetryCollectionManagerPluginSetup; - home: HomeServerPluginSetup; [key: string]: object; } diff --git a/src/legacy/server/kbn_server.js b/src/legacy/server/kbn_server.js index a5eefd140c8fa2..24d00abb99a058 100644 --- a/src/legacy/server/kbn_server.js +++ b/src/legacy/server/kbn_server.js @@ -29,7 +29,6 @@ import { coreMixin } from './core'; import { loggingMixin } from './logging'; import warningsMixin from './warnings'; import { statusMixin } from './status'; -import pidMixin from './pid'; import configCompleteMixin from './config/complete'; import { optimizeMixin } from '../../optimize'; import * as Plugins from './plugins'; @@ -93,9 +92,6 @@ export default class KbnServer { warningsMixin, statusMixin, - // writes pid file - pidMixin, - // scan translations dirs, register locale files and initialize i18n engine. i18nMixin, diff --git a/src/legacy/server/logging/log_interceptor.js b/src/legacy/server/logging/log_interceptor.js index 07545570445831..2298d83aa28571 100644 --- a/src/legacy/server/logging/log_interceptor.js +++ b/src/legacy/server/logging/log_interceptor.js @@ -20,7 +20,11 @@ import Stream from 'stream'; import { get, isEqual } from 'lodash'; -const GET_CLIENT_HELLO = /GET_CLIENT_HELLO:http/; +/** + * Matches error messages when clients connect via HTTP instead of HTTPS; see unit test for full message. Warning: this can change when Node + * and its bundled OpenSSL binary are upgraded. + */ +const OPENSSL_GET_RECORD_REGEX = /ssl3_get_record:http/; function doTagsMatch(event, tags) { return isEqual(get(event, 'tags'), tags); @@ -124,7 +128,7 @@ export class LogInterceptor extends Stream.Transform { } downgradeIfHTTPWhenHTTPS(event) { - return downgradeIfErrorMessage(GET_CLIENT_HELLO, event); + return downgradeIfErrorMessage(OPENSSL_GET_RECORD_REGEX, event); } _transform(event, enc, next) { diff --git a/src/legacy/server/logging/log_interceptor.test.js b/src/legacy/server/logging/log_interceptor.test.js index c99373ebe0a639..492d1ffc8d167f 100644 --- a/src/legacy/server/logging/log_interceptor.test.js +++ b/src/legacy/server/logging/log_interceptor.test.js @@ -147,7 +147,7 @@ describe('server logging LogInterceptor', () => { describe('#downgradeIfHTTPWhenHTTPS', () => { it('transforms http requests when serving https errors', () => { const message = - '40735139278848:error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request:../deps/openssl/openssl/ssl/s23_srvr.c:394'; + '4584650176:error:1408F09C:SSL routines:ssl3_get_record:http request:../deps/openssl/openssl/ssl/record/ssl3_record.c:322:\n'; const interceptor = new LogInterceptor(); const event = stubClientErrorEvent({ message }); assertDowngraded(interceptor.downgradeIfHTTPWhenHTTPS(event)); diff --git a/src/legacy/server/pid/index.js b/src/legacy/server/pid/index.js deleted file mode 100644 index d7b9da12922520..00000000000000 --- a/src/legacy/server/pid/index.js +++ /dev/null @@ -1,73 +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 _ from 'lodash'; -import Boom from 'boom'; -import Bluebird from 'bluebird'; -import { unlinkSync as unlink } from 'fs'; -const writeFile = Bluebird.promisify(require('fs').writeFile); - -export default Bluebird.method(function (kbnServer, server, config) { - const path = config.get('pid.file'); - if (!path) return; - - const pid = String(process.pid); - - return writeFile(path, pid, { flag: 'wx' }) - .catch(function (err) { - if (err.code !== 'EEXIST') throw err; - - const message = `pid file already exists at ${path}`; - const metadata = { - path: path, - pid: pid, - }; - - if (config.get('pid.exclusive')) { - throw Boom.internal(message, { message, ...metadata }); - } else { - server.log(['pid', 'warning'], message, metadata); - } - - return writeFile(path, pid); - }) - .then(function () { - server.logWithMetadata(['pid', 'debug'], `wrote pid file to ${path}`, { - path: path, - pid: pid, - }); - - const clean = _.once(function () { - unlink(path); - }); - - process.once('exit', clean); // for "natural" exits - process.once('SIGINT', function () { - // for Ctrl-C exits - clean(); - - // resend SIGINT - process.kill(process.pid, 'SIGINT'); - }); - - process.on('unhandledRejection', function (reason) { - server.log(['warning'], `Detected an unhandled Promise rejection.\n${reason}`); - }); - }); -}); diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx index bbc3f3632bf64f..8c9e3847844d93 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx @@ -121,7 +121,7 @@ export class AdvancedSettingsComponent extends Component< setTimeout(() => { const id = hash.replace('#', ''); const element = document.getElementById(id); - const globalNavOffset = document.getElementById('headerGlobalNav')?.offsetHeight || 0; + const globalNavOffset = document.getElementById('globalHeaderBars')?.offsetHeight || 0; if (element) { element.scrollIntoView(); diff --git a/src/plugins/advanced_settings/public/management_app/components/_index.scss b/src/plugins/advanced_settings/public/management_app/components/_index.scss deleted file mode 100644 index d2d2e38947f762..00000000000000 --- a/src/plugins/advanced_settings/public/management_app/components/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './form/index'; diff --git a/src/plugins/advanced_settings/public/management_app/components/form/_form.scss b/src/plugins/advanced_settings/public/management_app/components/form/_form.scss deleted file mode 100644 index 8d768d200fdd2a..00000000000000 --- a/src/plugins/advanced_settings/public/management_app/components/form/_form.scss +++ /dev/null @@ -1,15 +0,0 @@ -@import '@elastic/eui/src/global_styling/variables/header'; -@import '@elastic/eui/src/components/nav_drawer/variables'; - -// TODO #64541 -// Delete this whole file -.mgtAdvancedSettingsForm__bottomBar { - margin-left: $euiNavDrawerWidthCollapsed; - z-index: 9; // Puts it inuder the nav drawer when expanded - &--pushForNav { - margin-left: $euiNavDrawerWidthExpanded; - } - @include euiBreakpoint('xs', 's') { - margin-left: 0; - } -} diff --git a/src/plugins/advanced_settings/public/management_app/components/form/_index.scss b/src/plugins/advanced_settings/public/management_app/components/form/_index.scss deleted file mode 100644 index 2ef4ef1d20ce99..00000000000000 --- a/src/plugins/advanced_settings/public/management_app/components/form/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './form'; diff --git a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx index 5533f684870d90..0378d816fd2c3b 100644 --- a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx @@ -18,7 +18,6 @@ */ import React, { PureComponent, Fragment } from 'react'; -import classNames from 'classnames'; import { EuiFlexGroup, @@ -45,7 +44,6 @@ import { Field, getEditableValue } from '../field'; import { FieldSetting, SettingsChanges, FieldState } from '../../types'; type Category = string; -const NAV_IS_LOCKED_KEY = 'core.chrome.isLocked'; interface FormProps { settings: Record; @@ -326,23 +324,8 @@ export class Form extends PureComponent { renderBottomBar = () => { const areChangesInvalid = this.areChangesInvalid(); - - // TODO #64541 - // Delete these classes - let bottomBarClasses = ''; - const pageNav = this.props.settings.general.find( - (setting) => setting.name === 'pageNavigation' - ); - - if (pageNav?.value === 'legacy') { - bottomBarClasses = classNames('mgtAdvancedSettingsForm__bottomBar', { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'mgtAdvancedSettingsForm__bottomBar--pushForNav': - localStorage.getItem(NAV_IS_LOCKED_KEY) === 'true', - }); - } return ( - + ScopedHistory; + setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; savedObjects: SavedObjectsStart; restorePreviousUrl: () => void; } diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index 92d6f2ed91dde9..dd5eb1ee5ccaa4 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -153,6 +153,7 @@ export class DashboardAppController { i18n: i18nStart, }, history, + setHeaderActionMenu, kbnUrlStateStorage, usageCollection, navigation, @@ -709,7 +710,13 @@ export class DashboardAppController { }; const dashboardNavBar = document.getElementById('dashboardChrome'); const updateNavBar = () => { - ReactDOM.render(, dashboardNavBar); + ReactDOM.render( + , + dashboardNavBar + ); }; const unmountNavBar = () => { diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx b/src/plugins/dashboard/public/attribute_service/attribute_service.tsx index a36363d22d87da..84df05154fb630 100644 --- a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx +++ b/src/plugins/dashboard/public/attribute_service/attribute_service.tsx @@ -57,7 +57,7 @@ export interface AttributeServiceOptions { type: string, attributes: A, savedObjectId?: string - ) => Promise<{ id: string }>; + ) => Promise<{ id?: string } | { error: Error }>; customUnwrapMethod?: (savedObject: SimpleSavedObject) => A; } @@ -124,7 +124,10 @@ export class AttributeService< newAttributes, savedObjectId ); - return { ...originalInput, savedObjectId: savedItem.id } as RefType; + if ('id' in savedItem) { + return { ...originalInput, savedObjectId: savedItem.id } as RefType; + } + return { ...originalInput } as RefType; } if (savedObjectId) { @@ -208,7 +211,6 @@ export class AttributeService< return { error }; } }; - if (saveOptions && (saveOptions as { showSaveModal: boolean }).showSaveModal) { this.showSaveModal( ; + +const createStartContract = (): DashboardStart => { + // @ts-ignore + const startContract: DashboardStart = { + getAttributeService: jest.fn(), + }; + + return startContract; +}; + +export const dashboardPluginMock = { + createStartContract, +}; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 49584f62215ead..5a45229a58a7d4 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -310,7 +310,7 @@ export class DashboardPlugin id: DashboardConstants.DASHBOARDS_ID, title: 'Dashboard', order: 2500, - euiIconType: 'dashboardApp', + euiIconType: 'logoKibana', defaultPath: `#${DashboardConstants.LANDING_PAGE_PATH}`, updater$: this.appStateUpdater, category: DEFAULT_APP_CATEGORIES.kibana, @@ -352,6 +352,7 @@ export class DashboardPlugin localStorage: new Storage(localStorage), usageCollection, scopedHistory: () => this.currentHistory!, + setHeaderActionMenu: params.setHeaderActionMenu, savedObjects, restorePreviousUrl, }; diff --git a/src/plugins/data/common/es_query/kuery/node_types/types.ts b/src/plugins/data/common/es_query/kuery/node_types/types.ts index 6d3019e75d483f..894bd1812f0844 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/types.ts +++ b/src/plugins/data/common/es_query/kuery/node_types/types.ts @@ -76,6 +76,7 @@ export interface NamedArgTypeBuildNode { } interface WildcardType { + wildcardSymbol: string; buildNode: (value: string) => WildcardTypeBuildNode | KueryNode; test: (node: any, string: string) => boolean; toElasticsearchQuery: (node: any) => string; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index fa5d3cd85f4300..a8fbbcb08d3588 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -28,7 +28,6 @@ import { ExpressionAstFunction } from 'src/plugins/expressions/common'; import { ExpressionsSetup } from 'src/plugins/expressions/public'; import { History } from 'history'; import { Href } from 'history'; -import { HttpStart } from 'src/core/public'; import { IconType } from '@elastic/eui'; import { InjectedIntl } from '@kbn/i18n/react'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; @@ -49,6 +48,7 @@ import { Path } from 'history'; import { Plugin as Plugin_2 } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; import { PopoverAnchorPosition } from '@elastic/eui'; +import { PublicMethodsOf } from '@kbn/utility-types'; import { PublicUiSettingsParams } from 'src/core/server/types'; import React from 'react'; import * as React_2 from 'react'; @@ -1685,7 +1685,7 @@ export interface QueryStateChange extends QueryStateChangePartial { // Warning: (ae-missing-release-tag) "QueryStringInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const QueryStringInput: React.FC>; +export const QueryStringInput: React.FC>; // @public (undocumented) export type QuerySuggestion = QuerySuggestionBasic | QuerySuggestionField; @@ -2017,7 +2017,7 @@ export class SearchSource { onRequestStart(handler: (searchSource: SearchSource, options?: ISearchOptions) => Promise): void; serialize(): { searchSourceJSON: string; - references: import("../../../../../core/public").SavedObjectReference[]; + references: import("../../../../../core/types").SavedObjectReference[]; }; setField(field: K, value: SearchSourceFields[K]): this; setFields(newFields: SearchSourceFields): this; diff --git a/src/plugins/data/public/search/fetch/types.ts b/src/plugins/data/public/search/fetch/types.ts index 224a597766909b..cdf10d8f1a1b06 100644 --- a/src/plugins/data/public/search/fetch/types.ts +++ b/src/plugins/data/public/search/fetch/types.ts @@ -17,9 +17,9 @@ * under the License. */ -import { HttpStart } from 'src/core/public'; -import { BehaviorSubject } from 'rxjs'; +import { SearchResponse } from 'elasticsearch'; import { GetConfigFn } from '../../../common'; +import { LegacyFetchHandlers } from '../legacy/types'; /** * @internal @@ -31,9 +31,17 @@ import { GetConfigFn } from '../../../common'; export type SearchRequest = Record; export interface FetchHandlers { - config: { get: GetConfigFn }; - http: HttpStart; - loadingCount$: BehaviorSubject; + getConfig: GetConfigFn; + /** + * Callback which can be used to hook into responses, modify them, or perform + * side effects like displaying UI errors on the client. + */ + onResponse: (request: SearchRequest, response: SearchResponse) => SearchResponse; + /** + * These handlers are only used by the legacy defaultSearchStrategy and can be removed + * once that strategy has been deprecated. + */ + legacy: LegacyFetchHandlers; } export interface SearchError { diff --git a/src/plugins/data/public/search/legacy/call_client.test.ts b/src/plugins/data/public/search/legacy/call_client.test.ts index 943a02d22088d0..0a7913b0a734f8 100644 --- a/src/plugins/data/public/search/legacy/call_client.test.ts +++ b/src/plugins/data/public/search/legacy/call_client.test.ts @@ -17,18 +17,13 @@ * under the License. */ -import { coreMock } from '../../../../../core/public/mocks'; import { callClient } from './call_client'; import { SearchStrategySearchParams } from './types'; import { defaultSearchStrategy } from './default_search_strategy'; import { FetchHandlers } from '../fetch'; -import { handleResponse } from '../fetch/handle_response'; import { BehaviorSubject } from 'rxjs'; const mockAbortFn = jest.fn(); -jest.mock('../fetch/handle_response', () => ({ - handleResponse: jest.fn((request, response) => response), -})); jest.mock('./default_search_strategy', () => { return { @@ -50,32 +45,36 @@ jest.mock('./default_search_strategy', () => { }); describe('callClient', () => { + const handleResponse = jest.fn().mockImplementation((req, res) => res); + const handlers = { + getConfig: jest.fn(), + onResponse: handleResponse, + legacy: { + callMsearch: jest.fn(), + loadingCount$: new BehaviorSubject(0), + }, + } as FetchHandlers; + beforeEach(() => { - (handleResponse as jest.Mock).mockClear(); + handleResponse.mockClear(); }); test('Passes the additional arguments it is given to the search strategy', () => { const searchRequests = [{ _searchStrategyId: 0 }]; - const args = { - http: coreMock.createStart().http, - legacySearchService: {}, - config: { get: jest.fn() }, - loadingCount$: new BehaviorSubject(0), - } as FetchHandlers; - callClient(searchRequests, [], args); + callClient(searchRequests, [], handlers); expect(defaultSearchStrategy.search).toBeCalled(); expect((defaultSearchStrategy.search as any).mock.calls[0][0]).toEqual({ searchRequests, - ...args, + ...handlers, }); }); test('Returns the responses in the original order', async () => { const searchRequests = [{ _searchStrategyId: 1 }, { _searchStrategyId: 0 }]; - const responses = await Promise.all(callClient(searchRequests, [], {} as FetchHandlers)); + const responses = await Promise.all(callClient(searchRequests, [], handlers)); expect(responses[0]).toEqual({ id: searchRequests[0]._searchStrategyId }); expect(responses[1]).toEqual({ id: searchRequests[1]._searchStrategyId }); @@ -84,7 +83,7 @@ describe('callClient', () => { test('Calls handleResponse with each request and response', async () => { const searchRequests = [{ _searchStrategyId: 0 }, { _searchStrategyId: 1 }]; - const responses = callClient(searchRequests, [], {} as FetchHandlers); + const responses = callClient(searchRequests, [], handlers); await Promise.all(responses); expect(handleResponse).toBeCalledTimes(2); @@ -105,7 +104,7 @@ describe('callClient', () => { }, ]; - callClient(searchRequests, requestOptions, {} as FetchHandlers); + callClient(searchRequests, requestOptions, handlers); abortController.abort(); expect(mockAbortFn).toBeCalled(); diff --git a/src/plugins/data/public/search/legacy/call_client.ts b/src/plugins/data/public/search/legacy/call_client.ts index d66796b9427a14..b87affdd59c54a 100644 --- a/src/plugins/data/public/search/legacy/call_client.ts +++ b/src/plugins/data/public/search/legacy/call_client.ts @@ -19,7 +19,7 @@ import { SearchResponse } from 'elasticsearch'; import { ISearchOptions } from 'src/plugins/data/common'; -import { FetchHandlers, handleResponse } from '../fetch'; +import { FetchHandlers } from '../fetch'; import { defaultSearchStrategy } from './default_search_strategy'; import { SearchRequest } from '../index'; @@ -42,7 +42,7 @@ export function callClient( }); searchRequests.forEach((request, i) => { - const response = searching.then((results) => handleResponse(request, results[i])); + const response = searching.then((results) => fetchHandlers.onResponse(request, results[i])); const { abortSignal = null } = requestOptionsMap.get(request) || {}; if (abortSignal) abortSignal.addEventListener('abort', abort); requestResponseMap.set(request, response); diff --git a/src/plugins/data/public/search/legacy/call_msearch.ts b/src/plugins/data/public/search/legacy/call_msearch.ts new file mode 100644 index 00000000000000..fd4f8a07919f83 --- /dev/null +++ b/src/plugins/data/public/search/legacy/call_msearch.ts @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { HttpStart } from 'src/core/public'; +import { LegacyFetchHandlers } from './types'; + +/** + * Wrapper for calling the internal msearch endpoint from the client. + * This is needed to abstract away differences in the http service + * between client & server. + * + * @internal + */ +export function getCallMsearch({ http }: { http: HttpStart }): LegacyFetchHandlers['callMsearch'] { + return async ({ body, signal }) => { + return http.post('/internal/_msearch', { + body: JSON.stringify(body), + signal, + }); + }; +} diff --git a/src/plugins/data/public/search/legacy/default_search_strategy.test.ts b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts index e74ab49131430b..ad59e5c6c96257 100644 --- a/src/plugins/data/public/search/legacy/default_search_strategy.test.ts +++ b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts @@ -19,8 +19,9 @@ import { HttpStart } from 'src/core/public'; import { coreMock } from '../../../../../core/public/mocks'; +import { getCallMsearch } from './call_msearch'; import { defaultSearchStrategy } from './default_search_strategy'; -import { SearchStrategySearchParams } from './types'; +import { LegacyFetchHandlers, SearchStrategySearchParams } from './types'; import { BehaviorSubject } from 'rxjs'; const { search } = defaultSearchStrategy; @@ -44,11 +45,12 @@ describe('defaultSearchStrategy', function () { index: { title: 'foo' }, }, ], - http, - config: { - get: jest.fn(), - }, - loadingCount$: new BehaviorSubject(0) as any, + getConfig: jest.fn(), + onResponse: (req, res) => res, + legacy: { + callMsearch: getCallMsearch({ http }), + loadingCount$: new BehaviorSubject(0) as any, + } as jest.Mocked, }; }); diff --git a/src/plugins/data/public/search/legacy/default_search_strategy.ts b/src/plugins/data/public/search/legacy/default_search_strategy.ts index cbcd0da20207f3..bed86cb75cca64 100644 --- a/src/plugins/data/public/search/legacy/default_search_strategy.ts +++ b/src/plugins/data/public/search/legacy/default_search_strategy.ts @@ -29,12 +29,14 @@ export const defaultSearchStrategy: SearchStrategyProvider = { }, }; -function msearch({ searchRequests, config, http, loadingCount$ }: SearchStrategySearchParams) { +function msearch({ searchRequests, getConfig, legacy }: SearchStrategySearchParams) { + const { callMsearch, loadingCount$ } = legacy; + const requests = searchRequests.map(({ index, body }) => { return { header: { index: index.title || index, - preference: getPreference(config.get), + preference: getPreference(getConfig), }, body, }; @@ -55,12 +57,11 @@ function msearch({ searchRequests, config, http, loadingCount$ }: SearchStrategy } }; - const searching = http - .post('/internal/_msearch', { - body: JSON.stringify({ searches: requests }), - signal: abortController.signal, - }) - .then(({ body }) => body?.responses) + const searching = callMsearch({ + body: { searches: requests }, + signal: abortController.signal, + }) + .then((res: any) => res?.body?.responses) .finally(() => cleanup()); return { diff --git a/src/plugins/data/public/search/legacy/fetch_soon.test.ts b/src/plugins/data/public/search/legacy/fetch_soon.test.ts index d38a41cf5ffbc0..7243ab158009ad 100644 --- a/src/plugins/data/public/search/legacy/fetch_soon.test.ts +++ b/src/plugins/data/public/search/legacy/fetch_soon.test.ts @@ -67,25 +67,21 @@ describe('fetchSoon', () => { }); test('should execute asap if config is set to not batch searches', () => { - const config = { - get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: false }), - }; + const getConfig = getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: false }); const request = {}; const options = {}; - fetchSoon(request, options, { config } as FetchHandlers); + fetchSoon(request, options, { getConfig } as FetchHandlers); expect(callClient).toBeCalled(); }); test('should delay by 50ms if config is set to batch searches', () => { - const config = { - get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }), - }; + const getConfig = getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }); const request = {}; const options = {}; - fetchSoon(request, options, { config } as FetchHandlers); + fetchSoon(request, options, { getConfig } as FetchHandlers); expect(callClient).not.toBeCalled(); jest.advanceTimersByTime(0); @@ -95,14 +91,12 @@ describe('fetchSoon', () => { }); test('should send a batch of requests to callClient', () => { - const config = { - get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }), - }; + const getConfig = getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }); const requests = [{ foo: 1 }, { foo: 2 }]; const options = [{ bar: 1 }, { bar: 2 }]; requests.forEach((request, i) => { - fetchSoon(request, options[i] as ISearchOptions, { config } as FetchHandlers); + fetchSoon(request, options[i] as ISearchOptions, { getConfig } as FetchHandlers); }); jest.advanceTimersByTime(50); @@ -112,13 +106,11 @@ describe('fetchSoon', () => { }); test('should return the response to the corresponding call for multiple batched requests', async () => { - const config = { - get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }), - }; + const getConfig = getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }); const requests = [{ _mockResponseId: 'foo' }, { _mockResponseId: 'bar' }]; const promises = requests.map((request) => { - return fetchSoon(request, {}, { config } as FetchHandlers); + return fetchSoon(request, {}, { getConfig } as FetchHandlers); }); jest.advanceTimersByTime(50); const results = await Promise.all(promises); @@ -127,18 +119,16 @@ describe('fetchSoon', () => { }); test('should wait for the previous batch to start before starting a new batch', () => { - const config = { - get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }), - }; + const getConfig = getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }); const firstBatch = [{ foo: 1 }, { foo: 2 }]; const secondBatch = [{ bar: 1 }, { bar: 2 }]; firstBatch.forEach((request) => { - fetchSoon(request, {}, { config } as FetchHandlers); + fetchSoon(request, {}, { getConfig } as FetchHandlers); }); jest.advanceTimersByTime(50); secondBatch.forEach((request) => { - fetchSoon(request, {}, { config } as FetchHandlers); + fetchSoon(request, {}, { getConfig } as FetchHandlers); }); expect(callClient).toBeCalledTimes(1); diff --git a/src/plugins/data/public/search/legacy/fetch_soon.ts b/src/plugins/data/public/search/legacy/fetch_soon.ts index 37c3827bb7bba5..1c0573aa895d78 100644 --- a/src/plugins/data/public/search/legacy/fetch_soon.ts +++ b/src/plugins/data/public/search/legacy/fetch_soon.ts @@ -32,7 +32,7 @@ export async function fetchSoon( options: ISearchOptions, fetchHandlers: FetchHandlers ) { - const msToDelay = fetchHandlers.config.get(UI_SETTINGS.COURIER_BATCH_SEARCHES) ? 50 : 0; + const msToDelay = fetchHandlers.getConfig(UI_SETTINGS.COURIER_BATCH_SEARCHES) ? 50 : 0; return delayedFetch(request, options, fetchHandlers, msToDelay); } diff --git a/src/plugins/data/public/search/legacy/types.ts b/src/plugins/data/public/search/legacy/types.ts index ed17db464feffa..740bc22a7485c5 100644 --- a/src/plugins/data/public/search/legacy/types.ts +++ b/src/plugins/data/public/search/legacy/types.ts @@ -17,10 +17,20 @@ * under the License. */ +import { BehaviorSubject } from 'rxjs'; import { SearchResponse } from 'elasticsearch'; import { FetchHandlers } from '../fetch'; import { SearchRequest } from '..'; +// @internal +export interface LegacyFetchHandlers { + callMsearch: (params: { + body: SearchRequest; + signal: AbortSignal; + }) => Promise>>; + loadingCount$: BehaviorSubject; +} + export interface SearchStrategySearchParams extends FetchHandlers { searchRequests: SearchRequest[]; } diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 6b73761c5a4373..c41e1f78ee74e0 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -21,6 +21,8 @@ import { Plugin, CoreSetup, CoreStart } from 'src/core/public'; import { BehaviorSubject } from 'rxjs'; import { ISearchSetup, ISearchStart, SearchEnhancements } from './types'; +import { handleResponse } from './fetch'; +import { getCallMsearch } from './legacy/call_msearch'; import { createSearchSource, SearchSource, SearchSourceDependencies } from './search_source'; import { AggsService, AggsStartDependencies } from './aggs'; import { IndexPatternsContract } from '../index_patterns/index_patterns'; @@ -49,7 +51,7 @@ export class SearchService implements Plugin { private usageCollector?: SearchUsageCollector; public setup( - { http, getStartServices, injectedMetadata, notifications, uiSettings }: CoreSetup, + { http, getStartServices, notifications, uiSettings }: CoreSetup, { expressions, usageCollection }: SearchServiceSetupDependencies ): ISearchSetup { this.usageCollector = createUsageCollector(getStartServices, usageCollection); @@ -82,7 +84,7 @@ export class SearchService implements Plugin { } public start( - { application, http, injectedMetadata, notifications, uiSettings }: CoreStart, + { application, http, notifications, uiSettings }: CoreStart, { fieldFormats, indexPatterns }: SearchServiceStartDependencies ): ISearchStart { const search = ((request, options) => { @@ -95,8 +97,11 @@ export class SearchService implements Plugin { const searchSourceDependencies: SearchSourceDependencies = { getConfig: uiSettings.get.bind(uiSettings), search, - http, - loadingCount$, + onResponse: handleResponse, + legacy: { + callMsearch: getCallMsearch({ http }), + loadingCount$, + }, }; return { diff --git a/src/plugins/data/public/search/search_source/create_search_source.test.ts b/src/plugins/data/public/search/search_source/create_search_source.test.ts index bc1c7c06c88064..6b6cfb0c9b1ca2 100644 --- a/src/plugins/data/public/search/search_source/create_search_source.test.ts +++ b/src/plugins/data/public/search/search_source/create_search_source.test.ts @@ -22,7 +22,6 @@ import { SearchSourceDependencies } from './search_source'; import { IIndexPattern } from '../../../common/index_patterns'; import { IndexPatternsContract } from '../../index_patterns/index_patterns'; import { Filter } from '../../../common/es_query/filters'; -import { coreMock } from '../../../../../core/public/mocks'; import { BehaviorSubject } from 'rxjs'; describe('createSearchSource', () => { @@ -35,8 +34,11 @@ describe('createSearchSource', () => { dependencies = { getConfig: jest.fn(), search: jest.fn(), - http: coreMock.createStart().http, - loadingCount$: new BehaviorSubject(0), + onResponse: (req, res) => res, + legacy: { + callMsearch: jest.fn(), + loadingCount$: new BehaviorSubject(0), + }, }; indexPatternContractMock = ({ diff --git a/src/plugins/data/public/search/search_source/mocks.ts b/src/plugins/data/public/search/search_source/mocks.ts index adf53bee33fe1b..f582861e37c159 100644 --- a/src/plugins/data/public/search/search_source/mocks.ts +++ b/src/plugins/data/public/search/search_source/mocks.ts @@ -18,7 +18,7 @@ */ import { BehaviorSubject } from 'rxjs'; -import { httpServiceMock, uiSettingsServiceMock } from '../../../../../core/public/mocks'; +import { uiSettingsServiceMock } from '../../../../../core/public/mocks'; import { ISearchSource, SearchSource } from './search_source'; import { SearchSourceFields } from './types'; @@ -54,6 +54,9 @@ export const createSearchSourceMock = (fields?: SearchSourceFields) => new SearchSource(fields, { getConfig: uiSettingsServiceMock.createStartContract().get, search: jest.fn(), - http: httpServiceMock.createStartContract(), - loadingCount$: new BehaviorSubject(0), + onResponse: jest.fn().mockImplementation((req, res) => res), + legacy: { + callMsearch: jest.fn(), + loadingCount$: new BehaviorSubject(0), + }, }); diff --git a/src/plugins/data/public/search/search_source/search_source.test.ts b/src/plugins/data/public/search/search_source/search_source.test.ts index 282a33e6d01f71..d9a9fb2f4fef37 100644 --- a/src/plugins/data/public/search/search_source/search_source.test.ts +++ b/src/plugins/data/public/search/search_source/search_source.test.ts @@ -22,7 +22,6 @@ import { GetConfigFn } from 'src/plugins/data/common'; import { SearchSource, SearchSourceDependencies } from './search_source'; import { IndexPattern, SortDirection } from '../..'; import { fetchSoon } from '../legacy'; -import { coreMock } from '../../../../../core/public/mocks'; jest.mock('../legacy', () => ({ fetchSoon: jest.fn().mockResolvedValue({}), @@ -68,8 +67,11 @@ describe('SearchSource', () => { searchSourceDependencies = { getConfig: jest.fn(), search: mockSearchMethod, - http: coreMock.createStart().http, - loadingCount$: new BehaviorSubject(0), + onResponse: (req, res) => res, + legacy: { + callMsearch: jest.fn(), + loadingCount$: new BehaviorSubject(0), + }, }; }); diff --git a/src/plugins/data/public/search/search_source/search_source.ts b/src/plugins/data/public/search/search_source/search_source.ts index a39898e6a9f52b..4afee223454e49 100644 --- a/src/plugins/data/public/search/search_source/search_source.ts +++ b/src/plugins/data/public/search/search_source/search_source.ts @@ -72,19 +72,12 @@ import { setWith } from '@elastic/safer-lodash-set'; import { uniqueId, uniq, extend, pick, difference, omit, isObject, keys, isFunction } from 'lodash'; import { map } from 'rxjs/operators'; -import { HttpStart } from 'src/core/public'; -import { BehaviorSubject } from 'rxjs'; import { normalizeSortRequest } from './normalize_sort_request'; import { filterDocvalueFields } from './filter_docvalue_fields'; import { fieldWildcardFilter } from '../../../../kibana_utils/common'; import { IIndexPattern, ISearchGeneric } from '../..'; import { SearchSourceOptions, SearchSourceFields } from './types'; -import { - RequestFailure, - handleResponse, - getSearchParamsFromRequest, - SearchRequest, -} from '../fetch'; +import { FetchHandlers, RequestFailure, getSearchParamsFromRequest, SearchRequest } from '../fetch'; import { getEsQueryConfig, @@ -94,7 +87,6 @@ import { ISearchOptions, } from '../../../common'; import { getHighlightRequest } from '../../../common/field_formats'; -import { GetConfigFn } from '../../../common/types'; import { fetchSoon } from '../legacy'; import { extractReferences } from './extract_references'; @@ -114,11 +106,8 @@ export const searchSourceRequiredUiSettings = [ UI_SETTINGS.SORT_OPTIONS, ]; -export interface SearchSourceDependencies { - getConfig: GetConfigFn; +export interface SearchSourceDependencies extends FetchHandlers { search: ISearchGeneric; - http: HttpStart; - loadingCount$: BehaviorSubject; } /** @public **/ @@ -321,14 +310,14 @@ export class SearchSource { * @return {Observable>} */ private fetch$(searchRequest: SearchRequest, options: ISearchOptions) { - const { search, getConfig } = this.dependencies; + const { search, getConfig, onResponse } = this.dependencies; const params = getSearchParamsFromRequest(searchRequest, { getConfig, }); return search({ params, indexType: searchRequest.indexType }, options).pipe( - map(({ rawResponse }) => handleResponse(searchRequest, rawResponse)) + map(({ rawResponse }) => onResponse(searchRequest, rawResponse)) ); } @@ -337,7 +326,7 @@ export class SearchSource { * @return {Promise>} */ private async legacyFetch(searchRequest: SearchRequest, options: ISearchOptions) { - const { http, getConfig, loadingCount$ } = this.dependencies; + const { getConfig, legacy, onResponse } = this.dependencies; return await fetchSoon( searchRequest, @@ -346,9 +335,9 @@ export class SearchSource { ...options, }, { - http, - config: { get: getConfig }, - loadingCount$, + getConfig, + onResponse, + legacy, } ); } diff --git a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_description.tsx b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_description.tsx index 3606bfbaeb1f96..1f124291669ec2 100644 --- a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_description.tsx +++ b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_description.tsx @@ -17,9 +17,9 @@ * under the License. */ import React from 'react'; +import { getFlattenedObject } from '@kbn/std'; import { EuiCodeBlock, EuiDescriptionList, EuiSpacer } from '@elastic/eui'; import { ShardFailure } from './shard_failure_types'; -import { getFlattenedObject } from '../../../../../core/public'; import { ShardFailureDescriptionHeader } from './shard_failure_description_header'; /** diff --git a/src/plugins/dev_tools/public/index.scss b/src/plugins/dev_tools/public/index.scss index c9d8dc7470656a..4bec602ea42db3 100644 --- a/src/plugins/dev_tools/public/index.scss +++ b/src/plugins/dev_tools/public/index.scss @@ -16,10 +16,6 @@ } } -.devApp { - height: 100%; -} - .devAppWrapper { display: flex; flex-direction: column; diff --git a/src/plugins/dev_tools/public/plugin.ts b/src/plugins/dev_tools/public/plugin.ts index fcc6a57361a94d..8c4743c93fab33 100644 --- a/src/plugins/dev_tools/public/plugin.ts +++ b/src/plugins/dev_tools/public/plugin.ts @@ -60,7 +60,7 @@ export class DevToolsPlugin implements Plugin { defaultMessage: 'Dev Tools', }), updater$: this.appStateUpdater, - euiIconType: 'devToolsApp', + euiIconType: 'logoElastic', order: 9010, category: DEFAULT_APP_CATEGORIES.management, mount: async (params: AppMountParameters) => { diff --git a/src/plugins/discover/public/application/angular/discover.html b/src/plugins/discover/public/application/angular/discover.html index 94f13e1cd81325..e0e452aaa41c51 100644 --- a/src/plugins/discover/public/application/angular/discover.html +++ b/src/plugins/discover/public/application/angular/discover.html @@ -5,6 +5,7 @@

{{screenTitle}}

(uiActions = pluginUiActions); export const getUiActions = () => uiActions; +export const [getHeaderActionMenuMounter, setHeaderActionMenuMounter] = createGetterSetter< + AppMountParameters['setHeaderActionMenu'] +>('headerActionMenuMounter'); + export const [getUrlTracker, setUrlTracker] = createGetterSetter<{ setTrackedUrl: (url: string) => void; restorePreviousUrl: () => void; diff --git a/src/plugins/discover/public/plugin.ts b/src/plugins/discover/public/plugin.ts index b6960c8a20abfc..dd9b57b568e42c 100644 --- a/src/plugins/discover/public/plugin.ts +++ b/src/plugins/discover/public/plugin.ts @@ -54,6 +54,7 @@ import { setUrlTracker, setAngularModule, setServices, + setHeaderActionMenuMounter, setUiActions, setScopedHistory, getScopedHistory, @@ -240,7 +241,7 @@ export class DiscoverPlugin title: 'Discover', updater$: this.appStateUpdater.asObservable(), order: 1000, - euiIconType: 'discoverApp', + euiIconType: 'logoKibana', defaultPath: '#/', category: DEFAULT_APP_CATEGORIES.kibana, mount: async (params: AppMountParameters) => { @@ -251,6 +252,7 @@ export class DiscoverPlugin throw Error('Discover plugin method initializeInnerAngular is undefined'); } setScopedHistory(params.history); + setHeaderActionMenuMounter(params.setHeaderActionMenu); syncHistoryLocations(); appMounted(); const { @@ -264,6 +266,7 @@ export class DiscoverPlugin params.element.classList.add('dscAppWrapper'); const unmount = await renderApp(innerAngularName, params.element); return () => { + params.element.classList.remove('dscAppWrapper'); unmount(); appUnMounted(); }; diff --git a/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts b/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts index 570a78fc41ea91..b22f16c94aff8d 100644 --- a/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts +++ b/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts @@ -37,11 +37,11 @@ export const defaultEmbeddableFactoryProvider = < getExplicitInput: def.getExplicitInput ? def.getExplicitInput.bind(def) : () => Promise.resolve({}), - createFromSavedObject: - def.createFromSavedObject ?? - ((savedObjectId: string, input: Partial, parent?: IContainer) => { - throw new Error(`Creation from saved object not supported by type ${def.type}`); - }), + createFromSavedObject: def.createFromSavedObject + ? def.createFromSavedObject.bind(def) + : (savedObjectId: string, input: Partial, parent?: IContainer) => { + throw new Error(`Creation from saved object not supported by type ${def.type}`); + }, create: def.create.bind(def), type: def.type, isEditable: def.isEditable.bind(def), diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx index c14471991ccd33..dbf53a9f0a359a 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.test.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { useEffect } from 'react'; +import React, { useEffect, FunctionComponent } from 'react'; import { act } from 'react-dom/test-utils'; import { registerTestBed, TestBed } from '../shared_imports'; @@ -237,4 +237,64 @@ describe('', () => { expect(serializer).not.toBeCalled(); }); }); + + describe('custom components', () => { + interface MyForm { + name: string; + } + + let formHook: FormHook | null = null; + + beforeEach(() => { + formHook = null; + }); + + const onFormHook = (_form: FormHook) => { + formHook = _form; + }; + + const TestComp = ({ + component, + onForm, + }: { + component: FunctionComponent; + onForm: (form: FormHook) => void; + }) => { + const { form } = useForm(); + + useEffect(() => { + onForm(form); + }, [onForm, form]); + + return ( +
+ + + ); + }; + + it('allows function components', () => { + const Component = () =>