diff --git a/.ci/Dockerfile b/.ci/Dockerfile index 065b7f2aafd61..d6eee046611bb 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,7 +1,7 @@ # NOTE: This Dockerfile is ONLY used to run certain tasks in CI. It is not used to run Kibana or as a distributable. # If you're looking for the Kibana Docker image distributable, please see: src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts -ARG NODE_VERSION=10.22.0 +ARG NODE_VERSION=10.22.1 FROM node:${NODE_VERSION} AS base diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy index 0d9f5c9d92453..025836a90204c 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 5efbaba32e00a..d81f6af4cec28 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 @@ -128,6 +130,7 @@ /packages/kbn-test/ @elastic/kibana-operations /packages/kbn-ui-shared-deps/ @elastic/kibana-operations /packages/kbn-es-archiver/ @elastic/kibana-operations +/packages/kbn-utils/ @elastic/kibana-operations /src/legacy/server/keystore/ @elastic/kibana-operations /src/legacy/server/pid/ @elastic/kibana-operations /src/legacy/server/sass/ @elastic/kibana-operations @@ -152,6 +155,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 +297,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/.i18nrc.json b/.i18nrc.json index e8431fdb3f0e1..153a5a6cafece 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -57,7 +57,8 @@ "visTypeXy": "src/plugins/vis_type_xy", "visualizations": "src/plugins/visualizations", "visualize": "src/plugins/visualize", - "apmOss": "src/plugins/apm_oss" + "apmOss": "src/plugins/apm_oss", + "usageCollection": "src/plugins/usage_collection" }, "exclude": [ "src/legacy/ui/ui_render/ui_render_mixin.js" diff --git a/.node-version b/.node-version index b7604b0c8aea6..c2f6421352c48 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -10.22.0 +10.22.1 diff --git a/.nvmrc b/.nvmrc index b7604b0c8aea6..c2f6421352c48 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -10.22.0 +10.22.1 diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index 020ec8018b85b..56bd4abbc8023 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -23,7 +23,7 @@ experimental[] Import dashboards and corresponding saved objects. [[dashboard-api-import-request-body]] ==== Request body -Use the complete response body from the <> as the request body. Do not manually construct a payload to the endpoint. +Use the complete response body from the <> as the request body. Do not manually construct a payload to the endpoint. The max payload size is determined by the `savedObjects.maxImportPayloadBytes` configuration key. [[dashboard-api-import-response-body]] ==== Response body diff --git a/docs/developer/architecture/code-exploration.asciidoc b/docs/developer/architecture/code-exploration.asciidoc new file mode 100644 index 0000000000000..4a390336da34f --- /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 0000000000000..833478ccbda68 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 42cee6ef0e58a..13ea010d0aa96 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 0000000000000..d01f2c2aa0f95 --- /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 275fdf8fb69ad..501a3698d07d9 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. @@ -490,6 +494,10 @@ in their infrastructure. |This plugins adopts some conventions in addition to or in place of conventions in Kibana (at the time of the plugin's creation): +|{kib-repo}blob/{branch}/x-pack/plugins/xpack_legacy/README.md[xpackLegacy] +|Contains HTTP endpoints and UiSettings that are slated for removal. + + |=== include::{kibana-root}/src/plugins/dashboard/README.asciidoc[leveloffset=+1] diff --git a/docs/developer/plugin/external-plugin-functional-tests.asciidoc b/docs/developer/plugin/external-plugin-functional-tests.asciidoc index 7e5b5b79d06e9..b39d576d85268 100644 --- a/docs/developer/plugin/external-plugin-functional-tests.asciidoc +++ b/docs/developer/plugin/external-plugin-functional-tests.asciidoc @@ -13,7 +13,7 @@ To get started copy and paste this example to `test/functional/config.js`: ["source","js"] ----------- import { resolve } from 'path'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { MyServiceProvider } from './services/my_service'; import { MyAppPageProvider } from './services/my_app_page'; 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 8fefd4450d49b..0000000000000 --- 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 bca69adeef66b..47365782599ed 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 0000000000000..2f921050e58dd --- /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 c5c78bf9fb1da..514c44bd9d710 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 12058f1d16ab9..eb56e0e38c6c9 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 09864be43996d..0000000000000 --- 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 e983ad50d2afe..2594848ef0847 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 7c879b659a852..0000000000000 --- 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.environmentmode.dev.md b/docs/development/core/public/kibana-plugin-core-public.environmentmode.dev.md deleted file mode 100644 index 68ea11c62cf96..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.environmentmode.dev.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) > [dev](./kibana-plugin-core-public.environmentmode.dev.md) - -## EnvironmentMode.dev property - -Signature: - -```typescript -dev: boolean; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.environmentmode.md b/docs/development/core/public/kibana-plugin-core-public.environmentmode.md deleted file mode 100644 index 34ca3a0000563..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.environmentmode.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) - -## EnvironmentMode interface - - -Signature: - -```typescript -export interface EnvironmentMode -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [dev](./kibana-plugin-core-public.environmentmode.dev.md) | boolean | | -| [name](./kibana-plugin-core-public.environmentmode.name.md) | 'development' | 'production' | | -| [prod](./kibana-plugin-core-public.environmentmode.prod.md) | boolean | | - diff --git a/docs/development/core/public/kibana-plugin-core-public.environmentmode.name.md b/docs/development/core/public/kibana-plugin-core-public.environmentmode.name.md deleted file mode 100644 index 2d1722a82417c..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.environmentmode.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) > [name](./kibana-plugin-core-public.environmentmode.name.md) - -## EnvironmentMode.name property - -Signature: - -```typescript -name: 'development' | 'production'; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.environmentmode.prod.md b/docs/development/core/public/kibana-plugin-core-public.environmentmode.prod.md deleted file mode 100644 index 5145f8f6887e5..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.environmentmode.prod.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) > [prod](./kibana-plugin-core-public.environmentmode.prod.md) - -## EnvironmentMode.prod property - -Signature: - -```typescript -prod: boolean; -``` 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 fee87dde25c28..0000000000000 --- 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 3ef9b6bf703eb..0000000000000 --- 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 3c2ffa6340a97..0000000000000 --- 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 08b12190ef638..85ef00d271415 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 | @@ -65,7 +55,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [CoreSetup](./kibana-plugin-core-public.coresetup.md) | Core services exposed to the Plugin setup lifecycle | | [CoreStart](./kibana-plugin-core-public.corestart.md) | Core services exposed to the Plugin start lifecycle | | [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) | | -| [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) | | | [ErrorToastOptions](./kibana-plugin-core-public.errortoastoptions.md) | Options available for [IToasts](./kibana-plugin-core-public.itoasts.md) error APIs. | | [FatalErrorInfo](./kibana-plugin-core-public.fatalerrorinfo.md) | Represents the message and stack of a fatal Error | | [FatalErrorsSetup](./kibana-plugin-core-public.fatalerrorssetup.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. | @@ -95,7 +84,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [OverlayBannersStart](./kibana-plugin-core-public.overlaybannersstart.md) | | | [OverlayRef](./kibana-plugin-core-public.overlayref.md) | Returned by [OverlayStart](./kibana-plugin-core-public.overlaystart.md) methods for closing a mounted overlay. | | [OverlayStart](./kibana-plugin-core-public.overlaystart.md) | | -| [PackageInfo](./kibana-plugin-core-public.packageinfo.md) | | | [Plugin](./kibana-plugin-core-public.plugin.md) | The interface that should be returned by a PluginInitializer. | | [PluginInitializerContext](./kibana-plugin-core-public.plugininitializercontext.md) | The available core services passed to a PluginInitializer | | [SavedObject](./kibana-plugin-core-public.savedobject.md) | | @@ -128,7 +116,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 +143,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 b174f733a5c64..0000000000000 --- 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.packageinfo.branch.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.branch.md deleted file mode 100644 index 24fd8a74c84f1..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.branch.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [branch](./kibana-plugin-core-public.packageinfo.branch.md) - -## PackageInfo.branch property - -Signature: - -```typescript -branch: string; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildnum.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildnum.md deleted file mode 100644 index b849b7bdc2a54..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildnum.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [buildNum](./kibana-plugin-core-public.packageinfo.buildnum.md) - -## PackageInfo.buildNum property - -Signature: - -```typescript -buildNum: number; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildsha.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildsha.md deleted file mode 100644 index 42e5a21ab2f54..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildsha.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [buildSha](./kibana-plugin-core-public.packageinfo.buildsha.md) - -## PackageInfo.buildSha property - -Signature: - -```typescript -buildSha: string; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.dist.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.dist.md deleted file mode 100644 index ac6e39e07172b..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.dist.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [dist](./kibana-plugin-core-public.packageinfo.dist.md) - -## PackageInfo.dist property - -Signature: - -```typescript -dist: boolean; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.md deleted file mode 100644 index e819fb7990e5d..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) - -## PackageInfo interface - - -Signature: - -```typescript -export interface PackageInfo -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [branch](./kibana-plugin-core-public.packageinfo.branch.md) | string | | -| [buildNum](./kibana-plugin-core-public.packageinfo.buildnum.md) | number | | -| [buildSha](./kibana-plugin-core-public.packageinfo.buildsha.md) | string | | -| [dist](./kibana-plugin-core-public.packageinfo.dist.md) | boolean | | -| [version](./kibana-plugin-core-public.packageinfo.version.md) | string | | - diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.version.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.version.md deleted file mode 100644 index 5a7649a1445f6..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.version.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [version](./kibana-plugin-core-public.packageinfo.version.md) - -## PackageInfo.version property - -Signature: - -```typescript -version: string; -``` 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 238dd66885896..0000000000000 --- 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 161e7dc7ebfae..0000000000000 --- 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 f1884718337b5..0000000000000 --- 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 2816d4c7df541..0000000000000 --- 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 5ad21f004481c..0000000000000 --- 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 2e70da2f17421..0000000000000 --- 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 cedc7f0b878e3..0000000000000 --- 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 a9541efe0882a..0000000000000 --- 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 cb28a25f9e162..0000000000000 --- 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 c13c88df9b9bf..0000000000000 --- 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.configdeprecation.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecation.md deleted file mode 100644 index 6fa74bccb4fc5..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecation.md +++ /dev/null @@ -1,18 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) - -## ConfigDeprecation type - -Configuration deprecation returned from [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md) that handles a single deprecation from the configuration. - -Signature: - -```typescript -export declare type ConfigDeprecation = (config: Record, fromPath: string, logger: ConfigDeprecationLogger) => Record; -``` - -## Remarks - -This should only be manually implemented if [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) does not provide the proper helpers for a specific deprecation need. - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.md deleted file mode 100644 index 8b34b379af67e..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.md +++ /dev/null @@ -1,36 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) - -## ConfigDeprecationFactory interface - -Provides helpers to generates the most commonly used [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) when invoking a [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md). - -See methods documentation for more detailed examples. - -Signature: - -```typescript -export interface ConfigDeprecationFactory -``` - -## Example - - -```typescript -const provider: ConfigDeprecationProvider = ({ rename, unused }) => [ - rename('oldKey', 'newKey'), - unused('deprecatedKey'), -] - -``` - -## Methods - -| Method | Description | -| --- | --- | -| [rename(oldKey, newKey)](./kibana-plugin-core-server.configdeprecationfactory.rename.md) | Rename a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the oldKey was found and deprecation applied. | -| [renameFromRoot(oldKey, newKey, silent)](./kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md) | Rename a configuration property from the root configuration. Will log a deprecation warning if the oldKey was found and deprecation applied.This should be only used when renaming properties from different configuration's path. To rename properties from inside a plugin's configuration, use 'rename' instead. | -| [unused(unusedKey)](./kibana-plugin-core-server.configdeprecationfactory.unused.md) | Remove a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the unused key was found and deprecation applied. | -| [unusedFromRoot(unusedKey)](./kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md) | Remove a configuration property from the root configuration. Will log a deprecation warning if the unused key was found and deprecation applied.This should be only used when removing properties from outside of a plugin's configuration. To remove properties from inside a plugin's configuration, use 'unused' instead. | - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.rename.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.rename.md deleted file mode 100644 index 91bf1b86fe52e..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.rename.md +++ /dev/null @@ -1,36 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) > [rename](./kibana-plugin-core-server.configdeprecationfactory.rename.md) - -## ConfigDeprecationFactory.rename() method - -Rename a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the oldKey was found and deprecation applied. - -Signature: - -```typescript -rename(oldKey: string, newKey: string): ConfigDeprecation; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| oldKey | string | | -| newKey | string | | - -Returns: - -`ConfigDeprecation` - -## Example - -Rename 'myplugin.oldKey' to 'myplugin.newKey' - -```typescript -const provider: ConfigDeprecationProvider = ({ rename }) => [ - rename('oldKey', 'newKey'), -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md deleted file mode 100644 index cc8cde9a5ed79..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md +++ /dev/null @@ -1,39 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) > [renameFromRoot](./kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md) - -## ConfigDeprecationFactory.renameFromRoot() method - -Rename a configuration property from the root configuration. Will log a deprecation warning if the oldKey was found and deprecation applied. - -This should be only used when renaming properties from different configuration's path. To rename properties from inside a plugin's configuration, use 'rename' instead. - -Signature: - -```typescript -renameFromRoot(oldKey: string, newKey: string, silent?: boolean): ConfigDeprecation; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| oldKey | string | | -| newKey | string | | -| silent | boolean | | - -Returns: - -`ConfigDeprecation` - -## Example - -Rename 'oldplugin.key' to 'newplugin.key' - -```typescript -const provider: ConfigDeprecationProvider = ({ renameFromRoot }) => [ - renameFromRoot('oldplugin.key', 'newplugin.key'), -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unused.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unused.md deleted file mode 100644 index e7443b135984f..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unused.md +++ /dev/null @@ -1,35 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) > [unused](./kibana-plugin-core-server.configdeprecationfactory.unused.md) - -## ConfigDeprecationFactory.unused() method - -Remove a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the unused key was found and deprecation applied. - -Signature: - -```typescript -unused(unusedKey: string): ConfigDeprecation; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| unusedKey | string | | - -Returns: - -`ConfigDeprecation` - -## Example - -Flags 'myplugin.deprecatedKey' as unused - -```typescript -const provider: ConfigDeprecationProvider = ({ unused }) => [ - unused('deprecatedKey'), -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md deleted file mode 100644 index 8d5ed349e60e9..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md +++ /dev/null @@ -1,37 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) > [unusedFromRoot](./kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md) - -## ConfigDeprecationFactory.unusedFromRoot() method - -Remove a configuration property from the root configuration. Will log a deprecation warning if the unused key was found and deprecation applied. - -This should be only used when removing properties from outside of a plugin's configuration. To remove properties from inside a plugin's configuration, use 'unused' instead. - -Signature: - -```typescript -unusedFromRoot(unusedKey: string): ConfigDeprecation; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| unusedKey | string | | - -Returns: - -`ConfigDeprecation` - -## Example - -Flags 'somepath.deprecatedProperty' as unused - -```typescript -const provider: ConfigDeprecationProvider = ({ unusedFromRoot }) => [ - unusedFromRoot('somepath.deprecatedProperty'), -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationlogger.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationlogger.md deleted file mode 100644 index 15da7d24b4a7c..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationlogger.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationLogger](./kibana-plugin-core-server.configdeprecationlogger.md) - -## ConfigDeprecationLogger type - -Logger interface used when invoking a [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) - -Signature: - -```typescript -export declare type ConfigDeprecationLogger = (message: string) => void; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationprovider.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationprovider.md deleted file mode 100644 index 64351cbdb4a71..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationprovider.md +++ /dev/null @@ -1,28 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md) - -## ConfigDeprecationProvider type - -A provider that should returns a list of [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md). - -See [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) for more usage examples. - -Signature: - -```typescript -export declare type ConfigDeprecationProvider = (factory: ConfigDeprecationFactory) => ConfigDeprecation[]; -``` - -## Example - - -```typescript -const provider: ConfigDeprecationProvider = ({ rename, unused }) => [ - rename('oldKey', 'newKey'), - unused('deprecatedKey'), - myCustomDeprecation, -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configpath.md b/docs/development/core/server/kibana-plugin-core-server.configpath.md deleted file mode 100644 index 0b15d3ca727f2..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configpath.md +++ /dev/null @@ -1,12 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigPath](./kibana-plugin-core-server.configpath.md) - -## ConfigPath type - - -Signature: - -```typescript -export declare type ConfigPath = string | string[]; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.coresetup.md b/docs/development/core/server/kibana-plugin-core-server.coresetup.md index ccc73d4fb858e..75da8df2ae15a 100644 --- a/docs/development/core/server/kibana-plugin-core-server.coresetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.coresetup.md @@ -23,6 +23,7 @@ export interface CoreSetupStartServicesAccessor<TPluginsStart, TStart> | [StartServicesAccessor](./kibana-plugin-core-server.startservicesaccessor.md) | | [http](./kibana-plugin-core-server.coresetup.http.md) | HttpServiceSetup & {
resources: HttpResources;
} | [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) | | [logging](./kibana-plugin-core-server.coresetup.logging.md) | LoggingServiceSetup | [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md) | +| [metrics](./kibana-plugin-core-server.coresetup.metrics.md) | MetricsServiceSetup | [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) | | [savedObjects](./kibana-plugin-core-server.coresetup.savedobjects.md) | SavedObjectsServiceSetup | [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) | | [status](./kibana-plugin-core-server.coresetup.status.md) | StatusServiceSetup | [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) | | [uiSettings](./kibana-plugin-core-server.coresetup.uisettings.md) | UiSettingsServiceSetup | [UiSettingsServiceSetup](./kibana-plugin-core-server.uisettingsservicesetup.md) | diff --git a/docs/development/core/server/kibana-plugin-core-server.coresetup.metrics.md b/docs/development/core/server/kibana-plugin-core-server.coresetup.metrics.md new file mode 100644 index 0000000000000..77c9e867ef8ea --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.coresetup.metrics.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreSetup](./kibana-plugin-core-server.coresetup.md) > [metrics](./kibana-plugin-core-server.coresetup.metrics.md) + +## CoreSetup.metrics property + +[MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) + +Signature: + +```typescript +metrics: MetricsServiceSetup; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.corestart.md b/docs/development/core/server/kibana-plugin-core-server.corestart.md index 610c85c71e362..0d5474fae5e16 100644 --- a/docs/development/core/server/kibana-plugin-core-server.corestart.md +++ b/docs/development/core/server/kibana-plugin-core-server.corestart.md @@ -20,7 +20,7 @@ export interface CoreStart | [capabilities](./kibana-plugin-core-server.corestart.capabilities.md) | CapabilitiesStart | [CapabilitiesStart](./kibana-plugin-core-server.capabilitiesstart.md) | | [elasticsearch](./kibana-plugin-core-server.corestart.elasticsearch.md) | ElasticsearchServiceStart | [ElasticsearchServiceStart](./kibana-plugin-core-server.elasticsearchservicestart.md) | | [http](./kibana-plugin-core-server.corestart.http.md) | HttpServiceStart | [HttpServiceStart](./kibana-plugin-core-server.httpservicestart.md) | -| [metrics](./kibana-plugin-core-server.corestart.metrics.md) | MetricsServiceStart | | +| [metrics](./kibana-plugin-core-server.corestart.metrics.md) | MetricsServiceStart | [MetricsServiceStart](./kibana-plugin-core-server.metricsservicestart.md) | | [savedObjects](./kibana-plugin-core-server.corestart.savedobjects.md) | SavedObjectsServiceStart | [SavedObjectsServiceStart](./kibana-plugin-core-server.savedobjectsservicestart.md) | | [uiSettings](./kibana-plugin-core-server.corestart.uisettings.md) | UiSettingsServiceStart | [UiSettingsServiceStart](./kibana-plugin-core-server.uisettingsservicestart.md) | diff --git a/docs/development/core/server/kibana-plugin-core-server.corestart.metrics.md b/docs/development/core/server/kibana-plugin-core-server.corestart.metrics.md index a51c2f842c346..2c32f730c4c9b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.corestart.metrics.md +++ b/docs/development/core/server/kibana-plugin-core-server.corestart.metrics.md @@ -4,6 +4,7 @@ ## CoreStart.metrics property +[MetricsServiceStart](./kibana-plugin-core-server.metricsservicestart.md) Signature: 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 946050bff0585..0000000000000 --- 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.environmentmode.dev.md b/docs/development/core/server/kibana-plugin-core-server.environmentmode.dev.md deleted file mode 100644 index c1b9d5b7e19b1..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.environmentmode.dev.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) > [dev](./kibana-plugin-core-server.environmentmode.dev.md) - -## EnvironmentMode.dev property - -Signature: - -```typescript -dev: boolean; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.environmentmode.md b/docs/development/core/server/kibana-plugin-core-server.environmentmode.md deleted file mode 100644 index 94795754739ab..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.environmentmode.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) - -## EnvironmentMode interface - - -Signature: - -```typescript -export interface EnvironmentMode -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [dev](./kibana-plugin-core-server.environmentmode.dev.md) | boolean | | -| [name](./kibana-plugin-core-server.environmentmode.name.md) | 'development' | 'production' | | -| [prod](./kibana-plugin-core-server.environmentmode.prod.md) | boolean | | - diff --git a/docs/development/core/server/kibana-plugin-core-server.environmentmode.name.md b/docs/development/core/server/kibana-plugin-core-server.environmentmode.name.md deleted file mode 100644 index 5627deab9e83e..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.environmentmode.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) > [name](./kibana-plugin-core-server.environmentmode.name.md) - -## EnvironmentMode.name property - -Signature: - -```typescript -name: 'development' | 'production'; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.environmentmode.prod.md b/docs/development/core/server/kibana-plugin-core-server.environmentmode.prod.md deleted file mode 100644 index 1ed3fa33b8997..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.environmentmode.prod.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) > [prod](./kibana-plugin-core-server.environmentmode.prod.md) - -## EnvironmentMode.prod property - -Signature: - -```typescript -prod: boolean; -``` 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 32ba89e8370c1..0000000000000 --- 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 2e7850ca579f6..0000000000000 --- 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 bff9eb05419be..0000000000000 --- 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.logger.debug.md b/docs/development/core/server/kibana-plugin-core-server.logger.debug.md deleted file mode 100644 index c2b95d92b21d1..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.debug.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [debug](./kibana-plugin-core-server.logger.debug.md) - -## Logger.debug() method - -Log messages useful for debugging and interactive investigation - -Signature: - -```typescript -debug(message: string, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| message | string | The log message | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.error.md b/docs/development/core/server/kibana-plugin-core-server.logger.error.md deleted file mode 100644 index 68e7499fabc32..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.error.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [error](./kibana-plugin-core-server.logger.error.md) - -## Logger.error() method - -Logs abnormal or unexpected errors or messages that caused a failure in the application flow - -Signature: - -```typescript -error(errorOrMessage: string | Error, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| errorOrMessage | string | Error | An Error object or message string to log | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.fatal.md b/docs/development/core/server/kibana-plugin-core-server.logger.fatal.md deleted file mode 100644 index d39194d2126b7..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.fatal.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [fatal](./kibana-plugin-core-server.logger.fatal.md) - -## Logger.fatal() method - -Logs abnormal or unexpected errors or messages that caused an unrecoverable failure - -Signature: - -```typescript -fatal(errorOrMessage: string | Error, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| errorOrMessage | string | Error | An Error object or message string to log | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.get.md b/docs/development/core/server/kibana-plugin-core-server.logger.get.md deleted file mode 100644 index 5f69831a73808..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.get.md +++ /dev/null @@ -1,33 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [get](./kibana-plugin-core-server.logger.get.md) - -## Logger.get() method - -Returns a new [Logger](./kibana-plugin-core-server.logger.md) instance extending the current logger context. - -Signature: - -```typescript -get(...childContextPaths: string[]): Logger; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| childContextPaths | string[] | | - -Returns: - -`Logger` - -## Example - - -```typescript -const logger = loggerFactory.get('plugin', 'service'); // 'plugin.service' context -const subLogger = logger.get('feature'); // 'plugin.service.feature' context - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.info.md b/docs/development/core/server/kibana-plugin-core-server.logger.info.md deleted file mode 100644 index 7e786ed0e3f9b..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.info.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [info](./kibana-plugin-core-server.logger.info.md) - -## Logger.info() method - -Logs messages related to general application flow - -Signature: - -```typescript -info(message: string, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| message | string | The log message | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.md b/docs/development/core/server/kibana-plugin-core-server.logger.md deleted file mode 100644 index 7012358524c35..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.md +++ /dev/null @@ -1,26 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) - -## Logger interface - -Logger exposes all the necessary methods to log any type of information and this is the interface used by the logging consumers including plugins. - -Signature: - -```typescript -export interface Logger -``` - -## Methods - -| Method | Description | -| --- | --- | -| [debug(message, meta)](./kibana-plugin-core-server.logger.debug.md) | Log messages useful for debugging and interactive investigation | -| [error(errorOrMessage, meta)](./kibana-plugin-core-server.logger.error.md) | Logs abnormal or unexpected errors or messages that caused a failure in the application flow | -| [fatal(errorOrMessage, meta)](./kibana-plugin-core-server.logger.fatal.md) | Logs abnormal or unexpected errors or messages that caused an unrecoverable failure | -| [get(childContextPaths)](./kibana-plugin-core-server.logger.get.md) | Returns a new [Logger](./kibana-plugin-core-server.logger.md) instance extending the current logger context. | -| [info(message, meta)](./kibana-plugin-core-server.logger.info.md) | Logs messages related to general application flow | -| [trace(message, meta)](./kibana-plugin-core-server.logger.trace.md) | Log messages at the most detailed log level | -| [warn(errorOrMessage, meta)](./kibana-plugin-core-server.logger.warn.md) | Logs abnormal or unexpected errors or messages | - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.trace.md b/docs/development/core/server/kibana-plugin-core-server.logger.trace.md deleted file mode 100644 index b8d761f69541e..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.trace.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [trace](./kibana-plugin-core-server.logger.trace.md) - -## Logger.trace() method - -Log messages at the most detailed log level - -Signature: - -```typescript -trace(message: string, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| message | string | The log message | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.warn.md b/docs/development/core/server/kibana-plugin-core-server.logger.warn.md deleted file mode 100644 index 21cefe4f748dd..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.warn.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [warn](./kibana-plugin-core-server.logger.warn.md) - -## Logger.warn() method - -Logs abnormal or unexpected errors or messages - -Signature: - -```typescript -warn(errorOrMessage: string | Error, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| errorOrMessage | string | Error | An Error object or message string to log | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.loggerfactory.get.md b/docs/development/core/server/kibana-plugin-core-server.loggerfactory.get.md deleted file mode 100644 index d081d359b9480..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.loggerfactory.get.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LoggerFactory](./kibana-plugin-core-server.loggerfactory.md) > [get](./kibana-plugin-core-server.loggerfactory.get.md) - -## LoggerFactory.get() method - -Returns a `Logger` instance for the specified context. - -Signature: - -```typescript -get(...contextParts: string[]): Logger; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| contextParts | string[] | Parts of the context to return logger for. For example get('plugins', 'pid') will return a logger for the plugins.pid context. | - -Returns: - -`Logger` - diff --git a/docs/development/core/server/kibana-plugin-core-server.loggerfactory.md b/docs/development/core/server/kibana-plugin-core-server.loggerfactory.md deleted file mode 100644 index 8795413bf675a..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.loggerfactory.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LoggerFactory](./kibana-plugin-core-server.loggerfactory.md) - -## LoggerFactory interface - -The single purpose of `LoggerFactory` interface is to define a way to retrieve a context-based logger instance. - -Signature: - -```typescript -export interface LoggerFactory -``` - -## Methods - -| Method | Description | -| --- | --- | -| [get(contextParts)](./kibana-plugin-core-server.loggerfactory.get.md) | Returns a Logger instance for the specified context. | - diff --git a/docs/development/core/server/kibana-plugin-core-server.logmeta.md b/docs/development/core/server/kibana-plugin-core-server.logmeta.md deleted file mode 100644 index 56a2af7e826c6..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logmeta.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LogMeta](./kibana-plugin-core-server.logmeta.md) - -## LogMeta interface - -Contextual metadata - -Signature: - -```typescript -export interface LogMeta -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index c16600d1d0492..b83c091846f04 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 @@ -70,7 +65,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [Capabilities](./kibana-plugin-core-server.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. | | [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | APIs to manage the [Capabilities](./kibana-plugin-core-server.capabilities.md) that will be used by the application.Plugins relying on capabilities to toggle some of their features should register them during the setup phase using the registerProvider method.Plugins having the responsibility to restrict capabilities depending on a given context should register their capabilities switcher using the registerSwitcher method.Refers to the methods documentation for complete description and examples. | | [CapabilitiesStart](./kibana-plugin-core-server.capabilitiesstart.md) | APIs to access the application [Capabilities](./kibana-plugin-core-server.capabilities.md). | -| [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) | Provides helpers to generates the most commonly used [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) when invoking a [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md).See methods documentation for more detailed examples. | | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. | | [CoreSetup](./kibana-plugin-core-server.coresetup.md) | Context passed to the plugins setup method. | | [CoreStart](./kibana-plugin-core-server.corestart.md) | Context passed to the plugins start method. | @@ -86,7 +80,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) | | | [ElasticsearchServiceStart](./kibana-plugin-core-server.elasticsearchservicestart.md) | | | [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) | | -| [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) | | | [ErrorHttpResponseOptions](./kibana-plugin-core-server.errorhttpresponseoptions.md) | HTTP response parameters | | [Explanation](./kibana-plugin-core-server.explanation.md) | | | [FakeRequest](./kibana-plugin-core-server.fakerequest.md) | Fake request object created manually by Kibana plugins. | @@ -119,11 +112,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | [LegacyRequest](./kibana-plugin-core-server.legacyrequest.md) | | | [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md) | | | [LegacyServiceStartDeps](./kibana-plugin-core-server.legacyservicestartdeps.md) | | -| [Logger](./kibana-plugin-core-server.logger.md) | Logger exposes all the necessary methods to log any type of information and this is the interface used by the logging consumers including plugins. | | [LoggerContextConfigInput](./kibana-plugin-core-server.loggercontextconfiginput.md) | | -| [LoggerFactory](./kibana-plugin-core-server.loggerfactory.md) | The single purpose of LoggerFactory interface is to define a way to retrieve a context-based logger instance. | | [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md) | Provides APIs to plugins for customizing the plugin's logger. | -| [LogMeta](./kibana-plugin-core-server.logmeta.md) | Contextual metadata | | [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) | APIs to retrieves metrics gathered and exposed by the core platform. | | [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) | | | [OnPostAuthToolkit](./kibana-plugin-core-server.onpostauthtoolkit.md) | A tool set defining an outcome of OnPostAuth interceptor for incoming request. | @@ -136,7 +126,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [OpsOsMetrics](./kibana-plugin-core-server.opsosmetrics.md) | OS related metrics | | [OpsProcessMetrics](./kibana-plugin-core-server.opsprocessmetrics.md) | Process related metrics | | [OpsServerMetrics](./kibana-plugin-core-server.opsservermetrics.md) | server related metrics | -| [PackageInfo](./kibana-plugin-core-server.packageinfo.md) | | | [Plugin](./kibana-plugin-core-server.plugin.md) | The interface that should be returned by a PluginInitializer. | | [PluginConfigDescriptor](./kibana-plugin-core-server.pluginconfigdescriptor.md) | Describes a plugin configuration properties. | | [PluginInitializerContext](./kibana-plugin-core-server.plugininitializercontext.md) | Context that's available to plugins during initialization stage. | @@ -217,7 +206,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 @@ -239,14 +227,9 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AuthResult](./kibana-plugin-core-server.authresult.md) | | | [CapabilitiesProvider](./kibana-plugin-core-server.capabilitiesprovider.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | | [CapabilitiesSwitcher](./kibana-plugin-core-server.capabilitiesswitcher.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | -| [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) | Configuration deprecation returned from [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md) that handles a single deprecation from the configuration. | -| [ConfigDeprecationLogger](./kibana-plugin-core-server.configdeprecationlogger.md) | Logger interface used when invoking a [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) | -| [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md) | A provider that should returns a list of [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md).See [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) for more usage examples. | -| [ConfigPath](./kibana-plugin-core-server.configpath.md) | | | [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. | @@ -270,6 +253,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [LegacyElasticsearchClientConfig](./kibana-plugin-core-server.legacyelasticsearchclientconfig.md) | | | [LifecycleResponseFactory](./kibana-plugin-core-server.lifecycleresponsefactory.md) | Creates an object containing redirection or error response with error details, HTTP headers, and other data transmitted to the client. | | [LoggerConfigType](./kibana-plugin-core-server.loggerconfigtype.md) | | +| [MetricsServiceStart](./kibana-plugin-core-server.metricsservicestart.md) | APIs to retrieves metrics gathered and exposed by the core platform. | | [MIGRATION\_ASSISTANCE\_INDEX\_ACTION](./kibana-plugin-core-server.migration_assistance_index_action.md) | | | [MIGRATION\_DEPRECATION\_LEVEL](./kibana-plugin-core-server.migration_deprecation_level.md) | | | [MutatingOperationRefreshSetting](./kibana-plugin-core-server.mutatingoperationrefreshsetting.md) | Elasticsearch Refresh setting for mutating operation | diff --git a/docs/development/core/server/kibana-plugin-core-server.metricsservicestart.md b/docs/development/core/server/kibana-plugin-core-server.metricsservicestart.md new file mode 100644 index 0000000000000..8b3280d528c18 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.metricsservicestart.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [MetricsServiceStart](./kibana-plugin-core-server.metricsservicestart.md) + +## MetricsServiceStart type + +APIs to retrieves metrics gathered and exposed by the core platform. + +Signature: + +```typescript +export declare type MetricsServiceStart = MetricsServiceSetup; +``` 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 fc0bc354a3ca3..0000000000000 --- 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.packageinfo.branch.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.branch.md deleted file mode 100644 index 34fbd56045921..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.branch.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [branch](./kibana-plugin-core-server.packageinfo.branch.md) - -## PackageInfo.branch property - -Signature: - -```typescript -branch: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildnum.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildnum.md deleted file mode 100644 index acc33fae18fbc..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildnum.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [buildNum](./kibana-plugin-core-server.packageinfo.buildnum.md) - -## PackageInfo.buildNum property - -Signature: - -```typescript -buildNum: number; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildsha.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildsha.md deleted file mode 100644 index 14ad75168fd85..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildsha.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [buildSha](./kibana-plugin-core-server.packageinfo.buildsha.md) - -## PackageInfo.buildSha property - -Signature: - -```typescript -buildSha: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.dist.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.dist.md deleted file mode 100644 index d7567010ddd53..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.dist.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [dist](./kibana-plugin-core-server.packageinfo.dist.md) - -## PackageInfo.dist property - -Signature: - -```typescript -dist: boolean; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.md deleted file mode 100644 index 3e1789d8df896..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) - -## PackageInfo interface - - -Signature: - -```typescript -export interface PackageInfo -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [branch](./kibana-plugin-core-server.packageinfo.branch.md) | string | | -| [buildNum](./kibana-plugin-core-server.packageinfo.buildnum.md) | number | | -| [buildSha](./kibana-plugin-core-server.packageinfo.buildsha.md) | string | | -| [dist](./kibana-plugin-core-server.packageinfo.dist.md) | boolean | | -| [version](./kibana-plugin-core-server.packageinfo.version.md) | string | | - diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.version.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.version.md deleted file mode 100644 index 1606ab5901941..0000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.version.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [version](./kibana-plugin-core-server.packageinfo.version.md) - -## PackageInfo.version property - -Signature: - -```typescript -version: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md b/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md index d539b5a0501c1..d06b3cb9fa64a 100644 --- a/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md +++ b/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md @@ -4,7 +4,7 @@ ## PluginConfigDescriptor.deprecations property -Provider for the [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) to apply to the plugin configuration. +Provider for the to apply to the plugin configuration. Signature: diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md b/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md index 964d45f155948..5708c4f9a3f88 100644 --- a/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md +++ b/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md @@ -44,7 +44,7 @@ export const config: PluginConfigDescriptor = { | Property | Type | Description | | --- | --- | --- | -| [deprecations](./kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md) | ConfigDeprecationProvider | Provider for the [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) to apply to the plugin configuration. | +| [deprecations](./kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md) | ConfigDeprecationProvider | Provider for the to apply to the plugin configuration. | | [exposeToBrowser](./kibana-plugin-core-server.pluginconfigdescriptor.exposetobrowser.md) | {
[P in keyof T]?: boolean;
} | List of configuration properties that will be available on the client-side plugin. | | [schema](./kibana-plugin-core-server.pluginconfigdescriptor.schema.md) | PluginConfigSchema<T> | Schema to use to validate the plugin configuration.[PluginConfigSchema](./kibana-plugin-core-server.pluginconfigschema.md) | diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.configpath.md b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.configpath.md index c391431e12b1c..8aa603242df8e 100644 --- a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.configpath.md +++ b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.configpath.md @@ -4,7 +4,7 @@ ## PluginManifest.configPath property -Root [configuration path](./kibana-plugin-core-server.configpath.md) used by the plugin, defaults to "id" in snake\_case format. +Root used by the plugin, defaults to "id" in snake\_case format. Signature: diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md index 6db2f89590149..b0182a7c48e16 100644 --- a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md +++ b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md @@ -20,7 +20,7 @@ Should never be used in code outside of Core but is exported for documentation p | Property | Type | Description | | --- | --- | --- | -| [configPath](./kibana-plugin-core-server.pluginmanifest.configpath.md) | ConfigPath | Root [configuration path](./kibana-plugin-core-server.configpath.md) used by the plugin, defaults to "id" in snake\_case format. | +| [configPath](./kibana-plugin-core-server.pluginmanifest.configpath.md) | ConfigPath | Root used by the plugin, defaults to "id" in snake\_case format. | | [extraPublicDirs](./kibana-plugin-core-server.pluginmanifest.extrapublicdirs.md) | string[] | Specifies directory names that can be imported by other ui-plugins built using the same instance of the @kbn/optimizer. A temporary measure we plan to replace with better mechanisms for sharing static code between plugins | | [id](./kibana-plugin-core-server.pluginmanifest.id.md) | PluginName | Identifier of the plugin. Must be a string in camelCase. Part of a plugin public contract. Other plugins leverage it to access plugin API, navigate to the plugin, etc. | | [kibanaVersion](./kibana-plugin-core-server.pluginmanifest.kibanaversion.md) | string | The version of Kibana the plugin is compatible with, defaults to "version". | diff --git a/docs/development/core/server/kibana-plugin-core-server.servicestatuslevels.md b/docs/development/core/server/kibana-plugin-core-server.servicestatuslevels.md index a66cec78c736b..e57dc192cd572 100644 --- a/docs/development/core/server/kibana-plugin-core-server.servicestatuslevels.md +++ b/docs/development/core/server/kibana-plugin-core-server.servicestatuslevels.md @@ -13,18 +13,22 @@ ServiceStatusLevels: Readonly<{ available: Readonly<{ toString: () => "available"; valueOf: () => 0; + toJSON: () => "available"; }>; degraded: Readonly<{ toString: () => "degraded"; valueOf: () => 1; + toJSON: () => "degraded"; }>; unavailable: Readonly<{ toString: () => "unavailable"; valueOf: () => 2; + toJSON: () => "unavailable"; }>; critical: Readonly<{ toString: () => "critical"; valueOf: () => 3; + toJSON: () => "critical"; }>; }> ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md b/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md index 7f306919101ef..ec2e1b227a2d7 100644 --- a/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md +++ b/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md @@ -12,5 +12,6 @@ export declare type SharedGlobalConfig = RecursiveReadonly<{ kibana: Pick; elasticsearch: Pick; path: Pick; + savedObjects: Pick; }>; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.isstatuspageanonymous.md b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.isstatuspageanonymous.md new file mode 100644 index 0000000000000..c417aaa2cef48 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.isstatuspageanonymous.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) > [isStatusPageAnonymous](./kibana-plugin-core-server.statusservicesetup.isstatuspageanonymous.md) + +## StatusServiceSetup.isStatusPageAnonymous property + +Whether or not the status HTTP APIs are available to unauthenticated users when an authentication provider is present. + +Signature: + +```typescript +isStatusPageAnonymous: () => boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.md b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.md index ba0645be4d26c..f522d11a7ffef 100644 --- a/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.md @@ -74,6 +74,7 @@ core.status.set( | [core$](./kibana-plugin-core-server.statusservicesetup.core_.md) | Observable<CoreStatus> | Current status for all Core services. | | [dependencies$](./kibana-plugin-core-server.statusservicesetup.dependencies_.md) | Observable<Record<string, ServiceStatus>> | Current status for all plugins this plugin depends on. Each key of the Record is a plugin id. | | [derivedStatus$](./kibana-plugin-core-server.statusservicesetup.derivedstatus_.md) | Observable<ServiceStatus> | The status of this plugin as derived from its dependencies. | +| [isStatusPageAnonymous](./kibana-plugin-core-server.statusservicesetup.isstatuspageanonymous.md) | () => boolean | Whether or not the status HTTP APIs are available to unauthenticated users when an authentication provider is present. | | [overall$](./kibana-plugin-core-server.statusservicesetup.overall_.md) | Observable<ServiceStatus> | Overall system status for all of Kibana. | ## Methods 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 0422738669a70..0000000000000 --- 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 13a3f4a9c95c8..0000000000000 --- 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 6631f6f6744c5..0000000000000 --- 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 257f7b4b634ab..0000000000000 --- 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 8fee8c8e146ca..0000000000000 --- 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 dcf3517d92ba2..0000000000000 --- 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 914dcd4e8a8a5..0000000000000 --- 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 358adcfd3d180..0000000000000 --- 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 d5b598167f2f2..0000000000000 --- 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 cf171d9ee9f37..e85747b8cc3d7 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 73ba8eb66040b..496e1ae9677d8 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/docs/user/dashboard/url-drilldown.asciidoc b/docs/user/dashboard/url-drilldown.asciidoc index 16f82477756b7..4919625340da2 100644 --- a/docs/user/dashboard/url-drilldown.asciidoc +++ b/docs/user/dashboard/url-drilldown.asciidoc @@ -197,6 +197,7 @@ context.panel.timeRange.indexPatternIds | ID of saved object behind a panel. | *Single click* + | event.value | Value behind clicked data point. @@ -208,6 +209,22 @@ context.panel.timeRange.indexPatternIds | event.negate | Boolean, indicating whether clicked data point resulted in negative filter. +| +| event.points +| Some visualizations have clickable points that emit more than one data point. Use list of data points in case a single value is insufficient. + + +Example: + +`{{json event.points}}` + +`{{event.points.[0].key}}` + +`{{event.points.[0].value}}` +`{{#each event.points}}key=value&{{/each}}` + +Note: + +`{{event.value}}` is a shorthand for `{{event.points.[0].value}}` + +`{{event.key}}` is a shorthand for `{{event.points.[0].key}}` + | *Range selection* | event.from + event.to diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc index 214dae2b96e04..8255585aae411 100644 --- a/docs/user/ml/index.asciidoc +++ b/docs/user/ml/index.asciidoc @@ -20,15 +20,11 @@ image::user/ml/images/ml-data-visualizer-sample.jpg[{data-viz} for sample flight experimental[] You can also upload a CSV, NDJSON, or log file. The *{data-viz}* identifies the file format and field mappings. You can then optionally import that data into an {es} index. To change the default file size limit, see -<>. +<>. -You need the following permissions to use the {data-viz} with file upload: - -* cluster privileges: `monitor`, `manage_ingest_pipelines` -* index privileges: `read`, `manage`, `index` - -For more information, see {ref}/security-privileges.html[Security privileges] -and {ml-docs}/setup.html[Set up {ml-features}]. +If {stack-security-features} are enabled, users must have the necessary +privileges to use {ml-features}. Refer to +{ml-docs}/setup.html#setup-privileges[Set up {ml-features}]. -- diff --git a/docs/user/security/authorization/index.asciidoc b/docs/user/security/authorization/index.asciidoc index 93d6d4249119f..44ca96e4aece5 100644 --- a/docs/user/security/authorization/index.asciidoc +++ b/docs/user/security/authorization/index.asciidoc @@ -28,7 +28,9 @@ Use the **Privilege** menu to grant access to features. The default is **Custom* When using the **Customize by feature** option, you can choose either **All**, **Read** or **None** for access to each feature. As new features are added to Kibana, roles that use the custom option do not automatically get access to the new features. You must manually update the roles. -NOTE: Machine Learning and Stack Monitoring rely on built-in roles to grant access. When a user is assigned the appropriate roles, the Machine Learning and Stack Monitoring application are available; otherwise, these applications are not visible. +NOTE: *{stack-monitor-app}* relies on built-in roles to grant access. When a +user is assigned the appropriate roles, the *{stack-monitor-app}* application is +available; otherwise, it is not visible. To apply your changes, click **Create space privilege**. The space privilege shows up under the Kibana privileges section of the role. diff --git a/examples/alerting_example/tsconfig.json b/examples/alerting_example/tsconfig.json index 09c130aca4642..214e4b78a9a70 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 798a9c222c5ab..86b35c5e4943f 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 798a9c222c5ab..86b35c5e4943f 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 798a9c222c5ab..86b35c5e4943f 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 caeed2c1a434f..78098339c16f5 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 798a9c222c5ab..86b35c5e4943f 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 761a5c4da65ba..54ac800019f82 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 8bec69ca40ccc..2b7d86d76a8a5 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 007322e2d9525..6cfb9f9dc2321 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 798a9c222c5ab..86b35c5e4943f 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 119209114a7bb..782b9cd57fa7b 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 327b4642a8e7f..8aef3328b4222 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 327b4642a8e7f..8aef3328b4222 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 7468a49d56959..c88fbc0e5fd07 100644 --- a/package.json +++ b/package.json @@ -78,27 +78,18 @@ }, "resolutions": { "**/@types/node": ">=10.17.17 <10.20.0", - "**/@types/react": "^16.9.36", - "**/@types/hapi": "^17.0.18", - "**/@types/angular": "^1.6.56", - "**/@types/hoist-non-react-statics": "^3.3.1", - "**/@types/chai": "^4.2.11", - "**/cypress/@types/lodash": "^4.14.159", - "**/cypress/lodash": "^4.17.20", - "**/typescript": "4.0.2", + "**/cross-fetch/node-fetch": "^2.6.1", + "**/deepmerge": "^4.2.2", + "**/fast-deep-equal": "^3.1.1", "**/graphql-toolkit/lodash": "^4.17.15", "**/hoist-non-react-statics": "^3.3.2", - "**/isomorphic-git/**/base64-js": "^1.2.1", + "**/isomorphic-fetch/node-fetch": "^2.6.1", "**/istanbul-instrumenter-loader/schema-utils": "1.0.0", - "**/image-diff/gm/debug": "^2.6.9", "**/load-grunt-config/lodash": "^4.17.20", + "**/minimist": "^1.2.5", "**/node-jose/node-forge": "^0.10.0", - "**/react-dom": "^16.12.0", - "**/react": "^16.12.0", - "**/react-test-renderer": "^16.12.0", "**/request": "^2.88.2", - "**/deepmerge": "^4.2.2", - "**/fast-deep-equal": "^3.1.1" + "**/typescript": "4.0.2" }, "workspaces": { "packages": [ @@ -128,7 +119,7 @@ "@babel/register": "^7.10.5", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "7.9.0-rc.2", - "@elastic/eui": "28.2.0", + "@elastic/eui": "28.4.0", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "^2.5.0", "@elastic/request-crypto": "1.1.4", @@ -137,10 +128,13 @@ "@hapi/wreck": "^15.0.2", "@kbn/analytics": "1.0.0", "@kbn/babel-preset": "1.0.0", + "@kbn/config": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", + "@kbn/logging": "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", @@ -191,7 +185,7 @@ "moment": "^2.24.0", "moment-timezone": "^0.5.27", "mustache": "2.3.2", - "node-fetch": "1.7.3", + "node-fetch": "2.6.1", "node-forge": "^0.10.0", "opn": "^5.5.0", "oppsy": "^2.0.0", @@ -290,6 +284,7 @@ "@types/hoek": "^4.1.3", "@types/inert": "^5.1.2", "@types/jest": "^25.2.3", + "@types/jest-when": "^2.7.1", "@types/joi": "^13.4.2", "@types/jquery": "^3.3.31", "@types/js-yaml": "^3.11.1", @@ -414,6 +409,7 @@ "jest-cli": "^25.5.4", "jest-environment-jsdom-thirteen": "^1.0.1", "jest-raw-loader": "^1.0.1", + "jest-when": "^2.7.2", "jimp": "^0.14.0", "jquery": "^3.5.0", "js-levenshtein": "^1.1.6", @@ -484,7 +480,7 @@ "zlib": "^1.0.5" }, "engines": { - "node": "10.22.0", + "node": "10.22.1", "yarn": "^1.21.1" } } diff --git a/packages/kbn-config/README.md b/packages/kbn-config/README.md new file mode 100644 index 0000000000000..343748cb0d817 --- /dev/null +++ b/packages/kbn-config/README.md @@ -0,0 +1,3 @@ +# `@kbn/config` — Kibana configuration file loader + +This package contains the configuration service used to load and read the kibana configuration file diff --git a/src/core/server/config/__fixtures__/config.yml b/packages/kbn-config/__fixtures__/config.yml similarity index 100% rename from src/core/server/config/__fixtures__/config.yml rename to packages/kbn-config/__fixtures__/config.yml diff --git a/src/core/server/config/__fixtures__/config_flat.yml b/packages/kbn-config/__fixtures__/config_flat.yml similarity index 100% rename from src/core/server/config/__fixtures__/config_flat.yml rename to packages/kbn-config/__fixtures__/config_flat.yml diff --git a/src/core/server/config/__fixtures__/en_var_ref_config.yml b/packages/kbn-config/__fixtures__/en_var_ref_config.yml similarity index 100% rename from src/core/server/config/__fixtures__/en_var_ref_config.yml rename to packages/kbn-config/__fixtures__/en_var_ref_config.yml diff --git a/src/core/server/config/__fixtures__/one.yml b/packages/kbn-config/__fixtures__/one.yml similarity index 100% rename from src/core/server/config/__fixtures__/one.yml rename to packages/kbn-config/__fixtures__/one.yml diff --git a/src/core/server/config/__fixtures__/two.yml b/packages/kbn-config/__fixtures__/two.yml similarity index 100% rename from src/core/server/config/__fixtures__/two.yml rename to packages/kbn-config/__fixtures__/two.yml diff --git a/packages/kbn-config/package.json b/packages/kbn-config/package.json new file mode 100644 index 0000000000000..2d9dbc3b7ab8f --- /dev/null +++ b/packages/kbn-config/package.json @@ -0,0 +1,30 @@ +{ + "name": "@kbn/config", + "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" + }, + "dependencies": { + "@elastic/safer-lodash-set": "0.0.0", + "@kbn/config-schema": "1.0.0", + "@kbn/dev-utils": "1.0.0", + "@kbn/logging": "1.0.0", + "@kbn/std": "1.0.0", + "@kbn/utility-types": "1.0.0", + "js-yaml": "3.13.1", + "load-json-file": "^6.2.0", + "lodash": "^4.17.20", + "moment": "^2.24.0", + "rxjs": "^6.5.5", + "type-detect": "^4.0.8" + }, + "devDependencies": { + "typescript": "4.0.2", + "tsd": "^0.7.4" + } +} diff --git a/src/core/server/config/__mocks__/env.ts b/packages/kbn-config/src/__mocks__/env.ts similarity index 100% rename from src/core/server/config/__mocks__/env.ts rename to packages/kbn-config/src/__mocks__/env.ts diff --git a/src/core/server/config/__snapshots__/config_service.test.ts.snap b/packages/kbn-config/src/__snapshots__/config_service.test.ts.snap similarity index 100% rename from src/core/server/config/__snapshots__/config_service.test.ts.snap rename to packages/kbn-config/src/__snapshots__/config_service.test.ts.snap diff --git a/src/core/server/config/__snapshots__/env.test.ts.snap b/packages/kbn-config/src/__snapshots__/env.test.ts.snap similarity index 100% rename from src/core/server/config/__snapshots__/env.test.ts.snap rename to packages/kbn-config/src/__snapshots__/env.test.ts.snap diff --git a/src/core/server/config/apply_argv.test.ts b/packages/kbn-config/src/apply_argv.test.ts similarity index 100% rename from src/core/server/config/apply_argv.test.ts rename to packages/kbn-config/src/apply_argv.test.ts diff --git a/src/core/server/config/config.mock.ts b/packages/kbn-config/src/config.mock.ts similarity index 100% rename from src/core/server/config/config.mock.ts rename to packages/kbn-config/src/config.mock.ts diff --git a/src/core/server/config/config.test.ts b/packages/kbn-config/src/config.test.ts similarity index 100% rename from src/core/server/config/config.test.ts rename to packages/kbn-config/src/config.test.ts diff --git a/src/core/server/config/config.ts b/packages/kbn-config/src/config.ts similarity index 99% rename from src/core/server/config/config.ts rename to packages/kbn-config/src/config.ts index a4026b1d88ac3..3d012dd5f3885 100644 --- a/src/core/server/config/config.ts +++ b/packages/kbn-config/src/config.ts @@ -23,7 +23,7 @@ export type ConfigPath = string | string[]; /** * Checks whether specified value can be considered as config path. * @param value Value to check. - * @internal + * @public */ export function isConfigPath(value: unknown): value is ConfigPath { if (!value) { diff --git a/src/core/server/config/config_service.mock.ts b/packages/kbn-config/src/config_service.mock.ts similarity index 100% rename from src/core/server/config/config_service.mock.ts rename to packages/kbn-config/src/config_service.mock.ts diff --git a/src/core/server/config/config_service.test.mocks.ts b/packages/kbn-config/src/config_service.test.mocks.ts similarity index 94% rename from src/core/server/config/config_service.test.mocks.ts rename to packages/kbn-config/src/config_service.test.mocks.ts index 1299c4c0b4eb1..1b70802e9805e 100644 --- a/src/core/server/config/config_service.test.mocks.ts +++ b/packages/kbn-config/src/config_service.test.mocks.ts @@ -18,7 +18,7 @@ */ export const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] }); -jest.mock('../../../../package.json', () => mockPackage); +jest.mock('../../../package.json', () => mockPackage); export const mockApplyDeprecations = jest.fn((config, deprecations, log) => config); jest.mock('./deprecation/apply_deprecations', () => ({ diff --git a/src/core/server/config/config_service.test.ts b/packages/kbn-config/src/config_service.test.ts similarity index 95% rename from src/core/server/config/config_service.test.ts rename to packages/kbn-config/src/config_service.test.ts index 95153447bd4a9..b32b405455700 100644 --- a/src/core/server/config/config_service.test.ts +++ b/packages/kbn-config/src/config_service.test.ts @@ -20,22 +20,36 @@ import { BehaviorSubject, Observable } from 'rxjs'; import { first, take } from 'rxjs/operators'; -import { mockPackage, mockApplyDeprecations } from './config_service.test.mocks'; -import { rawConfigServiceMock } from './raw_config_service.mock'; +import { mockApplyDeprecations } from './config_service.test.mocks'; +import { rawConfigServiceMock } from './raw/raw_config_service.mock'; import { schema } from '@kbn/config-schema'; +import { MockedLogger, loggerMock } from '@kbn/logging/target/mocks'; + +import { ConfigService, Env, RawPackageInfo } from '.'; -import { ConfigService, Env } from '.'; -import { loggingSystemMock } from '../logging/logging_system.mock'; import { getEnvOptions } from './__mocks__/env'; +const packageInfos: RawPackageInfo = { + branch: 'master', + version: '8.0.0', + build: { + number: 42, + sha: 'one', + }, +}; const emptyArgv = getEnvOptions(); -const defaultEnv = new Env('/kibana', emptyArgv); -const logger = loggingSystemMock.create(); +const defaultEnv = new Env('/kibana', packageInfos, emptyArgv); + +let logger: MockedLogger; const getRawConfigProvider = (rawConfig: Record) => rawConfigServiceMock.create({ rawConfig }); +beforeEach(() => { + logger = loggerMock.create(); +}); + test('returns config at path as observable', async () => { const rawConfig = getRawConfigProvider({ key: 'foo' }); const configService = new ConfigService(rawConfig, defaultEnv, logger); @@ -237,7 +251,7 @@ test('tracks unhandled paths', async () => { }); test('correctly passes context', async () => { - mockPackage.raw = { + const mockPackage = { branch: 'feature-v1', version: 'v1', build: { @@ -247,7 +261,7 @@ test('correctly passes context', async () => { }, }; - const env = new Env('/kibana', getEnvOptions()); + const env = new Env('/kibana', mockPackage, getEnvOptions()); const rawConfigProvider = rawConfigServiceMock.create({ rawConfig: { foo: {} } }); const schemaDefinition = schema.object({ @@ -441,9 +455,9 @@ test('logs deprecation warning during validation', async () => { return config; }); - loggingSystemMock.clear(logger); + loggerMock.clear(logger); await configService.validate(); - expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(` + expect(loggerMock.collect(logger).warn).toMatchInlineSnapshot(` Array [ Array [ "some deprecation message", diff --git a/src/core/server/config/config_service.ts b/packages/kbn-config/src/config_service.ts similarity index 97% rename from src/core/server/config/config_service.ts rename to packages/kbn-config/src/config_service.ts index bceba420bb6ce..faa8397cd6d6e 100644 --- a/src/core/server/config/config_service.ts +++ b/packages/kbn-config/src/config_service.ts @@ -17,22 +17,23 @@ * 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'; import { distinctUntilChanged, first, map, shareReplay, take } from 'rxjs/operators'; +import { Logger, LoggerFactory } from '@kbn/logging'; import { Config, ConfigPath, Env } from '.'; -import { Logger, LoggerFactory } from '../logging'; import { hasConfigPathIntersection } from './config'; -import { RawConfigurationProvider } from './raw_config_service'; +import { RawConfigurationProvider } from './raw/raw_config_service'; import { applyDeprecations, ConfigDeprecationWithContext, ConfigDeprecationProvider, configDeprecationFactory, } from './deprecation'; -import { LegacyObjectToConfigAdapter } from '../legacy/config'; +import { LegacyObjectToConfigAdapter } from './legacy'; /** @internal */ export type IConfigService = PublicMethodsOf; diff --git a/src/core/server/config/deprecation/apply_deprecations.test.ts b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts similarity index 100% rename from src/core/server/config/deprecation/apply_deprecations.test.ts rename to packages/kbn-config/src/deprecation/apply_deprecations.test.ts diff --git a/src/core/server/config/deprecation/apply_deprecations.ts b/packages/kbn-config/src/deprecation/apply_deprecations.ts similarity index 100% rename from src/core/server/config/deprecation/apply_deprecations.ts rename to packages/kbn-config/src/deprecation/apply_deprecations.ts diff --git a/src/core/server/config/deprecation/deprecation_factory.test.ts b/packages/kbn-config/src/deprecation/deprecation_factory.test.ts similarity index 100% rename from src/core/server/config/deprecation/deprecation_factory.test.ts rename to packages/kbn-config/src/deprecation/deprecation_factory.test.ts diff --git a/src/core/server/config/deprecation/deprecation_factory.ts b/packages/kbn-config/src/deprecation/deprecation_factory.ts similarity index 98% rename from src/core/server/config/deprecation/deprecation_factory.ts rename to packages/kbn-config/src/deprecation/deprecation_factory.ts index cbc9984924c5d..0598347d2cffc 100644 --- a/src/core/server/config/deprecation/deprecation_factory.ts +++ b/packages/kbn-config/src/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/packages/kbn-config/src/deprecation/index.ts b/packages/kbn-config/src/deprecation/index.ts new file mode 100644 index 0000000000000..504dbfeeb001a --- /dev/null +++ b/packages/kbn-config/src/deprecation/index.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { + ConfigDeprecation, + ConfigDeprecationWithContext, + ConfigDeprecationLogger, + ConfigDeprecationFactory, + ConfigDeprecationProvider, +} from './types'; +export { configDeprecationFactory } from './deprecation_factory'; +export { applyDeprecations } from './apply_deprecations'; diff --git a/src/core/server/config/deprecation/types.ts b/packages/kbn-config/src/deprecation/types.ts similarity index 100% rename from src/core/server/config/deprecation/types.ts rename to packages/kbn-config/src/deprecation/types.ts diff --git a/src/core/server/config/env.test.mocks.ts b/packages/kbn-config/src/env.test.mocks.ts similarity index 81% rename from src/core/server/config/env.test.mocks.ts rename to packages/kbn-config/src/env.test.mocks.ts index d35afdf0b66e5..3976b1c2016fb 100644 --- a/src/core/server/config/env.test.mocks.ts +++ b/packages/kbn-config/src/env.test.mocks.ts @@ -17,7 +17,10 @@ * under the License. */ -jest.mock('path', () => ({ +const realPath = jest.requireActual('path'); + +jest.doMock('path', () => ({ + ...realPath, resolve(...pathSegments: string[]) { return pathSegments.join('/'); }, @@ -26,5 +29,10 @@ jest.mock('path', () => ({ }, })); -export const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] }); -jest.mock('../../../../package.json', () => mockPackage); +export const mockPackage = { + raw: {}, +}; + +jest.doMock('load-json-file', () => ({ + sync: () => mockPackage.raw, +})); diff --git a/src/core/server/config/env.test.ts b/packages/kbn-config/src/env.test.ts similarity index 89% rename from src/core/server/config/env.test.ts rename to packages/kbn-config/src/env.test.ts index 0fffcc44781d9..f3d51a021246e 100644 --- a/src/core/server/config/env.test.ts +++ b/packages/kbn-config/src/env.test.ts @@ -19,9 +19,24 @@ import { mockPackage } from './env.test.mocks'; -import { Env } from '.'; +import { Env, RawPackageInfo } from './env'; import { getEnvOptions } from './__mocks__/env'; +const REPO_ROOT = '/test/kibanaRoot'; + +const packageInfos: RawPackageInfo = { + branch: 'master', + version: '8.0.0', + build: { + number: 42, + sha: 'one', + }, +}; + +beforeEach(() => { + mockPackage.raw = {}; +}); + test('correctly creates default environment in dev mode.', () => { mockPackage.raw = { branch: 'some-branch', @@ -29,6 +44,7 @@ test('correctly creates default environment in dev mode.', () => { }; const defaultEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ configs: ['/test/cwd/config/kibana.yml'], isDevClusterMaster: true, @@ -50,6 +66,7 @@ test('correctly creates default environment in prod distributable mode.', () => }; const defaultEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false }, configs: ['/some/other/path/some-kibana.yml'], @@ -71,6 +88,7 @@ test('correctly creates default environment in prod non-distributable mode.', () }; const defaultEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false }, configs: ['/some/other/path/some-kibana.yml'], @@ -92,6 +110,7 @@ test('correctly creates default environment if `--env.name` is supplied.', () => }; const defaultDevEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { envName: 'development' }, configs: ['/some/other/path/some-kibana.yml'], @@ -99,6 +118,7 @@ test('correctly creates default environment if `--env.name` is supplied.', () => ); const defaultProdEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false, envName: 'production' }, configs: ['/some/other/path/some-kibana.yml'], @@ -110,18 +130,17 @@ test('correctly creates default environment if `--env.name` is supplied.', () => }); test('correctly creates environment with constructor.', () => { - mockPackage.raw = { - branch: 'feature-v1', - version: 'v1', - build: { - distributable: true, - number: 100, - sha: 'feature-v1-build-sha', - }, - }; - const env = new Env( '/some/home/dir', + { + branch: 'feature-v1', + version: 'v1', + build: { + distributable: true, + number: 100, + sha: 'feature-v1-build-sha', + }, + }, getEnvOptions({ cliArgs: { dev: false }, configs: ['/some/other/path/some-kibana.yml'], @@ -134,6 +153,7 @@ test('correctly creates environment with constructor.', () => { test('pluginSearchPaths contains x-pack plugins path if --oss flag is false', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { oss: false }, }) @@ -145,6 +165,7 @@ test('pluginSearchPaths contains x-pack plugins path if --oss flag is false', () test('pluginSearchPaths does not contains x-pack plugins path if --oss flag is true', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { oss: true }, }) @@ -156,6 +177,7 @@ test('pluginSearchPaths does not contains x-pack plugins path if --oss flag is t test('pluginSearchPaths contains examples plugins path if --run-examples flag is true', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { runExamples: true }, }) @@ -167,6 +189,7 @@ test('pluginSearchPaths contains examples plugins path if --run-examples flag is test('pluginSearchPaths contains x-pack/examples plugins path if --run-examples flag is true', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { runExamples: true }, }) @@ -178,6 +201,7 @@ test('pluginSearchPaths contains x-pack/examples plugins path if --run-examples test('pluginSearchPaths does not contains examples plugins path if --run-examples flag is false', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { runExamples: false }, }) @@ -189,6 +213,7 @@ test('pluginSearchPaths does not contains examples plugins path if --run-example test('pluginSearchPaths does not contains x-pack/examples plugins path if --run-examples flag is false', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { runExamples: false }, }) diff --git a/src/core/server/config/env.ts b/packages/kbn-config/src/env.ts similarity index 87% rename from src/core/server/config/env.ts rename to packages/kbn-config/src/env.ts index d8b056996315b..250c7b72d47a9 100644 --- a/src/core/server/config/env.ts +++ b/packages/kbn-config/src/env.ts @@ -17,13 +17,10 @@ * under the License. */ -import { resolve, dirname } from 'path'; +import { resolve, join } from 'path'; +import loadJsonFile from 'load-json-file'; import { PackageInfo, EnvironmentMode } from './types'; -// `require` is necessary for this to work inside x-pack code as well -// eslint-disable-next-line @typescript-eslint/no-var-requires -const pkg = require('../../../../package.json'); - /** @internal */ export interface EnvOptions { configs: string[]; @@ -50,13 +47,26 @@ export interface CliArgs { dist: boolean; } +/** @internal */ +export interface RawPackageInfo { + branch: string; + version: string; + build: { + distributable?: boolean; + number: number; + sha: string; + }; +} + export class Env { /** * @internal */ - public static createDefault(options: EnvOptions): Env { - const repoRoot = dirname(require.resolve('../../../../package.json')); - return new Env(repoRoot, options); + public static createDefault(repoRoot: string, options: EnvOptions, pkg?: RawPackageInfo): Env { + if (!pkg) { + pkg = loadJsonFile.sync(join(repoRoot, 'package.json')) as RawPackageInfo; + } + return new Env(repoRoot, pkg, options); } /** @internal */ @@ -99,7 +109,7 @@ export class Env { /** * @internal */ - constructor(public readonly homeDir: string, options: EnvOptions) { + constructor(public readonly homeDir: string, pkg: RawPackageInfo, options: EnvOptions) { this.configDir = resolve(this.homeDir, 'config'); this.binDir = resolve(this.homeDir, 'bin'); this.logDir = resolve(this.homeDir, 'log'); diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/search_requests/search_request.js b/packages/kbn-config/src/index.ts similarity index 52% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/search_requests/search_request.js rename to packages/kbn-config/src/index.ts index e6e3bcb527286..f02514a92e606 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/search_requests/search_request.js +++ b/packages/kbn-config/src/index.ts @@ -16,22 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -import { AbstractSearchRequest } from './abstract_request'; -import { MultiSearchRequest } from './multi_search_request'; -import { SingleSearchRequest } from './single_search_request'; +export { + applyDeprecations, + ConfigDeprecation, + ConfigDeprecationFactory, + configDeprecationFactory, + ConfigDeprecationLogger, + ConfigDeprecationProvider, + ConfigDeprecationWithContext, +} from './deprecation'; -export class SearchRequest extends AbstractSearchRequest { - getSearchRequestType(searches) { - const isMultiSearch = Array.isArray(searches) && searches.length > 1; - const SearchRequest = isMultiSearch ? MultiSearchRequest : SingleSearchRequest; +export { RawConfigurationProvider, RawConfigService, getConfigFromFiles } from './raw'; - return new SearchRequest(this.req, this.callWithRequest); - } - - async search(options) { - const concreteSearchRequest = this.getSearchRequestType(options); - - return concreteSearchRequest.search(options); - } -} +export { ConfigService, IConfigService } from './config_service'; +export { Config, ConfigPath, isConfigPath, hasConfigPathIntersection } from './config'; +export { ObjectToConfigAdapter } from './object_to_config_adapter'; +export { CliArgs, Env, RawPackageInfo } from './env'; +export { EnvironmentMode, PackageInfo } from './types'; +export { LegacyObjectToConfigAdapter, LegacyLoggingConfig } from './legacy'; diff --git a/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap b/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap similarity index 100% rename from src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap rename to packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap diff --git a/src/legacy/server/status/routes/index.js b/packages/kbn-config/src/legacy/index.ts similarity index 87% rename from src/legacy/server/status/routes/index.js rename to packages/kbn-config/src/legacy/index.ts index 12736a76d4915..cad6b754e956c 100644 --- a/src/legacy/server/status/routes/index.js +++ b/packages/kbn-config/src/legacy/index.ts @@ -17,5 +17,7 @@ * under the License. */ -export { registerStatusApi } from './api/register_status'; -export { registerStatsApi } from './api/register_stats'; +export { + LegacyObjectToConfigAdapter, + LegacyLoggingConfig, +} from './legacy_object_to_config_adapter'; diff --git a/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts similarity index 100% rename from src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts rename to packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts diff --git a/src/core/server/legacy/config/legacy_object_to_config_adapter.ts b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.ts similarity index 92% rename from src/core/server/legacy/config/legacy_object_to_config_adapter.ts rename to packages/kbn-config/src/legacy/legacy_object_to_config_adapter.ts index 3e496648c3af9..e8fca8735a6d9 100644 --- a/src/core/server/legacy/config/legacy_object_to_config_adapter.ts +++ b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.ts @@ -17,10 +17,17 @@ * under the License. */ -import { ConfigPath } from '../../config'; -import { ObjectToConfigAdapter } from '../../config/object_to_config_adapter'; -import { LoggingConfigType } from '../../logging/logging_config'; -import { LegacyVars } from '../types'; +import { ConfigPath } from '../config'; +import { ObjectToConfigAdapter } from '../object_to_config_adapter'; + +// TODO: fix once core schemas are moved to this package +type LoggingConfigType = any; + +/** + * @internal + * @deprecated + */ +export type LegacyVars = Record; /** * Represents logging config supported by the legacy platform. diff --git a/src/legacy/server/status/lib/case_conversion.test.ts b/packages/kbn-config/src/mocks.ts similarity index 57% rename from src/legacy/server/status/lib/case_conversion.test.ts rename to packages/kbn-config/src/mocks.ts index a231ee0ba4b0f..5ca5de3a328a2 100644 --- a/src/legacy/server/status/lib/case_conversion.test.ts +++ b/packages/kbn-config/src/mocks.ts @@ -17,20 +17,12 @@ * under the License. */ -import { keysToSnakeCaseShallow } from './case_conversion'; - -describe('keysToSnakeCaseShallow', () => { - test("should convert all of an object's keys to snake case", () => { - const data = { - camelCase: 'camel_case', - 'kebab-case': 'kebab_case', - snake_case: 'snake_case', - }; - - const result = keysToSnakeCaseShallow(data); - - expect(result.camel_case).toBe('camel_case'); - expect(result.kebab_case).toBe('kebab_case'); - expect(result.snake_case).toBe('snake_case'); - }); -}); +// these CANT be exported by the main entrypoint, as it cause ts check failures +// in `src/test` and `src/xpack/test` projects due to definition conflicts between +// mocha and jest declaring the same globals such as `it` or `beforeAll`, as the test +// files imports types from `core` that is importing the main `@kbn/config` entrypoint. +// For now, these should be imported using `import {} from '@kbn/config/target/mocks'` +export { configMock } from './config.mock'; +export { configServiceMock } from './config_service.mock'; +export { rawConfigServiceMock } from './raw/raw_config_service.mock'; +export { getEnvOptions } from './__mocks__/env'; diff --git a/src/core/server/config/object_to_config_adapter.test.ts b/packages/kbn-config/src/object_to_config_adapter.test.ts similarity index 100% rename from src/core/server/config/object_to_config_adapter.test.ts rename to packages/kbn-config/src/object_to_config_adapter.test.ts diff --git a/src/core/server/config/object_to_config_adapter.ts b/packages/kbn-config/src/object_to_config_adapter.ts similarity index 96% rename from src/core/server/config/object_to_config_adapter.ts rename to packages/kbn-config/src/object_to_config_adapter.ts index 50b31722dceeb..c4d6ac02ccf05 100644 --- a/src/core/server/config/object_to_config_adapter.ts +++ b/packages/kbn-config/src/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/__snapshots__/read_config.test.ts.snap b/packages/kbn-config/src/raw/__snapshots__/read_config.test.ts.snap similarity index 100% rename from src/core/server/config/__snapshots__/read_config.test.ts.snap rename to packages/kbn-config/src/raw/__snapshots__/read_config.test.ts.snap diff --git a/src/core/server/config/ensure_deep_object.test.ts b/packages/kbn-config/src/raw/ensure_deep_object.test.ts similarity index 100% rename from src/core/server/config/ensure_deep_object.test.ts rename to packages/kbn-config/src/raw/ensure_deep_object.test.ts diff --git a/src/core/server/config/ensure_deep_object.ts b/packages/kbn-config/src/raw/ensure_deep_object.ts similarity index 100% rename from src/core/server/config/ensure_deep_object.ts rename to packages/kbn-config/src/raw/ensure_deep_object.ts diff --git a/src/legacy/server/status/lib/case_conversion.ts b/packages/kbn-config/src/raw/index.ts similarity index 81% rename from src/legacy/server/status/lib/case_conversion.ts rename to packages/kbn-config/src/raw/index.ts index a3ae15028daeb..e32920fe64d67 100644 --- a/src/legacy/server/status/lib/case_conversion.ts +++ b/packages/kbn-config/src/raw/index.ts @@ -17,8 +17,5 @@ * under the License. */ -import { mapKeys, snakeCase } from 'lodash'; - -export function keysToSnakeCaseShallow(object: Record) { - return mapKeys(object, (value, key) => snakeCase(key)); -} +export { RawConfigService, RawConfigurationProvider } from './raw_config_service'; +export { getConfigFromFiles } from './read_config'; diff --git a/src/core/server/config/raw_config_service.mock.ts b/packages/kbn-config/src/raw/raw_config_service.mock.ts similarity index 95% rename from src/core/server/config/raw_config_service.mock.ts rename to packages/kbn-config/src/raw/raw_config_service.mock.ts index fdcb17395aaad..53ea797dac9e5 100644 --- a/src/core/server/config/raw_config_service.mock.ts +++ b/packages/kbn-config/src/raw/raw_config_service.mock.ts @@ -17,6 +17,7 @@ * 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/config/raw_config_service.test.mocks.ts b/packages/kbn-config/src/raw/raw_config_service.test.mocks.ts similarity index 100% rename from src/core/server/config/raw_config_service.test.mocks.ts rename to packages/kbn-config/src/raw/raw_config_service.test.mocks.ts diff --git a/src/core/server/config/raw_config_service.test.ts b/packages/kbn-config/src/raw/raw_config_service.test.ts similarity index 98% rename from src/core/server/config/raw_config_service.test.ts rename to packages/kbn-config/src/raw/raw_config_service.test.ts index 8846ea3847f79..444d67985250b 100644 --- a/src/core/server/config/raw_config_service.test.ts +++ b/packages/kbn-config/src/raw/raw_config_service.test.ts @@ -20,7 +20,7 @@ import { mockGetConfigFromFiles } from './raw_config_service.test.mocks'; import { first } from 'rxjs/operators'; -import { RawConfigService } from '.'; +import { RawConfigService } from './raw_config_service'; const configFile = '/config/kibana.yml'; const anotherConfigFile = '/config/kibana.dev.yml'; diff --git a/src/core/server/config/raw_config_service.ts b/packages/kbn-config/src/raw/raw_config_service.ts similarity index 100% rename from src/core/server/config/raw_config_service.ts rename to packages/kbn-config/src/raw/raw_config_service.ts diff --git a/src/core/server/config/read_config.test.ts b/packages/kbn-config/src/raw/read_config.test.ts similarity index 96% rename from src/core/server/config/read_config.test.ts rename to packages/kbn-config/src/raw/read_config.test.ts index 46b75f28eb987..89b73c5d4e26a 100644 --- a/src/core/server/config/read_config.test.ts +++ b/packages/kbn-config/src/raw/read_config.test.ts @@ -20,7 +20,7 @@ import { relative, resolve } from 'path'; import { getConfigFromFiles } from './read_config'; -const fixtureFile = (name: string) => `${__dirname}/__fixtures__/${name}`; +const fixtureFile = (name: string) => resolve(`${__dirname}/../../__fixtures__/${name}`); test('reads single yaml from file system and parses to json', () => { const config = getConfigFromFiles([fixtureFile('config.yml')]); diff --git a/src/core/server/config/read_config.ts b/packages/kbn-config/src/raw/read_config.ts similarity index 100% rename from src/core/server/config/read_config.ts rename to packages/kbn-config/src/raw/read_config.ts diff --git a/src/core/server/config/types.ts b/packages/kbn-config/src/types.ts similarity index 100% rename from src/core/server/config/types.ts rename to packages/kbn-config/src/types.ts diff --git a/packages/kbn-config/tsconfig.json b/packages/kbn-config/tsconfig.json new file mode 100644 index 0000000000000..ba00ddfa6adb6 --- /dev/null +++ b/packages/kbn-config/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "outDir": "./target", + "stripInternal": false, + "declarationMap": true, + "types": ["jest", "node"] + }, + "include": ["./src/**/*.ts"], + "exclude": ["target"] +} diff --git a/packages/kbn-config/yarn.lock b/packages/kbn-config/yarn.lock new file mode 120000 index 0000000000000..3f82ebc9cdbae --- /dev/null +++ b/packages/kbn-config/yarn.lock @@ -0,0 +1 @@ +../../yarn.lock \ No newline at end of file diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index 4f6f995f38f31..a3fe8178822aa 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@babel/core": "^7.11.1", + "@kbn/utils": "1.0.0", "axios": "^0.19.0", "chalk": "^4.1.0", "cheerio": "0.22.0", diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts index 2871fe2ffcf4a..8217999b01128 100644 --- a/packages/kbn-dev-utils/src/index.ts +++ b/packages/kbn-dev-utils/src/index.ts @@ -17,6 +17,7 @@ * under the License. */ +export { REPO_ROOT } from '@kbn/utils'; export { withProcRunner, ProcRunner } from './proc_runner'; export * from './tooling_log'; export * from './serializers'; @@ -33,7 +34,6 @@ export { KBN_P12_PATH, KBN_P12_PASSWORD, } from './certs'; -export { REPO_ROOT } from './repo_root'; export { KbnClient } from './kbn_client'; export * from './run'; export * from './axios'; diff --git a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts index 783d584656b17..5d92ddb600aa9 100644 --- a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts +++ b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts @@ -22,8 +22,8 @@ import Fs from 'fs'; import MarkdownIt from 'markdown-it'; import cheerio from 'cheerio'; +import { REPO_ROOT } from '@kbn/utils'; -import { REPO_ROOT } from '../repo_root'; import { simpleKibanaPlatformPluginDiscovery } from '../simple_kibana_platform_plugin_discovery'; import { extractAsciidocInfo } from './extract_asciidoc_info'; diff --git a/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts b/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts index 43dac1cb7d418..e1a1323553113 100644 --- a/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts +++ b/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts @@ -20,8 +20,8 @@ import Path from 'path'; import normalizePath from 'normalize-path'; +import { REPO_ROOT } from '@kbn/utils'; -import { REPO_ROOT } from '../repo_root'; import { Plugins } from './discover_plugins'; function* printPlugins(plugins: Plugins, includes: string[]) { diff --git a/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts b/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts index 553eb1dd8afa0..613f9c9c26411 100644 --- a/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts +++ b/packages/kbn-dev-utils/src/plugin_list/run_plugin_list_cli.ts @@ -19,10 +19,9 @@ import Path from 'path'; import Fs from 'fs'; +import { REPO_ROOT } from '@kbn/utils'; import { run } from '../run'; -import { REPO_ROOT } from '../repo_root'; - import { discoverPlugins } from './discover_plugins'; import { generatePluginList } from './generate_plugin_list'; diff --git a/packages/kbn-dev-utils/src/precommit_hook/cli.ts b/packages/kbn-dev-utils/src/precommit_hook/cli.ts index a83e8c2b193d9..28347f379150f 100644 --- a/packages/kbn-dev-utils/src/precommit_hook/cli.ts +++ b/packages/kbn-dev-utils/src/precommit_hook/cli.ts @@ -20,9 +20,9 @@ import Path from 'path'; import { chmod, writeFile } from 'fs'; import { promisify } from 'util'; +import { REPO_ROOT } from '@kbn/utils'; import { run } from '../run'; -import { REPO_ROOT } from '../repo_root'; import { SCRIPT_SOURCE } from './script_source'; import { getGitDir } from './get_git_dir'; diff --git a/packages/kbn-dev-utils/src/precommit_hook/get_git_dir.ts b/packages/kbn-dev-utils/src/precommit_hook/get_git_dir.ts index 5ca7d67d0d4ea..f75c86f510095 100644 --- a/packages/kbn-dev-utils/src/precommit_hook/get_git_dir.ts +++ b/packages/kbn-dev-utils/src/precommit_hook/get_git_dir.ts @@ -19,7 +19,7 @@ import execa from 'execa'; -import { REPO_ROOT } from '../repo_root'; +import { REPO_ROOT } from '@kbn/utils'; // Retrieves the correct location for the .git dir for // every git setup (including git worktree) diff --git a/packages/kbn-dev-utils/src/serializers/absolute_path_serializer.ts b/packages/kbn-dev-utils/src/serializers/absolute_path_serializer.ts index 4008cf852c3a8..cc6b8334d76cf 100644 --- a/packages/kbn-dev-utils/src/serializers/absolute_path_serializer.ts +++ b/packages/kbn-dev-utils/src/serializers/absolute_path_serializer.ts @@ -17,7 +17,7 @@ * under the License. */ -import { REPO_ROOT } from '../repo_root'; +import { REPO_ROOT } from '@kbn/utils'; export function createAbsolutePathSerializer( rootPath: string = REPO_ROOT, diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index dabf11fdd0b66..52ef3fe05e751 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -14,7 +14,7 @@ "execa": "^4.0.2", "getopts": "^2.2.4", "glob": "^7.1.2", - "node-fetch": "^2.6.0", + "node-fetch": "^2.6.1", "simple-git": "^1.91.0", "tar-fs": "^2.1.0", "tree-kill": "^1.2.2", diff --git a/packages/kbn-logging/README.md b/packages/kbn-logging/README.md new file mode 100644 index 0000000000000..8bde73722deb0 --- /dev/null +++ b/packages/kbn-logging/README.md @@ -0,0 +1,61 @@ +# kbn-logging + +Base types for the kibana platform logging system. + +Note that this package currently only contains logging types. The only concrete implementation +is still in `core` for now. + +- [Loggers, Appenders and Layouts](#loggers-appenders-and-layouts) +- [Logger hierarchy](#logger-hierarchy) +- [Log level](#log-level) +- [Layouts](#layouts) + +The way logging works in Kibana is inspired by `log4j 2` logging framework used by [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html#logging). +The main idea is to have consistent logging behaviour (configuration, log format etc.) across the entire Elastic Stack +where possible. + +## Loggers, Appenders and Layouts + +Kibana logging system has three main components: _loggers_, _appenders_ and _layouts_. These components allow us to log +messages according to message type and level, and to control how these messages are formatted and where the final logs +will be displayed or stored. + +__Loggers__ define what logging settings should be applied at the particular context. + +__Appenders__ define where log messages are displayed (eg. stdout or console) and stored (eg. file on the disk). + +__Layouts__ define how log messages are formatted and what type of information they include. + + +## Logger hierarchy + +Every logger has its unique name or context that follows hierarchical naming rule. The logger is considered to be an +ancestor of another logger if its name followed by a `.` is a prefix of the descendant logger name. For example logger +with `a.b` context is an ancestor of logger with `a.b.c` context. All top-level loggers are descendants of special +logger with `root` context that resides at the top of the logger hierarchy. This logger always exists and +fully configured. + +Developer can configure _log level_ and _appenders_ that should be used within particular context. If logger configuration +specifies only _log level_ then _appenders_ configuration will be inherited from the ancestor logger. + +__Note:__ in the current implementation log messages are only forwarded to appenders configured for a particular logger +context or to appenders of the closest ancestor if current logger doesn't have any appenders configured. That means that +we __don't support__ so called _appender additivity_ when log messages are forwarded to _every_ distinct appender within +ancestor chain including `root`. + +## Log level + +Currently we support the following log levels: _all_, _fatal_, _error_, _warn_, _info_, _debug_, _trace_, _off_. +Levels are ordered, so _all_ > _fatal_ > _error_ > _warn_ > _info_ > _debug_ > _trace_ > _off_. +A log record is being logged by the logger if its level is higher than or equal to the level of its logger. Otherwise, +the log record is ignored. + +The _all_ and _off_ levels can be used only in configuration and are just handy shortcuts that allow developer to log every +log record or disable logging entirely for the specific context. + +## Layouts + +Every appender should know exactly how to format log messages before they are written to the console or file on the disk. +This behaviour is controlled by the layouts and configured through `appender.layout` configuration property for every +custom appender. Currently we don't define any default layout for the custom appenders, so one should always make the choice +explicitly. diff --git a/packages/kbn-logging/package.json b/packages/kbn-logging/package.json new file mode 100644 index 0000000000000..922d562733622 --- /dev/null +++ b/packages/kbn-logging/package.json @@ -0,0 +1,18 @@ +{ + "name": "@kbn/logging", + "version": "1.0.0", + "private": true, + "license": "Apache-2.0", + "main": "./target/index.js", + "scripts": { + "build": "tsc", + "kbn:bootstrap": "yarn build", + "kbn:watch": "yarn build --watch" + }, + "dependencies": { + "@kbn/std": "1.0.0" + }, + "devDependencies": { + "typescript": "4.0.2" + } +} diff --git a/src/legacy/server/status/samples.js b/packages/kbn-logging/src/appenders.ts similarity index 55% rename from src/legacy/server/status/samples.js rename to packages/kbn-logging/src/appenders.ts index 9c41e29945a77..346d3d6dd1068 100644 --- a/src/legacy/server/status/samples.js +++ b/packages/kbn-logging/src/appenders.ts @@ -17,29 +17,23 @@ * under the License. */ -import _ from 'lodash'; +import { LogRecord } from './log_record'; -function Samples(max) { - this.vals = {}; - this.max = max || Infinity; - this.length = 0; +/** + * Entity that can append `LogRecord` instances to file, stdout, memory or whatever + * is implemented internally. It's supposed to be used by `Logger`. + * @internal + */ +export interface Appender { + append(record: LogRecord): void; } -Samples.prototype.add = function (sample) { - const vals = this.vals; - const length = (this.length = Math.min(this.length + 1, this.max)); - - _.forOwn(sample, function (val, name) { - if (val == null) val = null; - - if (!vals[name]) vals[name] = new Array(length); - vals[name].unshift([Date.now(), val]); - vals[name].length = length; - }); -}; - -Samples.prototype.toJSON = function () { - return this.vals; -}; - -export default Samples; +/** + * This interface should be additionally implemented by the `Appender`'s if they are supposed + * to be properly disposed. It's intentionally separated from `Appender` interface so that `Logger` + * that interacts with `Appender` doesn't have control over appender lifetime. + * @internal + */ +export interface DisposableAppender extends Appender { + dispose: () => void; +} diff --git a/packages/kbn-logging/src/index.ts b/packages/kbn-logging/src/index.ts new file mode 100644 index 0000000000000..d06218ac2eeb5 --- /dev/null +++ b/packages/kbn-logging/src/index.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { LogLevel, LogLevelId } from './log_level'; +export { LogRecord } from './log_record'; +export { Logger, LogMeta } from './logger'; +export { LoggerFactory } from './logger_factory'; +export { Layout } from './layout'; +export { Appender, DisposableAppender } from './appenders'; diff --git a/src/plugins/discover/public/application/components/skip_bottom_button/skip_bottom_button_directive.ts b/packages/kbn-logging/src/layout.ts similarity index 78% rename from src/plugins/discover/public/application/components/skip_bottom_button/skip_bottom_button_directive.ts rename to packages/kbn-logging/src/layout.ts index 27f17b25fd447..75556eab88bb6 100644 --- a/src/plugins/discover/public/application/components/skip_bottom_button/skip_bottom_button_directive.ts +++ b/packages/kbn-logging/src/layout.ts @@ -16,8 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { SkipBottomButton } from './skip_bottom_button'; -export function createSkipBottomButtonDirective(reactDirective: any) { - return reactDirective(SkipBottomButton, [['onClick', { watchDepth: 'reference' }]]); +import { LogRecord } from './log_record'; + +/** + * Entity that can format `LogRecord` instance into a string. + * @internal + */ +export interface Layout { + format(record: LogRecord): string; } diff --git a/src/core/server/logging/log_level.test.ts b/packages/kbn-logging/src/log_level.test.ts similarity index 100% rename from src/core/server/logging/log_level.test.ts rename to packages/kbn-logging/src/log_level.test.ts diff --git a/src/core/server/logging/log_level.ts b/packages/kbn-logging/src/log_level.ts similarity index 98% rename from src/core/server/logging/log_level.ts rename to packages/kbn-logging/src/log_level.ts index 577239ddae8e5..165e56e632d6d 100644 --- a/src/core/server/logging/log_level.ts +++ b/packages/kbn-logging/src/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/log_record.ts b/packages/kbn-logging/src/log_record.ts similarity index 100% rename from src/core/server/logging/log_record.ts rename to packages/kbn-logging/src/log_record.ts diff --git a/packages/kbn-logging/src/logger.ts b/packages/kbn-logging/src/logger.ts new file mode 100644 index 0000000000000..50e002a87fc52 --- /dev/null +++ b/packages/kbn-logging/src/logger.ts @@ -0,0 +1,96 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { LogRecord } from './log_record'; + +/** + * Contextual metadata + * + * @public + */ +export interface LogMeta { + [key: string]: any; +} + +/** + * Logger exposes all the necessary methods to log any type of information and + * this is the interface used by the logging consumers including plugins. + * + * @public + */ +export interface Logger { + /** + * Log messages at the most detailed log level + * + * @param message - The log message + * @param meta - + */ + trace(message: string, meta?: LogMeta): void; + + /** + * Log messages useful for debugging and interactive investigation + * @param message - The log message + * @param meta - + */ + debug(message: string, meta?: LogMeta): void; + + /** + * Logs messages related to general application flow + * @param message - The log message + * @param meta - + */ + info(message: string, meta?: LogMeta): void; + + /** + * Logs abnormal or unexpected errors or messages + * @param errorOrMessage - An Error object or message string to log + * @param meta - + */ + warn(errorOrMessage: string | Error, meta?: LogMeta): void; + + /** + * Logs abnormal or unexpected errors or messages that caused a failure in the application flow + * + * @param errorOrMessage - An Error object or message string to log + * @param meta - + */ + error(errorOrMessage: string | Error, meta?: LogMeta): void; + + /** + * Logs abnormal or unexpected errors or messages that caused an unrecoverable failure + * + * @param errorOrMessage - An Error object or message string to log + * @param meta - + */ + fatal(errorOrMessage: string | Error, meta?: LogMeta): void; + + /** @internal */ + log(record: LogRecord): void; + + /** + * Returns a new {@link Logger} instance extending the current logger context. + * + * @example + * ```typescript + * const logger = loggerFactory.get('plugin', 'service'); // 'plugin.service' context + * const subLogger = logger.get('feature'); // 'plugin.service.feature' context + * ``` + */ + get(...childContextPaths: string[]): Logger; +} diff --git a/src/core/server/logging/logger_factory.ts b/packages/kbn-logging/src/logger_factory.ts similarity index 100% rename from src/core/server/logging/logger_factory.ts rename to packages/kbn-logging/src/logger_factory.ts diff --git a/src/legacy/server/status/lib/index.js b/packages/kbn-logging/src/mocks/index.ts similarity index 92% rename from src/legacy/server/status/lib/index.js rename to packages/kbn-logging/src/mocks/index.ts index 93db8b2d22561..c7be3a1b8ce8c 100644 --- a/src/legacy/server/status/lib/index.js +++ b/packages/kbn-logging/src/mocks/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { getKibanaInfoForStats } from './get_kibana_info_for_stats'; +export { MockedLogger, loggerMock } from './logger.mock'; diff --git a/packages/kbn-logging/src/mocks/logger.mock.ts b/packages/kbn-logging/src/mocks/logger.mock.ts new file mode 100644 index 0000000000000..1a4c7a2dadf68 --- /dev/null +++ b/packages/kbn-logging/src/mocks/logger.mock.ts @@ -0,0 +1,70 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Logger } from '../logger'; + +export type MockedLogger = jest.Mocked & { context: string[] }; + +const createLoggerMock = (context: string[] = []) => { + const mockLog: MockedLogger = { + context, + debug: jest.fn(), + error: jest.fn(), + fatal: jest.fn(), + info: jest.fn(), + log: jest.fn(), + trace: jest.fn(), + warn: jest.fn(), + get: jest.fn(), + }; + mockLog.get.mockImplementation((...ctx) => ({ + ctx, + ...mockLog, + })); + + return mockLog; +}; + +const clearLoggerMock = (logger: MockedLogger) => { + logger.debug.mockClear(); + logger.info.mockClear(); + logger.warn.mockClear(); + logger.error.mockClear(); + logger.trace.mockClear(); + logger.fatal.mockClear(); + logger.log.mockClear(); +}; + +const collectLoggerMock = (logger: MockedLogger) => { + return { + debug: logger.debug.mock.calls, + error: logger.error.mock.calls, + fatal: logger.fatal.mock.calls, + info: logger.info.mock.calls, + log: logger.log.mock.calls, + trace: logger.trace.mock.calls, + warn: logger.warn.mock.calls, + }; +}; + +export const loggerMock = { + create: createLoggerMock, + clear: clearLoggerMock, + collect: collectLoggerMock, +}; diff --git a/packages/kbn-logging/tsconfig.json b/packages/kbn-logging/tsconfig.json new file mode 100644 index 0000000000000..c55c05de30a52 --- /dev/null +++ b/packages/kbn-logging/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target", + "stripInternal": false, + "declaration": true, + "declarationMap": true, + "types": ["jest", "node"] + }, + "include": ["./src/**/*.ts"] +} diff --git a/packages/kbn-logging/yarn.lock b/packages/kbn-logging/yarn.lock new file mode 120000 index 0000000000000..3f82ebc9cdbae --- /dev/null +++ b/packages/kbn-logging/yarn.lock @@ -0,0 +1 @@ +../../yarn.lock \ No newline at end of file diff --git a/packages/kbn-optimizer/README.md b/packages/kbn-optimizer/README.md index 13be836f0ea88..a666907f02678 100644 --- a/packages/kbn-optimizer/README.md +++ b/packages/kbn-optimizer/README.md @@ -69,7 +69,8 @@ To run the optimizer from code, you can import the [`OptimizerConfig`][Optimizer Example: ```ts import { runOptimizer, OptimizerConfig, logOptimizerState } from '@kbn/optimizer'; -import { REPO_ROOT, ToolingLog } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { ToolingLog } from '@kbn/dev-utils'; const log = new ToolingLog({ level: 'verbose', diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts index 542dc7255f22f..dcfb56be66efd 100644 --- a/packages/kbn-optimizer/src/cli.ts +++ b/packages/kbn-optimizer/src/cli.ts @@ -21,7 +21,8 @@ import 'source-map-support/register'; import Path from 'path'; -import { run, REPO_ROOT, createFlagError, CiStatsReporter } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { run, createFlagError, CiStatsReporter } from '@kbn/dev-utils'; import { logOptimizerState } from './log_optimizer_state'; import { OptimizerConfig } from './optimizer'; diff --git a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts index 12f037f5080fc..de3838eb92975 100644 --- a/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts +++ b/packages/kbn-optimizer/src/integration_tests/basic_optimization.test.ts @@ -25,7 +25,8 @@ import { inspect } from 'util'; import cpy from 'cpy'; import del from 'del'; import { toArray, tap, filter } from 'rxjs/operators'; -import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { ToolingLog } from '@kbn/dev-utils'; import { runOptimizer, OptimizerConfig, OptimizerUpdate, logOptimizerState } from '@kbn/optimizer'; const TMP_DIR = Path.resolve(__dirname, '../__fixtures__/__tmp__'); diff --git a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts b/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts index 47d01347a8f7d..1b4c9b117c4c3 100644 --- a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts +++ b/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts @@ -20,7 +20,8 @@ import Path from 'path'; import jestDiff from 'jest-diff'; -import { REPO_ROOT, createAbsolutePathSerializer } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { createAbsolutePathSerializer } from '@kbn/dev-utils'; import { reformatJestDiff, getOptimizerCacheKey, diffCacheKey } from './cache_keys'; import { OptimizerConfig } from './optimizer_config'; diff --git a/packages/kbn-optimizer/src/optimizer/cache_keys.ts b/packages/kbn-optimizer/src/optimizer/cache_keys.ts index d0aaad979485d..8dde67683a739 100644 --- a/packages/kbn-optimizer/src/optimizer/cache_keys.ts +++ b/packages/kbn-optimizer/src/optimizer/cache_keys.ts @@ -23,7 +23,7 @@ import { promisify } from 'util'; import Chalk from 'chalk'; import execa from 'execa'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import stripAnsi from 'strip-ansi'; import jestDiff from 'jest-diff'; diff --git a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts index 3cc58e744a7b9..6edcde56e26de 100644 --- a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts +++ b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts @@ -18,7 +18,7 @@ */ import * as Rx from 'rxjs'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { Update } from '../common'; diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts index afc2dc8952c87..fd887e8c2c012 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts @@ -32,7 +32,8 @@ jest.mock('os', () => { }); import Path from 'path'; -import { REPO_ROOT, createAbsolutePathSerializer } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { createAbsolutePathSerializer } from '@kbn/dev-utils'; import { OptimizerConfig, ParsedOptions } from './optimizer_config'; import { parseThemeTags } from '../common'; diff --git a/packages/kbn-plugin-generator/src/ask_questions.ts b/packages/kbn-plugin-generator/src/ask_questions.ts index b598396187245..732e22b0e2225 100644 --- a/packages/kbn-plugin-generator/src/ask_questions.ts +++ b/packages/kbn-plugin-generator/src/ask_questions.ts @@ -19,7 +19,7 @@ import Path from 'path'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import inquirer from 'inquirer'; export interface Answers { diff --git a/packages/kbn-plugin-generator/src/cli.ts b/packages/kbn-plugin-generator/src/cli.ts index f6966a245e46f..14c910a943fd5 100644 --- a/packages/kbn-plugin-generator/src/cli.ts +++ b/packages/kbn-plugin-generator/src/cli.ts @@ -21,7 +21,8 @@ import Path from 'path'; import Fs from 'fs'; import execa from 'execa'; -import { REPO_ROOT, run, createFailError, createFlagError } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { run, createFailError, createFlagError } from '@kbn/dev-utils'; import { snakeCase } from './casing'; import { askQuestions, getDefaultAnswers } from './ask_questions'; diff --git a/packages/kbn-plugin-generator/src/integration_tests/generate_plugin.test.ts b/packages/kbn-plugin-generator/src/integration_tests/generate_plugin.test.ts index b48113afc0ca7..25669b65e1ac9 100644 --- a/packages/kbn-plugin-generator/src/integration_tests/generate_plugin.test.ts +++ b/packages/kbn-plugin-generator/src/integration_tests/generate_plugin.test.ts @@ -21,7 +21,8 @@ import Path from 'path'; import del from 'del'; import execa from 'execa'; -import { REPO_ROOT, createAbsolutePathSerializer } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { createAbsolutePathSerializer } from '@kbn/dev-utils'; import globby from 'globby'; const GENERATED_DIR = Path.resolve(REPO_ROOT, `plugins`); diff --git a/packages/kbn-plugin-generator/src/plugin_types.ts b/packages/kbn-plugin-generator/src/plugin_types.ts index ae5201f4e8dbb..778e33353a2aa 100644 --- a/packages/kbn-plugin-generator/src/plugin_types.ts +++ b/packages/kbn-plugin-generator/src/plugin_types.ts @@ -19,7 +19,7 @@ import Path from 'path'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; export interface PluginType { thirdParty: boolean; diff --git a/packages/kbn-plugin-generator/src/render_template.ts b/packages/kbn-plugin-generator/src/render_template.ts index 894088c119651..ecb168042b1c2 100644 --- a/packages/kbn-plugin-generator/src/render_template.ts +++ b/packages/kbn-plugin-generator/src/render_template.ts @@ -23,7 +23,8 @@ import { promisify } from 'util'; import vfs from 'vinyl-fs'; import prettier from 'prettier'; -import { REPO_ROOT, transformFileStream } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { transformFileStream } from '@kbn/dev-utils'; import ejs from 'ejs'; import { Minimatch } from 'minimatch'; diff --git a/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts b/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts index be23d8dbde646..5c1ecc4ee4ee4 100644 --- a/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts +++ b/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts @@ -21,7 +21,8 @@ import Path from 'path'; import Fs from 'fs'; import execa from 'execa'; -import { createStripAnsiSerializer, REPO_ROOT, createReplaceSerializer } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { createStripAnsiSerializer, createReplaceSerializer } from '@kbn/dev-utils'; import extract from 'extract-zip'; import del from 'del'; import globby from 'globby'; diff --git a/packages/kbn-plugin-helpers/src/load_kibana_platform_plugin.ts b/packages/kbn-plugin-helpers/src/load_kibana_platform_plugin.ts index 8f48670e49de4..8ed20a26649e7 100644 --- a/packages/kbn-plugin-helpers/src/load_kibana_platform_plugin.ts +++ b/packages/kbn-plugin-helpers/src/load_kibana_platform_plugin.ts @@ -19,12 +19,8 @@ import Path from 'path'; -import { - REPO_ROOT, - parseKibanaPlatformPlugin, - KibanaPlatformPlugin, - createFailError, -} from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { parseKibanaPlatformPlugin, KibanaPlatformPlugin, createFailError } from '@kbn/dev-utils'; export type Plugin = KibanaPlatformPlugin; diff --git a/packages/kbn-plugin-helpers/src/tasks/optimize.ts b/packages/kbn-plugin-helpers/src/tasks/optimize.ts index 6cfeda8b2d81b..fc1bf48cc0e36 100644 --- a/packages/kbn-plugin-helpers/src/tasks/optimize.ts +++ b/packages/kbn-plugin-helpers/src/tasks/optimize.ts @@ -21,7 +21,7 @@ import Fs from 'fs'; import Path from 'path'; import { promisify } from 'util'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { OptimizerConfig, runOptimizer, logOptimizerState } from '@kbn/optimizer'; import { BuildContext } from '../build_context'; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 914ca2fd65fa2..1fb94e4c92ce1 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -66,6 +66,7 @@ "write-pkg": "^4.0.0" }, "dependencies": { + "@kbn/utils": "1.0.0", "tslib": "^2.0.0" } } diff --git a/packages/kbn-release-notes/package.json b/packages/kbn-release-notes/package.json index 12d0122f9a4d3..f8971fa02aa87 100644 --- a/packages/kbn-release-notes/package.json +++ b/packages/kbn-release-notes/package.json @@ -8,6 +8,7 @@ "kbn:watch": "tsc --watch" }, "dependencies": { + "@kbn/utils": "1.0.0", "@kbn/dev-utils": "1.0.0", "axios": "^0.19.2", "cheerio": "0.22.0", diff --git a/packages/kbn-release-notes/src/cli.ts b/packages/kbn-release-notes/src/cli.ts index 7dcfa38078391..dff59d79cb2b2 100644 --- a/packages/kbn-release-notes/src/cli.ts +++ b/packages/kbn-release-notes/src/cli.ts @@ -21,7 +21,8 @@ import Fs from 'fs'; import Path from 'path'; import { inspect } from 'util'; -import { run, createFlagError, createFailError, REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { run, createFlagError, createFailError } from '@kbn/dev-utils'; import { FORMATS, SomeFormat } from './formats'; import { diff --git a/packages/kbn-std/README.md b/packages/kbn-std/README.md new file mode 100644 index 0000000000000..dfd98287ada4b --- /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 0000000000000..2cc9fd72082be --- /dev/null +++ b/packages/kbn-std/package.json @@ -0,0 +1,20 @@ +{ + "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" + } +} 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 0000000000000..8cffcd43d7537 --- /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, ParsedQuery } 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 43878c27b1e19..c0de50544a34e 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 96% rename from src/core/utils/url.ts rename to packages/kbn-std/src/url.ts index 910fc8eaa4381..7a0f08130816d 100644 --- a/src/core/utils/url.ts +++ b/packages/kbn-std/src/url.ts @@ -16,9 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { ParsedQuery } from 'query-string'; + import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; +// duplicate type from 'query-string' to avoid adding the d.ts file to all packages depending on kbn-std +export interface ParsedQuery { + [key: string]: T | T[] | null | undefined; +} + /** * We define our own typings because the current version of @types/node * declares properties to be optional "hostname?: string". diff --git a/packages/kbn-std/tsconfig.json b/packages/kbn-std/tsconfig.json new file mode 100644 index 0000000000000..fd186a6e43d1c --- /dev/null +++ b/packages/kbn-std/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "./target", + "outDir": "./target", + "stripInternal": true, + "declarationMap": true, + "types": ["jest", "node"] + }, + "include": ["./src/**/*.ts"], + "exclude": ["target"] +} diff --git a/packages/kbn-std/yarn.lock b/packages/kbn-std/yarn.lock new file mode 120000 index 0000000000000..3f82ebc9cdbae --- /dev/null +++ b/packages/kbn-std/yarn.lock @@ -0,0 +1 @@ +../../yarn.lock \ No newline at end of file diff --git a/packages/kbn-storybook/lib/constants.js b/packages/kbn-storybook/lib/constants.js index 9d216d347eada..4d8ca0adbfe17 100644 --- a/packages/kbn-storybook/lib/constants.js +++ b/packages/kbn-storybook/lib/constants.js @@ -17,10 +17,10 @@ * under the License. */ -const { resolve, dirname } = require('path'); +const { resolve } = require('path'); +const { REPO_ROOT } = require('@kbn/utils'); -exports.REPO_ROOT = dirname(require.resolve('../../../package.json')); -exports.ASSET_DIR = resolve(exports.REPO_ROOT, 'built_assets/storybook'); +exports.ASSET_DIR = resolve(REPO_ROOT, 'built_assets/storybook'); exports.CURRENT_CONFIG = resolve(exports.ASSET_DIR, 'current.config.js'); exports.STORY_ENTRY_PATH = resolve(exports.ASSET_DIR, 'stories.entry.js'); exports.DLL_DIST_DIR = resolve(exports.ASSET_DIR, 'dll'); diff --git a/packages/kbn-storybook/lib/dll.js b/packages/kbn-storybook/lib/dll.js index a9154ca972120..55bc8e43a02ec 100644 --- a/packages/kbn-storybook/lib/dll.js +++ b/packages/kbn-storybook/lib/dll.js @@ -20,7 +20,8 @@ const { resolve } = require('path'); const { existsSync } = require('fs'); -const { REPO_ROOT, DLL_DIST_DIR } = require('./constants'); +const { REPO_ROOT } = require('@kbn/utils'); +const { DLL_DIST_DIR } = require('./constants'); exports.buildDll = async ({ rebuildDll, log, procRunner }) => { if (rebuildDll) { diff --git a/packages/kbn-storybook/lib/storybook_entry.js b/packages/kbn-storybook/lib/storybook_entry.js index 8b8aa4126ad88..fc970b1ff9d2a 100644 --- a/packages/kbn-storybook/lib/storybook_entry.js +++ b/packages/kbn-storybook/lib/storybook_entry.js @@ -27,11 +27,12 @@ const { promisify } = require('util'); const watch = require('glob-watcher'); const mkdirp = require('mkdirp'); // eslint-disable-line const glob = require('fast-glob'); +const { REPO_ROOT } = require('@kbn/utils'); const mkdirpAsync = promisify(mkdirp); const writeFileAsync = promisify(Fs.writeFile); -const { REPO_ROOT, STORY_ENTRY_PATH } = require('./constants'); +const { STORY_ENTRY_PATH } = require('./constants'); const STORE_ENTRY_DIR = dirname(STORY_ENTRY_PATH); exports.generateStorybookEntry = ({ log, storyGlobs }) => { diff --git a/packages/kbn-storybook/lib/webpack.dll.config.js b/packages/kbn-storybook/lib/webpack.dll.config.js index 18dbe3bd049d3..6e3b4d41bd7f0 100644 --- a/packages/kbn-storybook/lib/webpack.dll.config.js +++ b/packages/kbn-storybook/lib/webpack.dll.config.js @@ -20,8 +20,9 @@ const webpack = require('webpack'); const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const { REPO_ROOT } = require('@kbn/utils'); -const { DLL_NAME, REPO_ROOT, DLL_DIST_DIR } = require('./constants'); +const { DLL_NAME, DLL_DIST_DIR } = require('./constants'); // This is the Webpack config for the DLL of CSS and JS assets that are // not expected to change during development. This saves compile and run diff --git a/packages/kbn-storybook/package.json b/packages/kbn-storybook/package.json index 9f12cd1f46c68..5271ddb96d842 100644 --- a/packages/kbn-storybook/package.json +++ b/packages/kbn-storybook/package.json @@ -6,6 +6,7 @@ "dependencies": { "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", + "@kbn/utils": "1.0.0", "@storybook/addon-actions": "^5.3.19", "@storybook/addon-console": "^1.2.1", "@storybook/addon-info": "^5.3.19", diff --git a/packages/kbn-storybook/storybook_config/webpack.config.js b/packages/kbn-storybook/storybook_config/webpack.config.js index d505c4f9a0448..60b6b6add66d1 100644 --- a/packages/kbn-storybook/storybook_config/webpack.config.js +++ b/packages/kbn-storybook/storybook_config/webpack.config.js @@ -22,7 +22,8 @@ const webpack = require('webpack'); const webpackMerge = require('webpack-merge'); const { stringifyRequest } = require('loader-utils'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const { REPO_ROOT, DLL_DIST_DIR } = require('../lib/constants'); +const { REPO_ROOT } = require('@kbn/utils'); +const { DLL_DIST_DIR } = require('../lib/constants'); // eslint-disable-next-line import/no-unresolved const { currentConfig } = require('../../../built_assets/storybook/current.config'); diff --git a/packages/kbn-telemetry-tools/GUIDELINE.md b/packages/kbn-telemetry-tools/GUIDELINE.md new file mode 100644 index 0000000000000..e7d09babbf9e2 --- /dev/null +++ b/packages/kbn-telemetry-tools/GUIDELINE.md @@ -0,0 +1,211 @@ +# Collector Schema Guideline + +Table of contents: +- [Collector Schema Guideline](#collector-schema-guideline) + - [Adding schema to your collector](#adding-schema-to-your-collector) + - [1. Update the telemetryrc file](#1-update-the-telemetryrc-file) + - [2. Type the `fetch` function](#2-type-the-fetch-function) + - [3. Add a `schema` field](#3-add-a-schema-field) + - [4. Run the telemetry check](#4-run-the-telemetry-check) + - [5. Update the stored json files](#5-update-the-stored-json-files) + - [Updating the collector schema](#updating-the-collector-schema) + - [Writing the schema](#writing-the-schema) + - [Basics](#basics) + - [Allowed types](#allowed-types) + - [Dealing with arrays](#dealing-with-arrays) + - [Schema Restrictions](#schema-restrictions) + - [Root of schema can only be an object](#root-of-schema-can-only-be-an-object) + + +## Adding schema to your collector + +To add a `schema` to the collector, follow these steps until the telemetry check passes. +To check the next step needed simply run the telemetry check with the path of your collector: + +``` +node scripts/telemetry_check.js --path=.ts +``` + +### 1. Update the telemetryrc file + +Make sure your collector is not excluded in the `telemetryrc.json` files (located at the root of the kibana project, and another on in the `x-pack` dir). + +```s +[ + { + ... + "exclude": [ + "" + ] + } +] +``` + +Note that the check will fail if the collector in --path is excluded. + +### 2. Type the `fetch` function +1. Make sure the return of the `fetch` function is typed. + +The function `makeUsageCollector` accepts a generic type parameter of the returned type of the `fetch` function. + +``` +interface Usage { + someStat: number; +} + +usageCollection.makeUsageCollector({ + fetch: async () => { + return { + someStat: 3, + } + }, + ... +}) +``` + +The generic type passed to `makeUsageCollector` will automatically unwrap the `Promise` to check for the resolved type. + +### 3. Add a `schema` field + +Add a `schema` field to your collector. After passing the return type of the fetch function to the `makeUsageCollector` generic parameter. It will automaticallly figure out the correct type of the schema based on that provided type. + + +``` +interface Usage { + someStat: number; +} + +usageCollection.makeUsageCollector({ + schema: { + someStat: { + type: 'long' + } + }, + ... +}) +``` + +For full details on writing the `schema` object, check the [Writing the schema](#writing-the-schema) section. + +### 4. Run the telemetry check + +To make sure your changes pass the telemetry check you can run the following: + +``` +node scripts/telemetry_check.js --ignore-stored-json --path=.ts +``` + +### 5. Update the stored json files + +The `--fix` flag will automatically update the persisted json files used by the telemetry team. + +``` +node scripts/telemetry_check.js --fix +``` + +Note that any updates to the stored json files will require a review by the kibana-telemetry team to help us update the telemetry cluster mappings and ensure your changes adhere to our best practices. + + +## Updating the collector schema + +Simply update the fetch function to start returning the updated fields back to our cluster. The update the schema to accomodate these changes. + +Once youre run the changes to both the `fetch` function and the `schema` field run the following command + +``` +node scripts/telemetry_check.js --fix +``` + +The `--fix` flag will automatically update the persisted json files used by the telemetry team. Note that any updates to the stored json files will require a review by the kibana-telemetry team to help us update the telemetry cluster mappings and ensure your changes adhere to our best practices. + + +## Writing the schema + +We've designed the schema object to closely resemble elasticsearch mapping object to reduce any cognitive complexity. + +### Basics + +The function `makeUsageCollector` will automatically translate the returned `Usage` fetch type to the `schema` object. This way you'll have the typescript type checker helping you write the correct corrisponding schema. + +``` +interface Usage { + someStat: number; +} + +usageCollection.makeUsageCollector({ + schema: { + someStat: { + type: 'long' + } + }, + ... +}) +``` + + +### Allowed types + +Any field property in the schema accepts a `type` field. By default the type is `object` which accepts nested properties under it. Currently we accept the following property types: + +``` +AllowedSchemaTypes = + | 'keyword' + | 'text' + | 'number' + | 'boolean' + | 'long' + | 'date' + | 'float'; +``` + + +### Dealing with arrays + +You can optionally define a property to be an array by setting the `isArray` to `true`. Note that the `isArray` property is not currently required. + + +``` +interface Usage { + arrayOfStrings: string[]; + arrayOfObjects: {key: string; value: number; }[]; +} + +usageCollection.makeUsageCollector({ + fetch: () => { + return { + arrayOfStrings: ['item_one', 'item_two'], + arrayOfObjects: [ + { key: 'key_one', value: 13 }, + ] + } + } + schema: { + arrayOfStrings: { + type: 'keyword', + isArray: true, + }, + arrayOfObjects: { + isArray: true, + key: { + type: 'keyword', + }, + value: { + type: 'long', + }, + } + }, + ... +}) +``` + +Be careful adding arrays of objects due to the limitation in correlating the properties inside those objects inside kibana. It is advised to look for an alternative schema based on your use cases. + + +## Schema Restrictions + +We have enforced some restrictions to the schema object to adhere to our telemetry best practices. These practices are derived from the usablity of the sent data in our telemetry cluster. + + +### Root of schema can only be an object + +The root of the schema can only be an object. Currently any property must be nested inside the main schema object. \ No newline at end of file diff --git a/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts b/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts index 2f85fd2cdd2a4..87ba68c1bcb27 100644 --- a/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts +++ b/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts @@ -35,7 +35,7 @@ import { export function runTelemetryCheck() { run( - async ({ flags: { fix = false, path }, log }) => { + async ({ flags: { fix = false, 'ignore-stored-json': ignoreStoredJson, path }, log }) => { if (typeof fix !== 'boolean') { throw createFailError(`${chalk.white.bgRed(' TELEMETRY ERROR ')} --fix can't have a value`); } @@ -50,6 +50,14 @@ export function runTelemetryCheck() { ); } + if (fix && typeof ignoreStoredJson !== 'undefined') { + throw createFailError( + `${chalk.white.bgRed( + ' TELEMETRY ERROR ' + )} --fix is incompatible with --ignore-stored-json flag.` + ); + } + const list = new Listr([ { title: 'Checking .telemetryrc.json files', @@ -59,11 +67,28 @@ export function runTelemetryCheck() { title: 'Extracting Collectors', task: (context) => new Listr(extractCollectorsTask(context, path), { exitOnError: true }), }, + { + enabled: () => typeof path !== 'undefined', + title: 'Checking collectors in --path are not excluded', + task: ({ roots }: TaskContext) => { + const totalCollections = roots.reduce((acc, root) => { + return acc + (root.parsedCollections?.length || 0); + }, 0); + const collectorsInPath = Array.isArray(path) ? path.length : 1; + + if (totalCollections !== collectorsInPath) { + throw new Error( + 'Collector specified in `path` is excluded; Check the telemetryrc.json files.' + ); + } + }, + }, { title: 'Checking Compatible collector.schema with collector.fetch type', task: (context) => new Listr(checkCompatibleTypesTask(context), { exitOnError: true }), }, { + enabled: (_) => !!ignoreStoredJson, title: 'Checking Matching collector.schema against stored json files', task: (context) => new Listr(checkMatchingSchemasTask(context, !fix), { exitOnError: true }), diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index c84b0a93311bb..8a4ff55dcf68f 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -13,6 +13,7 @@ "@babel/cli": "^7.10.5", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", + "@kbn/utils": "1.0.0", "@types/joi": "^13.4.2", "@types/lodash": "^4.14.159", "@types/parse-link-header": "^1.0.0", diff --git a/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts b/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts index 3dfb1ea44d9e7..93616ce78a04a 100644 --- a/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts +++ b/packages/kbn-test/src/failed_tests_reporter/run_failed_tests_reporter_cli.ts @@ -19,7 +19,8 @@ import Path from 'path'; -import { REPO_ROOT, run, createFailError, createFlagError } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { run, createFailError, createFlagError } from '@kbn/dev-utils'; import globby from 'globby'; import { getFailures, TestFailure } from './get_failures'; diff --git a/packages/kbn-test/src/functional_test_runner/__tests__/integration/basic.js b/packages/kbn-test/src/functional_test_runner/__tests__/integration/basic.js index 133f4d2feb53e..a010d9f0b038e 100644 --- a/packages/kbn-test/src/functional_test_runner/__tests__/integration/basic.js +++ b/packages/kbn-test/src/functional_test_runner/__tests__/integration/basic.js @@ -21,7 +21,7 @@ import { spawnSync } from 'child_process'; import { resolve } from 'path'; import expect from '@kbn/expect'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; const SCRIPT = resolve(REPO_ROOT, 'scripts/functional_test_runner.js'); const BASIC_CONFIG = require.resolve('../fixtures/simple_project/config.js'); diff --git a/packages/kbn-test/src/functional_test_runner/__tests__/integration/failure_hooks.js b/packages/kbn-test/src/functional_test_runner/__tests__/integration/failure_hooks.js index 12e28d2702c5a..fa4ef88fd3e70 100644 --- a/packages/kbn-test/src/functional_test_runner/__tests__/integration/failure_hooks.js +++ b/packages/kbn-test/src/functional_test_runner/__tests__/integration/failure_hooks.js @@ -22,7 +22,7 @@ import { resolve } from 'path'; import stripAnsi from 'strip-ansi'; import expect from '@kbn/expect'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; const SCRIPT = resolve(REPO_ROOT, 'scripts/functional_test_runner.js'); const FAILURE_HOOKS_CONFIG = require.resolve('../fixtures/failure_hooks/config.js'); 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 e1d3bf1a8d901..701171876ad2c 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-test/src/functional_test_runner/lib/failure_metadata.ts b/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.ts index fdf8b3c0ddfa8..e5c60f1d208b1 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/failure_metadata.ts @@ -19,7 +19,7 @@ import Path from 'path'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { Lifecycle } from './lifecycle'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js index 5d3d8fe7d759b..92137a8c4f841 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/decorate_mocha_ui.js @@ -17,7 +17,7 @@ * under the License. */ import { relative } from 'path'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { createAssignmentProxy } from './assignment_proxy'; import { wrapFunction } from './wrap_function'; import { wrapRunnableArgs } from './wrap_runnable_args'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js index 3ac7a50cd28ea..39eb69a151918 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js +++ b/packages/kbn-test/src/functional_test_runner/lib/mocha/setup_mocha.js @@ -19,7 +19,7 @@ import Mocha from 'mocha'; import { relative } from 'path'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { loadTestFiles } from './load_test_files'; import { filterSuitesByTags } from './filter_suites_by_tags'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts index f879408bf2beb..5350b2709843d 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts @@ -21,7 +21,7 @@ import fs from 'fs'; import { join, resolve } from 'path'; jest.mock('fs'); -jest.mock('@kbn/dev-utils', () => { +jest.mock('@kbn/utils', () => { return { REPO_ROOT: '/dev/null/root' }; }); diff --git a/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.ts b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.ts index b346be2d58dad..6cbd74467106f 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.ts @@ -19,7 +19,7 @@ import fs from 'fs'; import { dirname, relative, resolve } from 'path'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { Lifecycle } from './lifecycle'; diff --git a/packages/kbn-test/src/mocha/run_mocha_cli.js b/packages/kbn-test/src/mocha/run_mocha_cli.js index 3c77fef963a76..0fc7c762be425 100644 --- a/packages/kbn-test/src/mocha/run_mocha_cli.js +++ b/packages/kbn-test/src/mocha/run_mocha_cli.js @@ -17,7 +17,7 @@ * under the License. */ -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import getopts from 'getopts'; import globby from 'globby'; diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index bbe7b1bc2e8da..8095e05e8b855 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@elastic/charts": "21.1.2", - "@elastic/eui": "28.2.0", + "@elastic/eui": "28.4.0", "@elastic/numeral": "^2.5.0", "@kbn/i18n": "1.0.0", "@kbn/monaco": "1.0.0", diff --git a/packages/kbn-ui-shared-deps/webpack.config.js b/packages/kbn-ui-shared-deps/webpack.config.js index fa80dfdeef20f..b7d4e929ac93f 100644 --- a/packages/kbn-ui-shared-deps/webpack.config.js +++ b/packages/kbn-ui-shared-deps/webpack.config.js @@ -21,7 +21,7 @@ const Path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CompressionPlugin = require('compression-webpack-plugin'); -const { REPO_ROOT } = require('@kbn/dev-utils'); +const { REPO_ROOT } = require('@kbn/utils'); const webpack = require('webpack'); const UiSharedDeps = require('./index'); diff --git a/packages/kbn-utility-types/index.ts b/packages/kbn-utility-types/index.ts index 6ccfeb8ab052c..d3fb5bdf36194 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 03cace5b9cb2c..79cf423fe78ac 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/packages/kbn-utils/README.md b/packages/kbn-utils/README.md new file mode 100644 index 0000000000000..fd132e4312487 --- /dev/null +++ b/packages/kbn-utils/README.md @@ -0,0 +1,3 @@ +# @kbn/utils + +Shared server-side utilities shared across packages and plugins. \ No newline at end of file diff --git a/packages/kbn-utils/package.json b/packages/kbn-utils/package.json new file mode 100644 index 0000000000000..15fe5c6df5648 --- /dev/null +++ b/packages/kbn-utils/package.json @@ -0,0 +1,19 @@ +{ + "name": "@kbn/utils", + "main": "./target/index.js", + "version": "1.0.0", + "license": "Apache-2.0", + "private": true, + "scripts": { + "build": "tsc", + "kbn:bootstrap": "yarn build", + "kbn:watch": "yarn build --watch" + }, + "dependencies": { + "@kbn/config-schema": "1.0.0", + "load-json-file": "^6.2.0" + }, + "devDependencies": { + "typescript": "4.0.2" + } +} diff --git a/src/legacy/server/status/wrap_auth_config.js b/packages/kbn-utils/src/index.ts similarity index 78% rename from src/legacy/server/status/wrap_auth_config.js rename to packages/kbn-utils/src/index.ts index 04e71a02d30de..7a894d72d5624 100644 --- a/src/legacy/server/status/wrap_auth_config.js +++ b/packages/kbn-utils/src/index.ts @@ -17,11 +17,6 @@ * under the License. */ -import { assign, identity } from 'lodash'; - -export const wrapAuthConfig = (allowAnonymous) => { - if (allowAnonymous) { - return (options) => assign(options, { config: { auth: false } }); - } - return identity; -}; +export * from './package_json'; +export * from './path'; +export * from './repo_root'; diff --git a/packages/kbn-utils/src/package_json/index.test.ts b/packages/kbn-utils/src/package_json/index.test.ts new file mode 100644 index 0000000000000..afc84102999fe --- /dev/null +++ b/packages/kbn-utils/src/package_json/index.test.ts @@ -0,0 +1,31 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import path from 'path'; +import { kibanaPackageJSON } from './'; + +it('parses package.json', () => { + expect(kibanaPackageJSON.name).toEqual('kibana'); +}); + +it('includes __dirname and __filename', () => { + const root = path.resolve(__dirname, '../../../../'); + expect(kibanaPackageJSON.__filename).toEqual(path.resolve(root, 'package.json')); + expect(kibanaPackageJSON.__dirname).toEqual(root); +}); diff --git a/packages/kbn-utils/src/package_json/index.ts b/packages/kbn-utils/src/package_json/index.ts new file mode 100644 index 0000000000000..f439c0100e193 --- /dev/null +++ b/packages/kbn-utils/src/package_json/index.ts @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { dirname, resolve } from 'path'; +import { REPO_ROOT } from '../repo_root'; + +export const kibanaPackageJSON = { + __filename: resolve(REPO_ROOT, 'package.json'), + __dirname: dirname(resolve(REPO_ROOT, 'package.json')), + ...require(resolve(REPO_ROOT, 'package.json')), +}; diff --git a/src/core/server/path/index.test.ts b/packages/kbn-utils/src/path/index.test.ts similarity index 100% rename from src/core/server/path/index.test.ts rename to packages/kbn-utils/src/path/index.test.ts diff --git a/src/core/server/path/index.ts b/packages/kbn-utils/src/path/index.ts similarity index 94% rename from src/core/server/path/index.ts rename to packages/kbn-utils/src/path/index.ts index 7c1a81643fbc8..a7eddca5f2fde 100644 --- a/src/core/server/path/index.ts +++ b/packages/kbn-utils/src/path/index.ts @@ -20,7 +20,7 @@ import { join } from 'path'; import { accessSync, constants } from 'fs'; import { TypeOf, schema } from '@kbn/config-schema'; -import { fromRoot } from '../utils'; +import { REPO_ROOT } from '../repo_root'; const isString = (v: any): v is string => typeof v === 'string'; @@ -28,19 +28,19 @@ const CONFIG_PATHS = [ process.env.KBN_PATH_CONF && join(process.env.KBN_PATH_CONF, 'kibana.yml'), process.env.KIBANA_PATH_CONF && join(process.env.KIBANA_PATH_CONF, 'kibana.yml'), process.env.CONFIG_PATH, // deprecated - fromRoot('config/kibana.yml'), + join(REPO_ROOT, 'config/kibana.yml'), ].filter(isString); const CONFIG_DIRECTORIES = [ process.env.KBN_PATH_CONF, process.env.KIBANA_PATH_CONF, - fromRoot('config'), + join(REPO_ROOT, 'config'), '/etc/kibana', ].filter(isString); const DATA_PATHS = [ process.env.DATA_PATH, // deprecated - fromRoot('data'), + join(REPO_ROOT, 'data'), '/var/lib/kibana', ].filter(isString); diff --git a/packages/kbn-dev-utils/src/repo_root.ts b/packages/kbn-utils/src/repo_root.ts similarity index 100% rename from packages/kbn-dev-utils/src/repo_root.ts rename to packages/kbn-utils/src/repo_root.ts diff --git a/packages/kbn-utils/tsconfig.json b/packages/kbn-utils/tsconfig.json new file mode 100644 index 0000000000000..e9dd6313e6f79 --- /dev/null +++ b/packages/kbn-utils/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target", + "declaration": true, + "declarationMap": true + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-utils/yarn.lock b/packages/kbn-utils/yarn.lock new file mode 120000 index 0000000000000..3f82ebc9cdbae --- /dev/null +++ b/packages/kbn-utils/yarn.lock @@ -0,0 +1 @@ +../../yarn.lock \ No newline at end of file diff --git a/src/cli/cluster/cluster_manager.ts b/src/cli/cluster/cluster_manager.ts index eb7db8dac7cb6..b1d1335eb1888 100644 --- a/src/cli/cluster/cluster_manager.ts +++ b/src/cli/cluster/cluster_manager.ts @@ -21,7 +21,7 @@ import { resolve } from 'path'; import { format as formatUrl } from 'url'; import opn from 'opn'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { FSWatcher } from 'chokidar'; import * as Rx from 'rxjs'; import { startWith, mapTo, filter, map, take, tap } from 'rxjs/operators'; diff --git a/src/cli/cluster/run_kbn_optimizer.ts b/src/cli/cluster/run_kbn_optimizer.ts index c231437da3943..8196cad4a99c7 100644 --- a/src/cli/cluster/run_kbn_optimizer.ts +++ b/src/cli/cluster/run_kbn_optimizer.ts @@ -19,12 +19,12 @@ import Chalk from 'chalk'; import moment from 'moment'; +import { REPO_ROOT } from '@kbn/utils'; import { ToolingLog, pickLevelFromFlags, ToolingLogTextWriter, parseLogLevel, - REPO_ROOT, } from '@kbn/dev-utils'; import { runOptimizer, OptimizerConfig, logOptimizerState } from '@kbn/optimizer'; diff --git a/src/cli/serve/integration_tests/reload_logging_config.test.ts b/src/cli/serve/integration_tests/reload_logging_config.test.ts index 35391b9b58ecc..55f71ea2401db 100644 --- a/src/cli/serve/integration_tests/reload_logging_config.test.ts +++ b/src/cli/serve/integration_tests/reload_logging_config.test.ts @@ -26,8 +26,7 @@ import Del from 'del'; import * as Rx from 'rxjs'; import { map, filter, take } from 'rxjs/operators'; import { safeDump } from 'js-yaml'; - -import { getConfigFromFiles } from '../../../core/server/config/read_config'; +import { getConfigFromFiles } from '@kbn/config'; const legacyConfig = follow('__fixtures__/reload_logging_config/kibana.test.yml'); const configFileLogConsole = follow( diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index c08f3aec64335..eeb5564667ec4 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -23,9 +23,9 @@ import { statSync } from 'fs'; import { resolve } from 'path'; import url from 'url'; +import { getConfigPath } from '@kbn/utils'; import { IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils'; import { fromRoot } from '../../core/server/utils'; -import { getConfigPath } from '../../core/server/path'; import { bootstrap } from '../../core/server'; import { readKeystore } from './read_keystore'; diff --git a/src/cli_keystore/get_keystore.js b/src/cli_keystore/get_keystore.js index e181efe9196b8..b97efbb2cbf4d 100644 --- a/src/cli_keystore/get_keystore.js +++ b/src/cli_keystore/get_keystore.js @@ -21,7 +21,7 @@ import { existsSync } from 'fs'; import { join } from 'path'; import { Logger } from '../cli_plugin/lib/logger'; -import { getConfigDirectory, getDataPath } from '../core/server/path'; +import { getConfigDirectory, getDataPath } from '@kbn/utils'; export function getKeystore() { const configKeystore = join(getConfigDirectory(), 'kibana.keystore'); diff --git a/src/cli_plugin/install/index.js b/src/cli_plugin/install/index.js index bc7e95b8489f0..eff14906853c1 100644 --- a/src/cli_plugin/install/index.js +++ b/src/cli_plugin/install/index.js @@ -17,10 +17,10 @@ * under the License. */ +import { getConfigPath } from '@kbn/utils'; import { pkg } from '../../core/server/utils'; import { install } from './install'; import { Logger } from '../lib/logger'; -import { getConfigPath } from '../../core/server/path'; import { parse, parseMilliseconds } from './settings'; import { logWarnings } from '../lib/log_warnings'; diff --git a/src/cli_plugin/remove/index.js b/src/cli_plugin/remove/index.js index c3bd96086db9b..b9c37c0cf802b 100644 --- a/src/cli_plugin/remove/index.js +++ b/src/cli_plugin/remove/index.js @@ -17,10 +17,10 @@ * under the License. */ +import { getConfigPath } from '@kbn/utils'; import { remove } from './remove'; import { Logger } from '../lib/logger'; import { parse } from './settings'; -import { getConfigPath } from '../../core/server/path'; import { logWarnings } from '../lib/log_warnings'; function processCommand(command, options) { diff --git a/src/core/public/_variables.scss b/src/core/public/_variables.scss new file mode 100644 index 0000000000000..8c054e770bd4b --- /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 54aaa31e08859..ee37854762666 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 7304a8e5a66bc..1164164aec4c5 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 64012f0c0b6c1..6d29b77d92f36 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 f668cf851da55..089d1cf3f3ced 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 5862ee7175f71..9cd96763d2e79 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 b96c34cd9fbe8..b01f120b81305 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 167948e01cb36..2638f40c77dc4 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 4b82e0ced4505..80f0265819c40 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 a770ece8496e4..699fe2c4c4d4d 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, @@ -279,6 +279,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` } } navigateToApp={[Function]} + navigateToUrl={[Function]} onIsLockedUpdate={[Function]} recentlyAccessed$={ BehaviorSubject { @@ -657,6 +658,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` aria-controls="mockId" aria-expanded={true} className="euiAccordion__button euiAccordion__buttonReverse euiCollapsibleNavGroup__heading" + id="mockId" onClick={[Function]} type="button" > @@ -910,6 +912,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` aria-controls="mockId" aria-expanded={true} className="euiAccordion__button euiAccordion__buttonReverse euiCollapsibleNavGroup__heading" + id="mockId" onClick={[Function]} type="button" > @@ -1199,6 +1202,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` aria-controls="mockId" aria-expanded={true} className="euiAccordion__button euiAccordion__buttonReverse euiCollapsibleNavGroup__heading" + id="mockId" onClick={[Function]} type="button" > @@ -1449,6 +1453,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` aria-controls="mockId" aria-expanded={true} className="euiAccordion__button euiAccordion__buttonReverse euiCollapsibleNavGroup__heading" + id="mockId" onClick={[Function]} type="button" > @@ -1651,6 +1656,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` aria-controls="mockId" aria-expanded={true} className="euiAccordion__button euiAccordion__buttonReverse euiCollapsibleNavGroup__heading" + id="mockId" onClick={[Function]} type="button" > @@ -2105,7 +2111,7 @@ exports[`CollapsibleNav renders the default nav 1`] = ` homeHref="/" id="collapsibe-nav" isLocked={false} - isOpen={false} + isNavOpen={false} navLinks$={ BehaviorSubject { "_isScalar": false, @@ -2156,6 +2162,7 @@ exports[`CollapsibleNav renders the default nav 1`] = ` } } navigateToApp={[Function]} + navigateToUrl={[Function]} onIsLockedUpdate={[Function]} recentlyAccessed$={ BehaviorSubject { @@ -2339,6 +2346,7 @@ exports[`CollapsibleNav renders the default nav 2`] = ` homeHref="/" id="collapsibe-nav" isLocked={false} + isNavOpen={false} isOpen={true} navLinks$={ BehaviorSubject { @@ -2390,6 +2398,7 @@ exports[`CollapsibleNav renders the default nav 2`] = ` } } navigateToApp={[Function]} + navigateToUrl={[Function]} onIsLockedUpdate={[Function]} recentlyAccessed$={ BehaviorSubject { @@ -2454,461 +2463,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 +2582,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` homeHref="/" id="collapsibe-nav" isLocked={true} + isNavOpen={false} isOpen={true} navLinks$={ BehaviorSubject { @@ -3076,6 +2634,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` } } navigateToApp={[Function]} + navigateToUrl={[Function]} onIsLockedUpdate={[Function]} recentlyAccessed$={ BehaviorSubject { @@ -3140,7 +2699,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` data-test-subj="collapsibleNav" id="collapsibe-nav" isDocked={true} - isOpen={true} + isOpen={false} onClose={[Function]} > diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 128a0c5369e08..b5c1f3ca843e4 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -9,7 +9,45 @@ exports[`Header renders 1`] = ` "closed": false, "hasError": false, "isStopped": false, - "observers": Array [], + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], "thrownError": null, } } @@ -35,7 +73,45 @@ exports[`Header renders 1`] = ` "closed": false, "hasError": false, "isStopped": false, - "observers": Array [], + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], "thrownError": null, }, "currentAppId$": Observable { @@ -45,7 +121,45 @@ exports[`Header renders 1`] = ` "closed": false, "hasError": false, "isStopped": false, - "observers": Array [], + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], "thrownError": null, }, }, @@ -82,7 +196,45 @@ exports[`Header renders 1`] = ` "closed": false, "hasError": false, "isStopped": false, - "observers": Array [], + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], "thrownError": null, } } @@ -106,7 +258,45 @@ exports[`Header renders 1`] = ` "closed": false, "hasError": false, "isStopped": false, - "observers": Array [], + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], "thrownError": null, } } @@ -122,147 +312,49 @@ exports[`Header renders 1`] = ` "closed": false, "hasError": false, "isStopped": false, - "observers": Array [], + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], "thrownError": null, } } forceAppSwitcherNavigation$={ - BehaviorSubject { - "_isScalar": false, - "_value": false, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - } - } - helpExtension$={ - BehaviorSubject { - "_isScalar": false, - "_value": undefined, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - } - } - helpSupportUrl$={ - BehaviorSubject { - "_isScalar": false, - "_value": "", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - } - } - homeHref="/" - intl={ - Object { - "defaultFormats": Object {}, - "defaultLocale": "en", - "formatDate": [Function], - "formatHTMLMessage": [Function], - "formatMessage": [Function], - "formatNumber": [Function], - "formatPlural": [Function], - "formatRelative": [Function], - "formatTime": [Function], - "formats": Object { - "date": Object { - "full": Object { - "day": "numeric", - "month": "long", - "weekday": "long", - "year": "numeric", - }, - "long": Object { - "day": "numeric", - "month": "long", - "year": "numeric", - }, - "medium": Object { - "day": "numeric", - "month": "short", - "year": "numeric", - }, - "short": Object { - "day": "numeric", - "month": "numeric", - "year": "2-digit", - }, - }, - "number": Object { - "currency": Object { - "style": "currency", - }, - "percent": Object { - "style": "percent", - }, - }, - "relative": Object { - "days": Object { - "units": "day", - }, - "hours": Object { - "units": "hour", - }, - "minutes": Object { - "units": "minute", - }, - "months": Object { - "units": "month", - }, - "seconds": Object { - "units": "second", - }, - "years": Object { - "units": "year", - }, - }, - "time": Object { - "full": Object { - "hour": "numeric", - "minute": "numeric", - "second": "numeric", - "timeZoneName": "short", - }, - "long": Object { - "hour": "numeric", - "minute": "numeric", - "second": "numeric", - "timeZoneName": "short", - }, - "medium": Object { - "hour": "numeric", - "minute": "numeric", - "second": "numeric", - }, - "short": Object { - "hour": "numeric", - "minute": "numeric", - }, - }, - }, - "formatters": Object { - "getDateTimeFormat": [Function], - "getMessageFormat": [Function], - "getNumberFormat": [Function], - "getPluralFormat": [Function], - "getRelativeFormat": [Function], - }, - "locale": "en", - "messages": Object {}, - "now": [Function], - "onError": [Function], - "textComponent": Symbol(react.fragment), - "timeZone": null, - } - } - isLocked$={ BehaviorSubject { "_isScalar": false, "_value": false, @@ -311,156 +403,181 @@ exports[`Header renders 1`] = ` "thrownError": null, } } - isVisible$={ + helpExtension$={ BehaviorSubject { "_isScalar": false, - "_value": false, + "_value": undefined, "closed": false, "hasError": false, "isStopped": false, "observers": Array [ - Subscriber { - "_parentOrParents": null, - "_subscriptions": Array [ - SubjectSubscription { - "_parentOrParents": [Circular], - "_subscriptions": null, + InnerSubscriber { + "_parentOrParents": CombineLatestSubscriber { + "_parentOrParents": Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + [Circular], + ], "closed": false, - "subject": [Circular], - "subscriber": [Circular], + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, }, - ], - "closed": false, - "destination": SafeSubscriber { - "_complete": undefined, - "_context": [Circular], - "_error": undefined, - "_next": [Function], - "_parentOrParents": null, - "_parentSubscriber": [Circular], - "_subscriptions": null, + "_subscriptions": Array [ + [Circular], + InnerSubscriber { + "_parentOrParents": [Circular], + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [ + [Circular], + ], + "thrownError": null, + }, + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "index": 1, + "isStopped": false, + "outerIndex": 1, + "outerValue": BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [ + [Circular], + ], + "thrownError": null, + }, + "parent": [Circular], + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], + "active": 2, "closed": false, - "destination": Object { - "closed": true, - "complete": [Function], - "error": [Function], - "next": [Function], + "destination": Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + [Circular], + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, }, - "isStopped": false, - "syncErrorThrowable": false, - "syncErrorThrown": false, - "syncErrorValue": null, - }, - "isStopped": false, - "syncErrorThrowable": true, - "syncErrorThrown": false, - "syncErrorValue": null, - }, - ], - "thrownError": null, - } - } - kibanaDocLink="/docs" - kibanaVersion="1.0.0" - loadingCount$={ - BehaviorSubject { - "_isScalar": false, - "_value": 0, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - Subscriber { - "_parentOrParents": null, - "_subscriptions": Array [ - SubjectSubscription { - "_parentOrParents": [Circular], - "_subscriptions": null, - "closed": false, - "subject": [Circular], - "subscriber": [Circular], - }, - ], - "closed": false, - "destination": SafeSubscriber { - "_complete": undefined, - "_context": [Circular], - "_error": undefined, - "_next": [Function], - "_parentOrParents": null, - "_parentSubscriber": [Circular], - "_subscriptions": null, - "closed": false, - "destination": Object { - "closed": true, - "complete": [Function], - "error": [Function], - "next": [Function], - }, - "isStopped": false, - "syncErrorThrowable": false, + "isStopped": true, + "observables": Array [ + [Circular], + BehaviorSubject { + "_isScalar": false, + "_value": "", + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [ + InnerSubscriber { + "_parentOrParents": [Circular], + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "index": 1, + "isStopped": false, + "outerIndex": 1, + "outerValue": [Circular], + "parent": [Circular], + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], + "thrownError": null, + }, + ], + "resultSelector": null, + "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, + "toRespond": 0, + "values": Array [ + undefined, + "", + ], }, - "isStopped": false, - "syncErrorThrowable": true, - "syncErrorThrown": false, - "syncErrorValue": null, - }, - ], - "thrownError": null, - } - } - navControlsLeft$={ - BehaviorSubject { - "_isScalar": false, - "_value": Array [], - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - } - } - navControlsRight$={ - BehaviorSubject { - "_isScalar": false, - "_value": Array [], - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - } - } - navLinks$={ - BehaviorSubject { - "_isScalar": false, - "_value": Array [ - Object { - "baseUrl": "", - "href": "", - "id": "kibana", - "title": "kibana", - }, - ], - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - } - } - navType$={ - BehaviorSubject { - "_isScalar": false, - "_value": "modern", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - Subscriber { - "_parentOrParents": null, "_subscriptions": Array [ SubjectSubscription { "_parentOrParents": [Circular], @@ -471,161 +588,183 @@ exports[`Header renders 1`] = ` }, ], "closed": false, - "destination": SafeSubscriber { - "_complete": undefined, - "_context": [Circular], - "_error": undefined, - "_next": [Function], - "_parentOrParents": null, - "_parentSubscriber": [Circular], - "_subscriptions": null, - "closed": false, - "destination": Object { - "closed": true, - "complete": [Function], - "error": [Function], - "next": [Function], - }, - "isStopped": false, - "syncErrorThrowable": false, - "syncErrorThrown": false, - "syncErrorValue": null, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], }, + "index": 1, "isStopped": false, - "syncErrorThrowable": true, - "syncErrorThrown": false, - "syncErrorValue": null, - }, - ], - "thrownError": null, - } - } - onIsLockedUpdate={[Function]} - recentlyAccessed$={ - BehaviorSubject { - "_isScalar": false, - "_value": Array [ - Object { - "id": "dashboard", - "label": "dashboard", - "link": "", - }, - ], - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [], - "thrownError": null, - } - } -> - -
-
-
- - -`; - -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 e3b73abbcabc2..44cd864278325 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 d4325e0caf88c..267e17dc0a9d0 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([]), @@ -67,6 +67,7 @@ function mockProps() { onIsLockedUpdate: () => {}, closeNav: () => {}, navigateToApp: () => Promise.resolve(), + navigateToUrl: () => Promise.resolve(), customNavLink$: new BehaviorSubject(undefined), }; } @@ -123,7 +124,7 @@ describe('CollapsibleNav', () => { const component = mount( { const component = mount( @@ -147,9 +148,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 +161,14 @@ describe('CollapsibleNav', () => { const component = mount( ); component.setProps({ closeNav: () => { - component.setProps({ isOpen: false }); + component.setProps({ isNavOpen: false }); onClose(); }, }); @@ -175,11 +176,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 a5f42c0949562..b00e82b660e9f 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; @@ -87,6 +87,7 @@ interface Props { onIsLockedUpdate: OnIsLockedUpdate; closeNav: () => void; navigateToApp: InternalApplicationStart['navigateToApp']; + navigateToUrl: InternalApplicationStart['navigateToUrl']; customNavLink$: Rx.Observable; } @@ -94,12 +95,13 @@ export function CollapsibleNav({ basePath, id, isLocked, - isOpen, + isNavOpen, homeHref, storage = window.localStorage, onIsLockedUpdate, closeNav, navigateToApp, + navigateToUrl, ...observables }: Props) { const navLinks = useObservable(observables.navLinks$, []).filter((link) => !link.hidden); @@ -129,7 +131,7 @@ export function CollapsibleNav({ aria-label={i18n.translate('core.ui.primaryNav.screenReaderLabel', { defaultMessage: 'Primary', })} - isOpen={isOpen} + isOpen={isNavOpen} isDocked={isLocked} onClose={closeNav} > @@ -217,17 +219,21 @@ export function CollapsibleNav({ listItems={recentlyAccessed.map((link) => { // TODO #64541 // Can remove icon from recent links completely - const { iconType, ...hydratedLink } = createRecentNavLink(link, navLinks, basePath); + const { iconType, onClick, ...hydratedLink } = createRecentNavLink( + link, + navLinks, + basePath, + navigateToUrl + ); return { ...hydratedLink, 'data-test-subj': 'collapsibleNavAppLink--recent', onClick: (event) => { - if (isModifiedOrPrevented(event)) { - return; + if (!isModifiedOrPrevented(event)) { + closeNav(); + onClick(event); } - - closeNav(); }, }; })} diff --git a/src/core/public/chrome/ui/header/header.test.tsx b/src/core/public/chrome/ui/header/header.test.tsx index 04eb256f30f37..7309b9af49388 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 c0b3fc72930dc..e01a62a54c34d 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,117 @@ 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 0000000000000..a124c8ab66969 --- /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 0000000000000..3a7a09608ba66 --- /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 9bec946b6b76e..dee93ecb1a804 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 0941f7b27b662..8d9d8097fd8e3 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 fc080fbafc303..0000000000000 --- 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/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx index 04d9c5bf7a10a..a3c73fc454362 100644 --- a/src/core/public/chrome/ui/header/nav_link.tsx +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem, CoreStart } from '../../..'; import { HttpStart } from '../../../http'; +import { InternalApplicationStart } from '../../../application/types'; import { relativeToAbsolute } from '../../nav_links/to_nav_link'; export const isModifiedOrPrevented = (event: React.MouseEvent) => @@ -87,6 +88,7 @@ export interface RecentNavLink { title: string; 'aria-label': string; iconType?: string; + onClick: React.MouseEventHandler; } /** @@ -102,8 +104,9 @@ export interface RecentNavLink { export function createRecentNavLink( recentLink: ChromeRecentlyAccessedHistoryItem, navLinks: ChromeNavLink[], - basePath: HttpStart['basePath'] -) { + basePath: HttpStart['basePath'], + navigateToUrl: InternalApplicationStart['navigateToUrl'] +): RecentNavLink { const { link, label } = recentLink; const href = relativeToAbsolute(basePath.prepend(link)); const navLink = navLinks.find((nl) => href.startsWith(nl.baseUrl)); @@ -125,5 +128,12 @@ export function createRecentNavLink( title: titleAndAriaLabel, 'aria-label': titleAndAriaLabel, iconType: navLink?.euiIconType, + /* Use href and onClick to support "open in new tab" and SPA navigation in the same link */ + onClick(event: React.MouseEvent) { + if (event.button === 0 && !isModifiedOrPrevented(event)) { + event.preventDefault(); + navigateToUrl(href); + } + }, }; } diff --git a/src/core/public/context/context_service.mock.ts b/src/core/public/context/context_service.mock.ts index eb55ced69dc04..3f9f647e1f7d0 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 b0e3871a40bf5..b2af81b3630a3 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 006d0036f7a12..a1d6f9c988b27 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 105c13f96cef6..07e29edad8974 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 fae7a272c9635..47f58a3a9fcbf 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 6d9876f787fa9..326ce9a3923e9 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 ac85d71c793fe..5d9eb51023b78 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 1111fd39ec78e..68533159765fb 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 79928aa4b160d..2ff635e815296 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 1a9ca0869d7c1..a91710fbed780 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 c2ad2841d5a77..6ba9254e5d381 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 a9774dafd2340..24d19e2d32074 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 3bb4358406246..33d04eedebb07 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 23630a5bcf228..5b51bc823d166 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 4f6ca0fb68459..88458ebb747fe 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 464f47e20aa3b..990ab479d35c3 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 14041b2720877..acd38af383d67 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 91544500713d6..90dcb308de1b9 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 5ac49874dcf93..0af207d168fae 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 e29247494034f..66ba36b20b45c 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 900f20422b826..0625d2c3ec4b2 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 f9bc40ca52601..87219f4c0bdd3 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 d90b8f780b674..d22c7ae805827 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -7,7 +7,8 @@ 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 { ConfigPath } from '@kbn/config'; +import { EnvironmentMode } from '@kbn/config'; import { EuiBreadcrumb } from '@elastic/eui'; import { EuiButtonEmptyProps } from '@elastic/eui'; import { EuiConfirmModalProps } from '@elastic/eui'; @@ -20,17 +21,18 @@ import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType } from 'src/core/server/kibana_config'; import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; +import { Logger } from '@kbn/logging'; +import { LogMeta } from '@kbn/logging'; import { MaybePromise } from '@kbn/utility-types'; import { Observable } from 'rxjs'; -import { ParsedQuery } from 'query-string'; +import { PackageInfo } from '@kbn/config'; 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 +189,6 @@ export type AppUpdatableFields = Pick Partial | undefined; -// @public -export function assertNever(x: never): never; - // @public export interface Capabilities { [key: string]: Record>; @@ -272,10 +271,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 +347,6 @@ export interface ChromeStart { getHelpExtension$(): Observable; getIsNavDrawerLocked$(): Observable; getIsVisible$(): Observable; - getNavType$(): Observable; navControls: ChromeNavControls; navLinks: ChromeNavLinks; recentlyAccessed: ChromeRecentlyAccessed; @@ -444,9 +445,6 @@ export class CoreSystem { stop(): void; } -// @public -export function deepFreeze(object: T): RecursiveReadonly; - // @internal (undocumented) export const DEFAULT_APP_CATEGORIES: Readonly<{ kibana: { @@ -583,15 +581,7 @@ export interface DocLinksStart { }; } -// @public (undocumented) -export interface EnvironmentMode { - // (undocumented) - dev: boolean; - // (undocumented) - name: 'development' | 'production'; - // (undocumented) - prod: boolean; -} +export { EnvironmentMode } // @public export interface ErrorToastOptions extends ToastOptions { @@ -616,16 +606,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 +817,6 @@ export interface ImageValidation { }; } -// @public -export function isRelativeUrl(candidatePath: string): boolean; - // @public export type IToasts = Pick; @@ -868,9 +845,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; @@ -935,19 +909,7 @@ export interface OverlayStart { openModal: OverlayModalStart['open']; } -// @public (undocumented) -export interface PackageInfo { - // (undocumented) - branch: string; - // (undocumented) - buildNum: number; - // (undocumented) - buildSha: string; - // (undocumented) - dist: boolean; - // (undocumented) - version: string; -} +export { PackageInfo } // @public export interface Plugin { @@ -1447,26 +1409,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 211e9c03beea5..b806ac270331d 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 bb4723e69ab5e..803a7dae91a40 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 351020004b0e7..5a8949ca2f55f 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 427c6b7735435..bfb07c1b51427 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 e1f7eeff93471..1921c26e57971 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 d63b3539e5cdc..4c9c064840750 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/bootstrap.ts b/src/core/server/bootstrap.ts index c0cbe3a39ff54..ff1a5c0340c46 100644 --- a/src/core/server/bootstrap.ts +++ b/src/core/server/bootstrap.ts @@ -60,7 +60,15 @@ export async function bootstrap({ return; } - const env = Env.createDefault({ + // `bootstrap` is exported from the `src/core/server/index` module, + // meaning that any test importing, implicitly or explicitly, anything concrete + // from `core/server` will load `dev-utils`. As some tests are mocking the `fs` package, + // and as `REPO_ROOT` is initialized on the fly when importing `dev-utils` and requires + // the `fs` package, it causes failures. This is why we use a dynamic `require` here. + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { REPO_ROOT } = require('@kbn/utils'); + + const env = Env.createDefault(REPO_ROOT, { configs, cliArgs, isDevClusterMaster: isMaster && cliArgs.dev && features.isClusterModeSupported, diff --git a/src/core/server/capabilities/capabilities_service.mock.ts b/src/core/server/capabilities/capabilities_service.mock.ts index 3f31eca8339d8..7d134f9592dc7 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/capabilities/integration_tests/capabilities_service.test.ts b/src/core/server/capabilities/integration_tests/capabilities_service.test.ts index b4d620965b047..17f2c77bbf660 100644 --- a/src/core/server/capabilities/integration_tests/capabilities_service.test.ts +++ b/src/core/server/capabilities/integration_tests/capabilities_service.test.ts @@ -18,16 +18,18 @@ */ import supertest from 'supertest'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { HttpService, InternalHttpServiceSetup } from '../../http'; import { contextServiceMock } from '../../context/context_service.mock'; import { loggingSystemMock } from '../../logging/logging_system.mock'; import { Env } from '../../config'; -import { getEnvOptions } from '../../config/__mocks__/env'; +import { getEnvOptions } from '../../config/mocks'; import { CapabilitiesService, CapabilitiesSetup } from '..'; import { createHttpServer } from '../../http/test_utils'; const coreId = Symbol('core'); -const env = Env.createDefault(getEnvOptions()); + +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); describe('CapabilitiesService', () => { let server: HttpService; diff --git a/src/core/server/config/deprecation/core_deprecations.test.ts b/src/core/server/config/deprecation/core_deprecations.test.ts index adf0f52339366..7a69dc2fa726e 100644 --- a/src/core/server/config/deprecation/core_deprecations.test.ts +++ b/src/core/server/config/deprecation/core_deprecations.test.ts @@ -17,9 +17,8 @@ * under the License. */ +import { configDeprecationFactory, applyDeprecations } from '@kbn/config'; import { coreDeprecationProvider } from './core_deprecations'; -import { configDeprecationFactory } from './deprecation_factory'; -import { applyDeprecations } from './apply_deprecations'; const initialEnv = { ...process.env }; diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 2b8b8e383da24..2701edcf44e03 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -18,7 +18,7 @@ */ import { has, get } from 'lodash'; -import { ConfigDeprecationProvider, ConfigDeprecation } from './types'; +import { ConfigDeprecationProvider, ConfigDeprecation } from '@kbn/config'; const configPathDeprecation: ConfigDeprecation = (settings, fromPath, log) => { if (has(process.env, 'CONFIG_PATH')) { diff --git a/src/core/server/config/deprecation/index.ts b/src/core/server/config/deprecation/index.ts index f79338665166b..9e8e2e5a2bddf 100644 --- a/src/core/server/config/deprecation/index.ts +++ b/src/core/server/config/deprecation/index.ts @@ -17,13 +17,4 @@ * under the License. */ -export { - ConfigDeprecation, - ConfigDeprecationWithContext, - ConfigDeprecationLogger, - ConfigDeprecationFactory, - ConfigDeprecationProvider, -} from './types'; -export { configDeprecationFactory } from './deprecation_factory'; export { coreDeprecationProvider } from './core_deprecations'; -export { applyDeprecations } from './apply_deprecations'; diff --git a/src/core/server/config/index.ts b/src/core/server/config/index.ts index 04dc402d35b22..f69a5d081cc8a 100644 --- a/src/core/server/config/index.ts +++ b/src/core/server/config/index.ts @@ -17,17 +17,25 @@ * under the License. */ -export { ConfigService, IConfigService } from './config_service'; -export { RawConfigService, RawConfigurationProvider } from './raw_config_service'; -export { Config, ConfigPath, isConfigPath, hasConfigPathIntersection } from './config'; -export { ObjectToConfigAdapter } from './object_to_config_adapter'; -export { CliArgs, Env } from './env'; +export { coreDeprecationProvider } from './deprecation'; + export { + ConfigService, + IConfigService, + RawConfigService, + RawConfigurationProvider, + Config, + ConfigPath, + isConfigPath, + hasConfigPathIntersection, + ObjectToConfigAdapter, + CliArgs, + Env, ConfigDeprecation, ConfigDeprecationLogger, ConfigDeprecationProvider, ConfigDeprecationFactory, - coreDeprecationProvider, -} from './deprecation'; - -export { EnvironmentMode, PackageInfo } from './types'; + EnvironmentMode, + PackageInfo, + LegacyObjectToConfigAdapter, +} from '@kbn/config'; diff --git a/src/core/server/config/mocks.ts b/src/core/server/config/mocks.ts new file mode 100644 index 0000000000000..960dda496e635 --- /dev/null +++ b/src/core/server/config/mocks.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { + getEnvOptions, + rawConfigServiceMock, + configServiceMock, + configMock, +} from '@kbn/config/target/mocks'; diff --git a/src/core/server/context/context_service.mock.ts b/src/core/server/context/context_service.mock.ts index eb55ced69dc04..24e0d52100bb1 100644 --- a/src/core/server/context/context_service.mock.ts +++ b/src/core/server/context/context_service.mock.ts @@ -16,13 +16,14 @@ * 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'; -const createSetupContractMock = () => { +const createSetupContractMock = (mockContext = {}) => { const setupContract: jest.Mocked = { - createContextContainer: jest.fn().mockImplementation(() => contextMock.create()), + createContextContainer: jest.fn().mockImplementation(() => contextMock.create(mockContext)), }; return setupContract; }; diff --git a/src/core/server/core_context.mock.ts b/src/core/server/core_context.mock.ts index f870d30528df4..bbf04783278f7 100644 --- a/src/core/server/core_context.mock.ts +++ b/src/core/server/core_context.mock.ts @@ -17,15 +17,15 @@ * under the License. */ +import { REPO_ROOT } from '@kbn/dev-utils'; import { CoreContext } from './core_context'; -import { getEnvOptions } from './config/__mocks__/env'; import { Env, IConfigService } from './config'; +import { configServiceMock, getEnvOptions } from './config/mocks'; import { loggingSystemMock } from './logging/logging_system.mock'; -import { configServiceMock } from './config/config_service.mock'; import { ILoggingSystem } from './logging'; function create({ - env = Env.createDefault(getEnvOptions()), + env = Env.createDefault(REPO_ROOT, getEnvOptions()), logger = loggingSystemMock.create(), configService = configServiceMock.create(), }: { diff --git a/src/core/server/elasticsearch/client/configure_client.test.ts b/src/core/server/elasticsearch/client/configure_client.test.ts index 716e2fd98a5e1..250cfc18a757d 100644 --- a/src/core/server/elasticsearch/client/configure_client.test.ts +++ b/src/core/server/elasticsearch/client/configure_client.test.ts @@ -16,9 +16,11 @@ * specific language governing permissions and limitations * under the License. */ +import { Buffer } from 'buffer'; +import { Readable } from 'stream'; import { RequestEvent, errors } from '@elastic/elasticsearch'; -import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport'; +import { TransportRequestParams, RequestBody } from '@elastic/elasticsearch/lib/Transport'; import { parseClientOptionsMock, ClientMock } from './configure_client.test.mocks'; import { loggingSystemMock } from '../../logging/logging_system.mock'; @@ -195,40 +197,184 @@ describe('configureClient', () => { `); }); - it('logs each queries if `logQueries` is true', () => { - const client = configureClient( - createFakeConfig({ - logQueries: true, - }), - { logger, scoped: false } - ); + describe('logs each queries if `logQueries` is true', () => { + function createResponseWithBody(body?: RequestBody) { + return createApiResponse({ + body: {}, + statusCode: 200, + params: { + method: 'GET', + path: '/foo', + querystring: { hello: 'dolly' }, + body, + }, + }); + } + + it('when request body is an object', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody({ + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` + Array [ + Array [ + "200 + GET /foo?hello=dolly + {\\"seq_no_primary_term\\":true,\\"query\\":{\\"term\\":{\\"user\\":\\"kimchy\\"}}}", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); + }); - const response = createApiResponse({ - body: {}, - statusCode: 200, - params: { - method: 'GET', - path: '/foo', - querystring: { hello: 'dolly' }, - }, + it('when request body is a string', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody( + JSON.stringify({ + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }) + ); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` + Array [ + Array [ + "200 + GET /foo?hello=dolly + {\\"seq_no_primary_term\\":true,\\"query\\":{\\"term\\":{\\"user\\":\\"kimchy\\"}}}", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); }); - client.emit('response', null, response); + it('when request body is a buffer', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody( + Buffer.from( + JSON.stringify({ + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }) + ) + ); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` + Array [ + Array [ + "200 + GET /foo?hello=dolly + [buffer]", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); + }); - expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` - Array [ + it('when request body is a readable stream', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody( + // @ts-expect-error definition doesn't know about from + Readable.from( + JSON.stringify({ + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }) + ) + ); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` Array [ - "200 - GET /foo - hello=dolly", - Object { - "tags": Array [ - "query", - ], - }, - ], - ] - `); + Array [ + "200 + GET /foo?hello=dolly + [stream]", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); + }); + + it('when request body is not defined', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody(); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` + Array [ + Array [ + "200 + GET /foo?hello=dolly", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); + }); }); it('properly encode queries', () => { @@ -255,8 +401,7 @@ describe('configureClient', () => { Array [ Array [ "200 - GET /foo - city=M%C3%BCnich", + GET /foo?city=M%C3%BCnich", Object { "tags": Array [ "query", @@ -286,6 +431,12 @@ describe('configureClient', () => { method: 'GET', path: '/foo', querystring: { hello: 'dolly' }, + body: { + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }, }, }); client.emit('response', new errors.ResponseError(response), response); @@ -294,8 +445,8 @@ describe('configureClient', () => { Array [ Array [ "500 - GET /foo - hello=dolly", + GET /foo?hello=dolly + {\\"seq_no_primary_term\\":true,\\"query\\":{\\"term\\":{\\"user\\":\\"kimchy\\"}}}", Object { "tags": Array [ "query", diff --git a/src/core/server/elasticsearch/client/configure_client.ts b/src/core/server/elasticsearch/client/configure_client.ts index a777344813068..bf07ea604d228 100644 --- a/src/core/server/elasticsearch/client/configure_client.ts +++ b/src/core/server/elasticsearch/client/configure_client.ts @@ -16,9 +16,11 @@ * specific language governing permissions and limitations * under the License. */ - +import { Buffer } from 'buffer'; import { stringify } from 'querystring'; import { Client } from '@elastic/elasticsearch'; +import { RequestBody } from '@elastic/elasticsearch/lib/Transport'; + import { Logger } from '../../logging'; import { parseClientOptions, ElasticsearchClientConfig } from './client_config'; @@ -48,15 +50,11 @@ const addLogging = (client: Client, logger: Logger, logQueries: boolean) => { // definition is wrong, `params.querystring` can be either a string or an object const querystring = convertQueryString(params.querystring); - - logger.debug( - `${event.statusCode}\n${params.method} ${params.path}${ - querystring ? `\n${querystring}` : '' - }`, - { - tags: ['query'], - } - ); + const url = `${params.path}${querystring ? `?${querystring}` : ''}`; + const body = params.body ? `\n${ensureString(params.body)}` : ''; + logger.debug(`${event.statusCode}\n${params.method} ${url}${body}`, { + tags: ['query'], + }); } }); }; @@ -67,3 +65,10 @@ const convertQueryString = (qs: string | Record | undefined): strin } return stringify(qs); }; + +function ensureString(body: RequestBody): string { + if (typeof body === 'string') return body; + if (Buffer.isBuffer(body)) return '[buffer]'; + if ('readable' in body && body.readable && typeof body._read === 'function') return '[stream]'; + return JSON.stringify(body); +} diff --git a/src/core/server/elasticsearch/client/mocks.ts b/src/core/server/elasticsearch/client/mocks.ts index 2f2ca08fee6f2..6fb3dc090bfb4 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_config.test.ts b/src/core/server/elasticsearch/elasticsearch_config.test.ts index 648eaaf8a97f2..3912c68836bb2 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.test.ts @@ -23,8 +23,8 @@ import { mockReadPkcs12Truststore, } from './elasticsearch_config.test.mocks'; +import { applyDeprecations, configDeprecationFactory } from '@kbn/config'; import { ElasticsearchConfig, config } from './elasticsearch_config'; -import { applyDeprecations, configDeprecationFactory } from '../config/deprecation'; const CONFIG_PATH = 'elasticsearch'; diff --git a/src/core/server/elasticsearch/elasticsearch_service.mock.ts b/src/core/server/elasticsearch/elasticsearch_service.mock.ts index 26186efc286bf..ad80928d2fe5e 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.test.ts b/src/core/server/elasticsearch/elasticsearch_service.test.ts index 49f5c8dd98790..ce82410f6061e 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.test.ts @@ -20,10 +20,10 @@ import { MockLegacyClusterClient, MockClusterClient } from './elasticsearch_service.test.mocks'; import { BehaviorSubject } from 'rxjs'; import { first } from 'rxjs/operators'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; +import { configServiceMock, getEnvOptions } from '../config/mocks'; import { CoreContext } from '../core_context'; -import { configServiceMock } from '../config/config_service.mock'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { httpServiceMock } from '../http/http_service.mock'; import { auditTrailServiceMock } from '../audit_trail/audit_trail_service.mock'; @@ -64,7 +64,7 @@ let mockClusterClientInstance: ReturnType; beforeEach(() => { - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); coreContext = { coreId: Symbol(), env, logger, configService: configService as any }; elasticsearchService = new ElasticsearchService(coreContext); diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index 2cc065aaaaeb1..5d07840e8bda7 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 3dbc1c2c647a9..6896c0a2e301f 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/elasticsearch/version_check/ensure_es_version.ts b/src/core/server/elasticsearch/version_check/ensure_es_version.ts index 5f926215d167f..70ff8857117de 100644 --- a/src/core/server/elasticsearch/version_check/ensure_es_version.ts +++ b/src/core/server/elasticsearch/version_check/ensure_es_version.ts @@ -72,7 +72,7 @@ export function mapNodesVersionCompatibility( kibanaVersion: string, ignoreVersionMismatch: boolean ): NodesVersionCompatibility { - if (Object.keys(nodesInfo.nodes).length === 0) { + if (Object.keys(nodesInfo.nodes ?? {}).length === 0) { return { isCompatible: false, message: 'Unable to retrieve version information from Elasticsearch nodes.', diff --git a/src/core/server/environment/create_data_folder.test.ts b/src/core/server/environment/create_data_folder.test.ts index 2a480a7a3954f..25c1d57996e0d 100644 --- a/src/core/server/environment/create_data_folder.test.ts +++ b/src/core/server/environment/create_data_folder.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { PathConfigType } from '../path'; +import { PathConfigType } from '@kbn/utils'; import { createDataFolder } from './create_data_folder'; import { mkdir } from './fs'; import { loggingSystemMock } from '../logging/logging_system.mock'; diff --git a/src/core/server/environment/create_data_folder.ts b/src/core/server/environment/create_data_folder.ts index 641d95cbf9411..f90e9f93694eb 100644 --- a/src/core/server/environment/create_data_folder.ts +++ b/src/core/server/environment/create_data_folder.ts @@ -17,9 +17,9 @@ * under the License. */ +import { PathConfigType } from '@kbn/utils'; import { mkdir } from './fs'; import { Logger } from '../logging'; -import { PathConfigType } from '../path'; export async function createDataFolder({ pathConfig, diff --git a/src/core/server/environment/environment_service.mock.ts b/src/core/server/environment/environment_service.mock.ts index 8bf726b4a6388..a956e369ba4a7 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 06fd250ebe4f9..e4dcfc49c1130 100644 --- a/src/core/server/environment/environment_service.test.ts +++ b/src/core/server/environment/environment_service.test.ts @@ -18,12 +18,14 @@ */ 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'; +import { configServiceMock } from '../config/mocks'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { mockCoreContext } from '../core_context.mock'; @@ -35,12 +37,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 +61,9 @@ const getConfigService = () => { if (path === 'server') { return new BehaviorSubject(serverConfig); } + if (path === 'pid') { + return new BehaviorSubject(pidConfig); + } return new BehaviorSubject({}); }); return configService; @@ -76,7 +89,7 @@ describe('UuidService', () => { expect(resolveInstanceUuid).toHaveBeenCalledWith({ pathConfig, serverConfig, - logger: logger.get('uuid'), + logger: logger.get('environment'), }); }); @@ -86,7 +99,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 6a0b1122c7053..b051dfbe4c7f2 100644 --- a/src/core/server/environment/environment_service.ts +++ b/src/core/server/environment/environment_service.ts @@ -18,13 +18,15 @@ */ import { take } from 'rxjs/operators'; +import { PathConfigType, config as pathConfigDef } from '@kbn/utils'; import { CoreContext } from '../core_context'; 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 dc040ccb73615..b79c70dbee280 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 57a26d5ea3c79..92b57c6af2fff 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 0000000000000..ee9963016717e --- /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/resolve_uuid.test.ts b/src/core/server/environment/resolve_uuid.test.ts index d162c9d8e364b..c6e8465507123 100644 --- a/src/core/server/environment/resolve_uuid.test.ts +++ b/src/core/server/environment/resolve_uuid.test.ts @@ -18,10 +18,10 @@ */ import { join } from 'path'; +import { PathConfigType } from '@kbn/utils'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { readFile, writeFile } from './fs'; import { resolveInstanceUuid, UUID_7_6_0_BUG } from './resolve_uuid'; -import { PathConfigType } from '../path'; import { HttpConfigType } from '../http'; jest.mock('uuid', () => ({ diff --git a/src/core/server/environment/resolve_uuid.ts b/src/core/server/environment/resolve_uuid.ts index 0267e06939997..a640319e30af4 100644 --- a/src/core/server/environment/resolve_uuid.ts +++ b/src/core/server/environment/resolve_uuid.ts @@ -19,8 +19,8 @@ import uuid from 'uuid'; import { join } from 'path'; +import { PathConfigType } from '@kbn/utils'; import { readFile, writeFile } from './fs'; -import { PathConfigType } from '../path'; import { HttpConfigType } from '../http'; import { Logger } from '../logging'; 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 0000000000000..f9eb78a486970 --- /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 0000000000000..6ee20af02d7b0 --- /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 093d73b2da3bf..059eb36f42dd5 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/cookie_session_storage.test.ts b/src/core/server/http/cookie_session_storage.test.ts index 8e5dec7d4eadd..b7ade0cbde0fc 100644 --- a/src/core/server/http/cookie_session_storage.test.ts +++ b/src/core/server/http/cookie_session_storage.test.ts @@ -18,20 +18,20 @@ */ import request from 'request'; import supertest from 'supertest'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { ByteSizeValue } from '@kbn/config-schema'; import { BehaviorSubject } from 'rxjs'; import { CoreContext } from '../core_context'; import { HttpService } from './http_service'; import { KibanaRequest } from './router'; - import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; -import { configServiceMock } from '../config/config_service.mock'; + import { contextServiceMock } from '../context/context_service.mock'; import { loggingSystemMock } from '../logging/logging_system.mock'; - +import { getEnvOptions, configServiceMock } from '../config/mocks'; import { httpServerMock } from './http_server.mocks'; + import { createCookieSessionStorageFactory } from './cookie_session_storage'; let server: HttpService; @@ -72,7 +72,7 @@ configService.atPath.mockReturnValue( beforeEach(() => { logger = loggingSystemMock.create(); - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); coreContext = { coreId: Symbol(), env, logger, configService: configService as any }; server = new HttpService(coreContext); diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 676cee1954c59..f81708145edc4 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -18,9 +18,10 @@ */ 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'; import { InternalHttpServiceSetup, HttpServiceSetup, @@ -35,6 +36,7 @@ import { sessionStorageMock } from './cookie_session_storage.mocks'; import { OnPostAuthToolkit } from './lifecycle/on_post_auth'; import { OnPreAuthToolkit } from './lifecycle/on_pre_auth'; import { OnPreResponseToolkit } from './lifecycle/on_pre_response'; +import { configMock } from '../config/mocks'; type BasePathMocked = jest.Mocked; type AuthMocked = jest.Mocked; diff --git a/src/core/server/http/http_service.test.ts b/src/core/server/http/http_service.test.ts index 3d759b427d9fb..11cea88fa0dd2 100644 --- a/src/core/server/http/http_service.test.ts +++ b/src/core/server/http/http_service.test.ts @@ -21,17 +21,18 @@ import { mockHttpServer } from './http_service.test.mocks'; import { noop } from 'lodash'; import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import { getEnvOptions } from '../config/mocks'; import { HttpService } from '.'; import { HttpConfigType, config } from './http_config'; import { httpServerMock } from './http_server.mocks'; import { ConfigService, Env } from '../config'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { contextServiceMock } from '../context/context_service.mock'; -import { getEnvOptions } from '../config/__mocks__/env'; import { config as cspConfig } from '../csp'; const logger = loggingSystemMock.create(); -const env = Env.createDefault(getEnvOptions()); +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); const coreId = Symbol(); const createConfigService = (value: Partial = {}) => { @@ -115,7 +116,7 @@ test('spins up notReady server until started if configured with `autoListen:true const service = new HttpService({ coreId, configService, - env: new Env('.', getEnvOptions()), + env: Env.createDefault(REPO_ROOT, getEnvOptions()), logger, }); @@ -263,7 +264,7 @@ test('does not start http server if process is dev cluster master', async () => const service = new HttpService({ coreId, configService, - env: new Env('.', getEnvOptions({ isDevClusterMaster: true })), + env: Env.createDefault(REPO_ROOT, getEnvOptions({ isDevClusterMaster: true })), logger, }); @@ -288,7 +289,7 @@ test('does not start http server if configured with `autoListen:false`', async ( const service = new HttpService({ coreId, configService, - env: new Env('.', getEnvOptions()), + env: Env.createDefault(REPO_ROOT, getEnvOptions()), logger, }); diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index c2fd653918171..82b141c8e50dd 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 a1401ba73813b..a964130550bf5 100644 --- a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts +++ b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts @@ -20,16 +20,18 @@ 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'; import { HttpServerSetup } from '../http_server'; import { IRouter, RouteRegistrar } from '../router'; -import { configServiceMock } from '../../config/config_service.mock'; +import { configServiceMock } from '../../config/mocks'; 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 f27f5e937b2c0..498dd153a43dd 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 76f8761a7e998..e04f8585981b5 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/http/test_utils.ts b/src/core/server/http/test_utils.ts index c3afae108027e..b4d91926f13f4 100644 --- a/src/core/server/http/test_utils.ts +++ b/src/core/server/http/test_utils.ts @@ -18,16 +18,16 @@ */ import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { ByteSizeValue } from '@kbn/config-schema'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; import { HttpService } from './http_service'; import { CoreContext } from '../core_context'; -import { configServiceMock } from '../config/config_service.mock'; +import { getEnvOptions, configServiceMock } from '../config/mocks'; import { loggingSystemMock } from '../logging/logging_system.mock'; const coreId = Symbol('core'); -const env = Env.createDefault(getEnvOptions()); +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); const logger = loggingSystemMock.create(); diff --git a/src/core/server/index.ts b/src/core/server/index.ts index d127471348d9f..24d1fc9d369f2 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -60,7 +60,7 @@ import { SavedObjectsServiceStart, } from './saved_objects'; import { CapabilitiesSetup, CapabilitiesStart } from './capabilities'; -import { MetricsServiceStart } from './metrics'; +import { MetricsServiceSetup, MetricsServiceStart } from './metrics'; import { StatusServiceSetup } from './status'; import { Auditor, AuditTrailSetup, AuditTrailStart } from './audit_trail'; import { AppenderConfigType, appendersSchema, LoggingServiceSetup } from './logging'; @@ -320,18 +320,10 @@ export { OpsServerMetrics, OpsProcessMetrics, MetricsServiceSetup, + MetricsServiceStart, } from './metrics'; -export { - DEFAULT_APP_CATEGORIES, - getFlattenedObject, - URLMeaningfulParts, - modifyUrl, - isRelativeUrl, - Freezable, - deepFreeze, - assertNever, -} from '../utils'; +export { DEFAULT_APP_CATEGORIES } from '../utils'; export { SavedObject, @@ -423,6 +415,8 @@ export interface CoreSetup = KbnServer as any; @@ -68,7 +69,7 @@ let environmentSetup: ReturnType { coreId = Symbol(); - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); configService = configServiceMock.create(); environmentSetup = environmentServiceMock.createSetupContract(); @@ -99,6 +100,7 @@ beforeEach(() => { status: statusServiceMock.createInternalSetupContract(), auditTrail: auditTrailServiceMock.createSetupContract(), logging: loggingServiceMock.createInternalSetupContract(), + metrics: metricsServiceMock.createInternalSetupContract(), }, plugins: { 'plugin-id': 'plugin-value' }, uiPlugins: { @@ -363,6 +365,7 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => { const devClusterLegacyService = new LegacyService({ coreId, env: Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { silent: true, basePath: false }, isDevClusterMaster: true, @@ -391,6 +394,7 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => { const devClusterLegacyService = new LegacyService({ coreId, env: Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { quiet: true, basePath: true }, isDevClusterMaster: true, diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 6e6d5cfc24340..4dc22be2a9971 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -16,10 +16,11 @@ * 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'; +import { PathConfigType } from '@kbn/utils'; import { CoreService } from '../../types'; import { Config } from '../config'; import { CoreContext } from '../core_context'; @@ -27,7 +28,6 @@ import { CspConfigType, config as cspConfig } from '../csp'; import { DevConfig, DevConfigType, config as devConfig } from '../dev'; import { BasePathProxyServer, HttpConfig, HttpConfigType, config as httpConfig } from '../http'; import { Logger } from '../logging'; -import { PathConfigType } from '../path'; import { findLegacyPluginSpecs, logLegacyThirdPartyPluginDeprecationWarning } from './plugins'; import { ILegacyInternals, @@ -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, @@ -302,6 +302,10 @@ export class LegacyService implements CoreService { logging: { configure: (config$) => setupDeps.core.logging.configure([], config$), }, + metrics: { + collectionInterval: setupDeps.core.metrics.collectionInterval, + getOpsMetrics$: setupDeps.core.metrics.getOpsMetrics$, + }, savedObjects: { setClientFactoryProvider: setupDeps.core.savedObjects.setClientFactoryProvider, addClientWrapper: setupDeps.core.savedObjects.addClientWrapper, @@ -309,6 +313,7 @@ export class LegacyService implements CoreService { getImportExportObjectLimit: setupDeps.core.savedObjects.getImportExportObjectLimit, }, status: { + isStatusPageAnonymous: setupDeps.core.status.isStatusPageAnonymous, core$: setupDeps.core.status.core$, overall$: setupDeps.core.status.overall$, set: () => { @@ -368,7 +373,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/logging/appenders/legacy_appender.test.ts b/src/core/server/legacy/logging/appenders/legacy_appender.test.ts index 538d987e781d0..697e5bc37d602 100644 --- a/src/core/server/legacy/logging/appenders/legacy_appender.test.ts +++ b/src/core/server/legacy/logging/appenders/legacy_appender.test.ts @@ -19,8 +19,7 @@ jest.mock('../legacy_logging_server'); -import { LogLevel } from '../../../logging/log_level'; -import { LogRecord } from '../../../logging/log_record'; +import { LogRecord, LogLevel } from '../../../logging'; import { LegacyLoggingServer } from '../legacy_logging_server'; import { LegacyAppender } from './legacy_appender'; diff --git a/src/core/server/legacy/logging/appenders/legacy_appender.ts b/src/core/server/legacy/logging/appenders/legacy_appender.ts index a5d36423ba4c6..67337c7d67629 100644 --- a/src/core/server/legacy/logging/appenders/legacy_appender.ts +++ b/src/core/server/legacy/logging/appenders/legacy_appender.ts @@ -18,8 +18,7 @@ */ import { schema } from '@kbn/config-schema'; -import { DisposableAppender } from '../../../logging/appenders/appenders'; -import { LogRecord } from '../../../logging/log_record'; +import { DisposableAppender, LogRecord } from '../../../logging'; import { LegacyLoggingServer } from '../legacy_logging_server'; import { LegacyVars } from '../../types'; diff --git a/src/core/server/legacy/logging/legacy_logging_server.test.ts b/src/core/server/legacy/logging/legacy_logging_server.test.ts index 6dca3a199728e..2f6c34e0fc5d6 100644 --- a/src/core/server/legacy/logging/legacy_logging_server.test.ts +++ b/src/core/server/legacy/logging/legacy_logging_server.test.ts @@ -20,7 +20,7 @@ jest.mock('../../../../legacy/server/config'); jest.mock('../../../../legacy/server/logging'); -import { LogLevel } from '../../logging/log_level'; +import { LogLevel } from '../../logging'; import { LegacyLoggingServer } from './legacy_logging_server'; test('correctly forwards log records.', () => { diff --git a/src/core/server/legacy/logging/legacy_logging_server.ts b/src/core/server/legacy/logging/legacy_logging_server.ts index 4a7fea87cf69f..096dbe54565e1 100644 --- a/src/core/server/legacy/logging/legacy_logging_server.ts +++ b/src/core/server/legacy/logging/legacy_logging_server.ts @@ -23,8 +23,7 @@ import Podium from 'podium'; import { Config } from '../../../../legacy/server/config'; // @ts-expect-error: implicit any for JS file import { setupLogging } from '../../../../legacy/server/logging'; -import { LogLevel } from '../../logging/log_level'; -import { LogRecord } from '../../logging/log_record'; +import { LogLevel, LogRecord } from '../../logging'; import { LegacyVars } from '../../types'; export const metadataSymbol = Symbol('log message with metadata'); 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 0000000000000..842ab554d79d1 --- /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 82e04496ffc3e..cb4277b130a88 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 edfce4988275a..4e6920c50686c 100644 --- a/src/core/server/logging/appenders/appenders.ts +++ b/src/core/server/logging/appenders/appenders.ts @@ -18,14 +18,14 @@ */ import { schema } from '@kbn/config-schema'; +import { assertNever } from '@kbn/std'; +import { DisposableAppender } from '@kbn/logging'; -import { assertNever } from '../../../utils'; import { LegacyAppender, LegacyAppenderConfig, } from '../../legacy/logging/appenders/legacy_appender'; import { Layouts } from '../layouts/layouts'; -import { LogRecord } from '../log_record'; import { ConsoleAppender, ConsoleAppenderConfig } from './console/console_appender'; import { FileAppender, FileAppenderConfig } from './file/file_appender'; @@ -44,25 +44,6 @@ export const appendersSchema = schema.oneOf([ /** @public */ export type AppenderConfigType = ConsoleAppenderConfig | FileAppenderConfig | LegacyAppenderConfig; -/** - * Entity that can append `LogRecord` instances to file, stdout, memory or whatever - * is implemented internally. It's supposed to be used by `Logger`. - * @internal - */ -export interface Appender { - append(record: LogRecord): void; -} - -/** - * This interface should be additionally implemented by the `Appender`'s if they are supposed - * to be properly disposed. It's intentionally separated from `Appender` interface so that `Logger` - * that interacts with `Appender` doesn't have control over appender lifetime. - * @internal - */ -export interface DisposableAppender extends Appender { - dispose: () => void; -} - /** @internal */ export class Appenders { public static configSchema = appendersSchema; diff --git a/src/core/server/logging/appenders/buffer/buffer_appender.test.ts b/src/core/server/logging/appenders/buffer/buffer_appender.test.ts index 49d70db8d5d43..7981aef64e589 100644 --- a/src/core/server/logging/appenders/buffer/buffer_appender.test.ts +++ b/src/core/server/logging/appenders/buffer/buffer_appender.test.ts @@ -17,8 +17,7 @@ * under the License. */ -import { LogLevel } from '../../log_level'; -import { LogRecord } from '../../log_record'; +import { LogLevel, LogRecord } from '@kbn/logging'; import { BufferAppender } from './buffer_appender'; test('`flush()` does not return any record buffered at the beginning.', () => { diff --git a/src/core/server/logging/appenders/buffer/buffer_appender.ts b/src/core/server/logging/appenders/buffer/buffer_appender.ts index 7024d3e5d16df..9e3a9d0f910f8 100644 --- a/src/core/server/logging/appenders/buffer/buffer_appender.ts +++ b/src/core/server/logging/appenders/buffer/buffer_appender.ts @@ -17,8 +17,7 @@ * under the License. */ -import { LogRecord } from '../../log_record'; -import { DisposableAppender } from '../appenders'; +import { LogRecord, DisposableAppender } from '@kbn/logging'; /** * Simple appender that just buffers `LogRecord` instances it receives. It is a *reserved* appender diff --git a/src/core/server/logging/appenders/console/console_appender.test.ts b/src/core/server/logging/appenders/console/console_appender.test.ts index 6e30df1cfb65c..0601ac10167ac 100644 --- a/src/core/server/logging/appenders/console/console_appender.test.ts +++ b/src/core/server/logging/appenders/console/console_appender.test.ts @@ -29,8 +29,7 @@ jest.mock('../../layouts/layouts', () => { }; }); -import { LogLevel } from '../../log_level'; -import { LogRecord } from '../../log_record'; +import { LogRecord, LogLevel } from '@kbn/logging'; import { ConsoleAppender } from './console_appender'; test('`configSchema` creates correct schema.', () => { diff --git a/src/core/server/logging/appenders/console/console_appender.ts b/src/core/server/logging/appenders/console/console_appender.ts index a54674b1d347c..dc491fcff664c 100644 --- a/src/core/server/logging/appenders/console/console_appender.ts +++ b/src/core/server/logging/appenders/console/console_appender.ts @@ -18,10 +18,8 @@ */ import { schema } from '@kbn/config-schema'; - -import { Layout, Layouts, LayoutConfigType } from '../../layouts/layouts'; -import { LogRecord } from '../../log_record'; -import { DisposableAppender } from '../appenders'; +import { Layout, LogRecord, DisposableAppender } from '@kbn/logging'; +import { Layouts, LayoutConfigType } from '../../layouts/layouts'; const { literal, object } = schema; diff --git a/src/core/server/logging/appenders/file/file_appender.test.ts b/src/core/server/logging/appenders/file/file_appender.test.ts index bff60029faf11..645455c5ae04c 100644 --- a/src/core/server/logging/appenders/file/file_appender.test.ts +++ b/src/core/server/logging/appenders/file/file_appender.test.ts @@ -19,8 +19,7 @@ import { mockCreateWriteStream } from './file_appender.test.mocks'; -import { LogLevel } from '../../log_level'; -import { LogRecord } from '../../log_record'; +import { LogRecord, LogLevel } from '@kbn/logging'; import { FileAppender } from './file_appender'; const tickMs = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/src/core/server/logging/appenders/file/file_appender.ts b/src/core/server/logging/appenders/file/file_appender.ts index a0e484cd87c8f..b1712bd4e9412 100644 --- a/src/core/server/logging/appenders/file/file_appender.ts +++ b/src/core/server/logging/appenders/file/file_appender.ts @@ -18,11 +18,10 @@ */ import { schema } from '@kbn/config-schema'; +import { LogRecord, Layout, DisposableAppender } from '@kbn/logging'; import { createWriteStream, WriteStream } from 'fs'; -import { Layout, Layouts, LayoutConfigType } from '../../layouts/layouts'; -import { LogRecord } from '../../log_record'; -import { DisposableAppender } from '../appenders'; +import { Layouts, LayoutConfigType } from '../../layouts/layouts'; export interface FileAppenderConfig { kind: 'file'; diff --git a/src/core/server/logging/index.ts b/src/core/server/logging/index.ts index 9471972030281..01f153cae9e2d 100644 --- a/src/core/server/logging/index.ts +++ b/src/core/server/logging/index.ts @@ -17,10 +17,17 @@ * under the License. */ -export { Logger, LogMeta } from './logger'; -export { LoggerFactory } from './logger_factory'; -export { LogRecord } from './log_record'; -export { LogLevel } from './log_level'; +export { + DisposableAppender, + Appender, + LogRecord, + Layout, + LoggerFactory, + LogMeta, + Logger, + LogLevelId, + LogLevel, +} from '@kbn/logging'; export { config, LoggingConfigType, diff --git a/src/core/server/logging/layouts/conversions/date.ts b/src/core/server/logging/layouts/conversions/date.ts index d3ed54fb98240..3a43c0ffcd389 100644 --- a/src/core/server/logging/layouts/conversions/date.ts +++ b/src/core/server/logging/layouts/conversions/date.ts @@ -18,9 +18,9 @@ */ import moment from 'moment-timezone'; import { last } from 'lodash'; +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; const dateRegExp = /%date({(?[^}]+)})?({(?[^}]+)})?/g; diff --git a/src/core/server/logging/layouts/conversions/level.ts b/src/core/server/logging/layouts/conversions/level.ts index 58b271140eff5..83208242dc258 100644 --- a/src/core/server/logging/layouts/conversions/level.ts +++ b/src/core/server/logging/layouts/conversions/level.ts @@ -18,10 +18,9 @@ */ import chalk from 'chalk'; +import { LogRecord, LogLevel } from '@kbn/logging'; import { Conversion } from './type'; -import { LogLevel } from '../../log_level'; -import { LogRecord } from '../../log_record'; const LEVEL_COLORS = new Map([ [LogLevel.Fatal, chalk.red], diff --git a/src/core/server/logging/layouts/conversions/logger.ts b/src/core/server/logging/layouts/conversions/logger.ts index debb1737ab95a..e63976052443b 100644 --- a/src/core/server/logging/layouts/conversions/logger.ts +++ b/src/core/server/logging/layouts/conversions/logger.ts @@ -18,9 +18,9 @@ */ import chalk from 'chalk'; +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; export const LoggerConversion: Conversion = { pattern: /%logger/g, diff --git a/src/core/server/logging/layouts/conversions/message.ts b/src/core/server/logging/layouts/conversions/message.ts index f8c5e68ada4fb..73d85532a5a90 100644 --- a/src/core/server/logging/layouts/conversions/message.ts +++ b/src/core/server/logging/layouts/conversions/message.ts @@ -17,8 +17,8 @@ * under the License. */ +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; export const MessageConversion: Conversion = { pattern: /%message/g, diff --git a/src/core/server/logging/layouts/conversions/meta.ts b/src/core/server/logging/layouts/conversions/meta.ts index ee8c207389fbe..b78db41b7e969 100644 --- a/src/core/server/logging/layouts/conversions/meta.ts +++ b/src/core/server/logging/layouts/conversions/meta.ts @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ + +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; export const MetaConversion: Conversion = { pattern: /%meta/g, diff --git a/src/core/server/logging/layouts/conversions/pid.ts b/src/core/server/logging/layouts/conversions/pid.ts index 37d34a4f1cf8b..f6902005f5668 100644 --- a/src/core/server/logging/layouts/conversions/pid.ts +++ b/src/core/server/logging/layouts/conversions/pid.ts @@ -17,8 +17,8 @@ * under the License. */ +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; export const PidConversion: Conversion = { pattern: /%pid/g, diff --git a/src/core/server/logging/layouts/conversions/type.ts b/src/core/server/logging/layouts/conversions/type.ts index a57a1f954e53a..be172a0a98f7d 100644 --- a/src/core/server/logging/layouts/conversions/type.ts +++ b/src/core/server/logging/layouts/conversions/type.ts @@ -16,7 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import { LogRecord } from 'kibana/server'; + +import { LogRecord } from '@kbn/logging'; export interface Conversion { pattern: RegExp; diff --git a/src/core/server/logging/layouts/json_layout.test.ts b/src/core/server/logging/layouts/json_layout.test.ts index 6cda1e4806aa8..f476e3f217278 100644 --- a/src/core/server/logging/layouts/json_layout.test.ts +++ b/src/core/server/logging/layouts/json_layout.test.ts @@ -17,8 +17,7 @@ * under the License. */ -import { LogLevel } from '../log_level'; -import { LogRecord } from '../log_record'; +import { LogLevel, LogRecord } from '@kbn/logging'; import { JsonLayout } from './json_layout'; const timestamp = new Date(Date.UTC(2012, 1, 1, 14, 30, 22, 11)); diff --git a/src/core/server/logging/layouts/json_layout.ts b/src/core/server/logging/layouts/json_layout.ts index 37eb6b8c4806e..7573d0b837416 100644 --- a/src/core/server/logging/layouts/json_layout.ts +++ b/src/core/server/logging/layouts/json_layout.ts @@ -20,9 +20,7 @@ import moment from 'moment-timezone'; import { merge } from 'lodash'; import { schema } from '@kbn/config-schema'; - -import { LogRecord } from '../log_record'; -import { Layout } from './layouts'; +import { LogRecord, Layout } from '@kbn/logging'; const { literal, object } = schema; diff --git a/src/core/server/logging/layouts/layouts.ts b/src/core/server/logging/layouts/layouts.ts index 124c007bae104..faf05e4d1e347 100644 --- a/src/core/server/logging/layouts/layouts.ts +++ b/src/core/server/logging/layouts/layouts.ts @@ -18,9 +18,9 @@ */ import { schema } from '@kbn/config-schema'; +import { Layout } from '@kbn/logging'; +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'; @@ -28,14 +28,6 @@ const { oneOf } = schema; export type LayoutConfigType = PatternLayoutConfigType | JsonLayoutConfigType; -/** - * Entity that can format `LogRecord` instance into a string. - * @internal - */ -export interface Layout { - format(record: LogRecord): string; -} - /** @internal */ export class Layouts { public static configSchema = oneOf([JsonLayout.configSchema, PatternLayout.configSchema]); diff --git a/src/core/server/logging/layouts/pattern_layout.test.ts b/src/core/server/logging/layouts/pattern_layout.test.ts index cce55b147e0ed..d37ee3c87d08f 100644 --- a/src/core/server/logging/layouts/pattern_layout.test.ts +++ b/src/core/server/logging/layouts/pattern_layout.test.ts @@ -17,9 +17,8 @@ * under the License. */ +import { LogLevel, LogRecord } from '@kbn/logging'; import { stripAnsiSnapshotSerializer } from '../../../test_helpers/strip_ansi_snapshot_serializer'; -import { LogLevel } from '../log_level'; -import { LogRecord } from '../log_record'; import { PatternLayout, patternSchema } from './pattern_layout'; const timestamp = new Date(Date.UTC(2012, 1, 1, 14, 30, 22, 11)); diff --git a/src/core/server/logging/layouts/pattern_layout.ts b/src/core/server/logging/layouts/pattern_layout.ts index 5dfc8aca77f18..2ca444f54b499 100644 --- a/src/core/server/logging/layouts/pattern_layout.ts +++ b/src/core/server/logging/layouts/pattern_layout.ts @@ -18,9 +18,8 @@ */ import { schema } from '@kbn/config-schema'; +import { LogRecord, Layout } from '@kbn/logging'; -import { LogRecord } from '../log_record'; -import { Layout } from './layouts'; import { Conversion, LoggerConversion, diff --git a/src/core/server/logging/logger.mock.ts b/src/core/server/logging/logger.mock.ts index a3bb07ea4c095..5af967ecd430e 100644 --- a/src/core/server/logging/logger.mock.ts +++ b/src/core/server/logging/logger.mock.ts @@ -17,30 +17,4 @@ * under the License. */ -import { Logger } from './logger'; - -export type MockedLogger = jest.Mocked & { context: string[] }; - -const createLoggerMock = (context: string[] = []) => { - const mockLog: MockedLogger = { - context, - debug: jest.fn(), - error: jest.fn(), - fatal: jest.fn(), - info: jest.fn(), - log: jest.fn(), - trace: jest.fn(), - warn: jest.fn(), - get: jest.fn(), - }; - mockLog.get.mockImplementation((...ctx) => ({ - ctx, - ...mockLog, - })); - - return mockLog; -}; - -export const loggerMock = { - create: createLoggerMock, -}; +export { loggerMock, MockedLogger } from '@kbn/logging/target/mocks'; diff --git a/src/core/server/logging/logger.test.ts b/src/core/server/logging/logger.test.ts index 1cc00a254300b..1796519ff65e5 100644 --- a/src/core/server/logging/logger.test.ts +++ b/src/core/server/logging/logger.test.ts @@ -17,9 +17,8 @@ * under the License. */ +import { LogLevel, Appender } from '@kbn/logging'; import { LoggingConfig } from './logging_config'; -import { Appender } from './appenders/appenders'; -import { LogLevel } from './log_level'; import { BaseLogger } from './logger'; const context = LoggingConfig.getLoggerContext(['context', 'parent', 'child']); diff --git a/src/core/server/logging/logger.ts b/src/core/server/logging/logger.ts index 285998c23832c..6861072ef3b8b 100644 --- a/src/core/server/logging/logger.ts +++ b/src/core/server/logging/logger.ts @@ -17,86 +17,7 @@ * under the License. */ -import { Appender } from './appenders/appenders'; -import { LogLevel } from './log_level'; -import { LogRecord } from './log_record'; -import { LoggerFactory } from './logger_factory'; - -/** - * Contextual metadata - * - * @public - */ -export interface LogMeta { - [key: string]: any; -} - -/** - * Logger exposes all the necessary methods to log any type of information and - * this is the interface used by the logging consumers including plugins. - * - * @public - */ -export interface Logger { - /** - * Log messages at the most detailed log level - * - * @param message - The log message - * @param meta - - */ - trace(message: string, meta?: LogMeta): void; - - /** - * Log messages useful for debugging and interactive investigation - * @param message - The log message - * @param meta - - */ - debug(message: string, meta?: LogMeta): void; - - /** - * Logs messages related to general application flow - * @param message - The log message - * @param meta - - */ - info(message: string, meta?: LogMeta): void; - - /** - * Logs abnormal or unexpected errors or messages - * @param errorOrMessage - An Error object or message string to log - * @param meta - - */ - warn(errorOrMessage: string | Error, meta?: LogMeta): void; - - /** - * Logs abnormal or unexpected errors or messages that caused a failure in the application flow - * - * @param errorOrMessage - An Error object or message string to log - * @param meta - - */ - error(errorOrMessage: string | Error, meta?: LogMeta): void; - - /** - * Logs abnormal or unexpected errors or messages that caused an unrecoverable failure - * - * @param errorOrMessage - An Error object or message string to log - * @param meta - - */ - fatal(errorOrMessage: string | Error, meta?: LogMeta): void; - - /** @internal */ - log(record: LogRecord): void; - - /** - * Returns a new {@link Logger} instance extending the current logger context. - * - * @example - * ```typescript - * const logger = loggerFactory.get('plugin', 'service'); // 'plugin.service' context - * const subLogger = logger.get('feature'); // 'plugin.service.feature' context - * ``` - */ - get(...childContextPaths: string[]): Logger; -} +import { Appender, LogLevel, LogRecord, LoggerFactory, LogMeta, Logger } from '@kbn/logging'; function isError(x: any): x is Error { return x instanceof Error; diff --git a/src/core/server/logging/logger_adapter.ts b/src/core/server/logging/logger_adapter.ts index 14e5712e55c58..4ce65bf3302e7 100644 --- a/src/core/server/logging/logger_adapter.ts +++ b/src/core/server/logging/logger_adapter.ts @@ -17,8 +17,7 @@ * under the License. */ -import { LogRecord } from './log_record'; -import { Logger, LogMeta } from './logger'; +import { LogRecord, Logger, LogMeta } from '@kbn/logging'; /** @internal */ export class LoggerAdapter implements Logger { diff --git a/src/core/server/logging/logging_service.mock.ts b/src/core/server/logging/logging_service.mock.ts index 21edbe670eaec..6a721e134feed 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_service.ts b/src/core/server/logging/logging_service.ts index 09051f8f07702..f2b609f2258c7 100644 --- a/src/core/server/logging/logging_service.ts +++ b/src/core/server/logging/logging_service.ts @@ -18,10 +18,10 @@ */ import { Observable, Subscription } from 'rxjs'; +import { Logger } from '@kbn/logging'; import { CoreService } from '../../types'; import { LoggingConfig, LoggerContextConfigInput } from './logging_config'; import { ILoggingSystem } from './logging_system'; -import { Logger } from './logger'; import { CoreContext } from '../core_context'; /** diff --git a/src/core/server/logging/logging_system.mock.ts b/src/core/server/logging/logging_system.mock.ts index ac1e9b5196002..6ea784be5411f 100644 --- a/src/core/server/logging/logging_system.mock.ts +++ b/src/core/server/logging/logging_system.mock.ts @@ -18,8 +18,8 @@ */ // Test helpers to simplify mocking logs and collecting all their outputs +import { LoggerFactory } from '@kbn/logging'; import { ILoggingSystem } from './logging_system'; -import { LoggerFactory } from './logger_factory'; import { loggerMock, MockedLogger } from './logger.mock'; const createLoggingSystemMock = () => { @@ -48,15 +48,7 @@ const createLoggingSystemMock = () => { const collectLoggingSystemMock = (loggerFactory: LoggerFactory) => { const mockLog = loggerFactory.get() as MockedLogger; - return { - debug: mockLog.debug.mock.calls, - error: mockLog.error.mock.calls, - fatal: mockLog.fatal.mock.calls, - info: mockLog.info.mock.calls, - log: mockLog.log.mock.calls, - trace: mockLog.trace.mock.calls, - warn: mockLog.warn.mock.calls, - }; + return loggerMock.collect(mockLog); }; const clearLoggingSystemMock = (loggerFactory: LoggerFactory) => { @@ -67,13 +59,7 @@ const clearLoggingSystemMock = (loggerFactory: LoggerFactory) => { mockedLoggerFactory.stop.mockClear(); const mockLog = loggerFactory.get() as MockedLogger; - mockLog.debug.mockClear(); - mockLog.info.mockClear(); - mockLog.warn.mockClear(); - mockLog.error.mockClear(); - mockLog.trace.mockClear(); - mockLog.fatal.mockClear(); - mockLog.log.mockClear(); + loggerMock.clear(mockLog); }; export const loggingSystemMock = { diff --git a/src/core/server/logging/logging_system.ts b/src/core/server/logging/logging_system.ts index 8aadab83bf716..8bc22bdf537af 100644 --- a/src/core/server/logging/logging_system.ts +++ b/src/core/server/logging/logging_system.ts @@ -16,12 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { Appenders, DisposableAppender } from './appenders/appenders'; + +import type { PublicMethodsOf } from '@kbn/utility-types'; +import { DisposableAppender, LogLevel, Logger, LoggerFactory } from '@kbn/logging'; +import { Appenders } from './appenders/appenders'; import { BufferAppender } from './appenders/buffer/buffer_appender'; -import { LogLevel } from './log_level'; -import { BaseLogger, Logger } from './logger'; +import { BaseLogger } from './logger'; import { LoggerAdapter } from './logger_adapter'; -import { LoggerFactory } from './logger_factory'; import { LoggingConfigType, LoggerConfigType, diff --git a/src/core/server/metrics/metrics_service.mock.ts b/src/core/server/metrics/metrics_service.mock.ts index 2af653004a479..0d9e9af39317c 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, @@ -76,8 +78,8 @@ type MetricsServiceContract = PublicMethodsOf; const createMock = () => { const mocked: jest.Mocked = { - setup: jest.fn().mockReturnValue(createInternalSetupContractMock()), - start: jest.fn().mockReturnValue(createInternalStartContractMock()), + setup: jest.fn().mockReturnValue(createSetupContractMock()), + start: jest.fn().mockReturnValue(createStartContractMock()), stop: jest.fn(), }; return mocked; diff --git a/src/core/server/metrics/metrics_service.test.ts b/src/core/server/metrics/metrics_service.test.ts index f2019de7b6cab..384a56c8dba94 100644 --- a/src/core/server/metrics/metrics_service.test.ts +++ b/src/core/server/metrics/metrics_service.test.ts @@ -18,10 +18,11 @@ */ import moment from 'moment'; + +import { configServiceMock } from '../config/mocks'; import { mockOpsCollector } from './metrics_service.test.mocks'; import { MetricsService } from './metrics_service'; import { mockCoreContext } from '../core_context.mock'; -import { configServiceMock } from '../config/config_service.mock'; import { httpServiceMock } from '../http/http_service.mock'; import { take } from 'rxjs/operators'; @@ -105,6 +106,25 @@ describe('MetricsService', () => { `"#setup() needs to be run first"` ); }); + + it('emits the last value on each getOpsMetrics$ call', async () => { + const firstMetrics = { metric: 'first' }; + const secondMetrics = { metric: 'second' }; + mockOpsCollector.collect + .mockResolvedValueOnce(firstMetrics) + .mockResolvedValueOnce(secondMetrics); + + await metricsService.setup({ http: httpMock }); + const { getOpsMetrics$ } = await metricsService.start(); + + const firstEmission = getOpsMetrics$().pipe(take(1)).toPromise(); + jest.advanceTimersByTime(testInterval); + expect(await firstEmission).toEqual({ metric: 'first' }); + + const secondEmission = getOpsMetrics$().pipe(take(1)).toPromise(); + jest.advanceTimersByTime(testInterval); + expect(await secondEmission).toEqual({ metric: 'second' }); + }); }); describe('#stop', () => { diff --git a/src/core/server/metrics/metrics_service.ts b/src/core/server/metrics/metrics_service.ts index d4696b3aa9aaf..ab58a75d49a98 100644 --- a/src/core/server/metrics/metrics_service.ts +++ b/src/core/server/metrics/metrics_service.ts @@ -37,7 +37,7 @@ export class MetricsService private readonly logger: Logger; private metricsCollector?: OpsMetricsCollector; private collectInterval?: NodeJS.Timeout; - private metrics$ = new ReplaySubject(); + private metrics$ = new ReplaySubject(1); private service?: InternalMetricsServiceSetup; constructor(private readonly coreContext: CoreContext) { diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 3c79706422cd4..7e001ffe28100 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -18,6 +18,7 @@ */ import { of } from 'rxjs'; import { duration } from 'moment'; +import { ByteSizeValue } from '@kbn/config-schema'; import { PluginInitializerContext, CoreSetup, CoreStart, StartServicesAccessor } from '.'; import { loggingSystemMock } from './logging/logging_system.mock'; import { loggingServiceMock } from './logging/logging_service.mock'; @@ -37,10 +38,10 @@ import { environmentServiceMock } from './environment/environment_service.mock'; import { statusServiceMock } from './status/status_service.mock'; import { auditTrailServiceMock } from './audit_trail/audit_trail_service.mock'; +export { configServiceMock } from './config/mocks'; export { httpServerMock } from './http/http_server.mocks'; export { httpResourcesMock } from './http_resources/http_resources_service.mock'; export { sessionStorageMock } from './http/cookie_session_storage.mocks'; -export { configServiceMock } from './config/config_service.mock'; export { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock'; export { httpServiceMock } from './http/http_service.mock'; export { loggingSystemMock } from './logging/logging_system.mock'; @@ -51,6 +52,8 @@ export { typeRegistryMock as savedObjectsTypeRegistryMock } from './saved_object export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; export { metricsServiceMock } from './metrics/metrics_service.mock'; export { renderingMock } from './rendering/rendering_service.mock'; +export { statusServiceMock } from './status/status_service.mock'; +export { contextServiceMock } from './context/context_service.mock'; export function pluginInitializerContextConfigMock(config: T) { const globalConfig: SharedGlobalConfig = { @@ -66,6 +69,9 @@ export function pluginInitializerContextConfigMock(config: T) { startupTimeout: duration('30s'), }, path: { data: '/tmp' }, + savedObjects: { + maxImportPayloadBytes: new ByteSizeValue(10485760), + }, }; const mock: jest.Mocked['config']> = { @@ -133,6 +139,7 @@ function createCoreSetupMock({ uiSettings: uiSettingsMock, auditTrail: auditTrailServiceMock.createSetupContract(), logging: loggingServiceMock.createSetupContract(), + metrics: metricsServiceMock.createSetupContract(), getStartServices: jest .fn, object, any]>, []>() .mockResolvedValue([createCoreStartMock(), pluginStartDeps, pluginStartContract]), @@ -169,6 +176,7 @@ function createInternalCoreSetupMock() { uiSettings: uiSettingsServiceMock.createSetupContract(), auditTrail: auditTrailServiceMock.createSetupContract(), logging: loggingServiceMock.createInternalSetupContract(), + metrics: metricsServiceMock.createInternalSetupContract(), }; return setupDeps; } @@ -178,7 +186,7 @@ function createInternalCoreStartMock() { capabilities: capabilitiesServiceMock.createStartContract(), elasticsearch: elasticsearchServiceMock.createInternalStart(), http: httpServiceMock.createInternalStartContract(), - metrics: metricsServiceMock.createStartContract(), + metrics: metricsServiceMock.createInternalStartContract(), savedObjects: savedObjectsServiceMock.createInternalStartContract(), uiSettings: uiSettingsServiceMock.createStartContract(), auditTrail: auditTrailServiceMock.createStartContract(), diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts index eb8dc1e609582..3634a7ed2c277 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts @@ -17,6 +17,12 @@ * under the License. */ +const realFs = jest.requireActual('fs'); + export const mockReadFile = jest.fn(); const mockStat = jest.fn(); -jest.mock('fs', () => ({ readFile: mockReadFile, stat: mockStat })); +jest.doMock('fs', () => ({ + ...realFs, + readFile: mockReadFile, + stat: mockStat, +})); diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts index 836aabf881474..cb9f5982d0f19 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts @@ -17,9 +17,9 @@ * under the License. */ -import { PluginDiscoveryErrorType } from './plugin_discovery_error'; - import { mockReadFile } from './plugin_manifest_parser.test.mocks'; + +import { PluginDiscoveryErrorType } from './plugin_discovery_error'; import { loggingSystemMock } from '../../logging/logging_system.mock'; import { resolve } from 'path'; diff --git a/src/core/server/plugins/discovery/plugins_discovery.test.mocks.ts b/src/core/server/plugins/discovery/plugins_discovery.test.mocks.ts index 83accc06cb995..131ee58e21a81 100644 --- a/src/core/server/plugins/discovery/plugins_discovery.test.mocks.ts +++ b/src/core/server/plugins/discovery/plugins_discovery.test.mocks.ts @@ -17,5 +17,10 @@ * under the License. */ -export const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] }); -jest.mock('../../../../../package.json', () => mockPackage); +export const mockPackage = { + raw: {}, +}; + +jest.doMock('load-json-file', () => ({ + sync: () => mockPackage.raw, +})); diff --git a/src/core/server/plugins/discovery/plugins_discovery.test.ts b/src/core/server/plugins/discovery/plugins_discovery.test.ts index 4894f19e38df4..0dfc5599e8007 100644 --- a/src/core/server/plugins/discovery/plugins_discovery.test.ts +++ b/src/core/server/plugins/discovery/plugins_discovery.test.ts @@ -17,18 +17,19 @@ * under the License. */ +// must be before mocks imports to avoid conflicting with `REPO_ROOT` accessor. +import { REPO_ROOT } from '@kbn/dev-utils'; import { mockPackage } from './plugins_discovery.test.mocks'; import mockFs from 'mock-fs'; import { loggingSystemMock } from '../../logging/logging_system.mock'; +import { getEnvOptions, rawConfigServiceMock } from '../../config/mocks'; import { first, map, toArray } from 'rxjs/operators'; import { resolve } from 'path'; import { ConfigService, Env } from '../../config'; -import { getEnvOptions } from '../../config/__mocks__/env'; import { PluginsConfig, PluginsConfigType, config } from '../plugins_config'; import type { InstanceInfo } from '../plugin_context'; import { discover } from './plugins_discovery'; -import { rawConfigServiceMock } from '../../config/raw_config_service.mock'; import { CoreContext } from '../../core_context'; const KIBANA_ROOT = process.cwd(); @@ -94,6 +95,7 @@ describe('plugins discovery system', () => { }; env = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { envName: 'development' }, }) @@ -382,6 +384,7 @@ describe('plugins discovery system', () => { const extraPluginTestPath = resolve(process.cwd(), 'my-extra-plugin'); env = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false, envName: 'development' }, }) @@ -409,6 +412,7 @@ describe('plugins discovery system', () => { const extraPluginTestPath = resolve(process.cwd(), 'my-extra-plugin'); env = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false, envName: 'production' }, }) diff --git a/src/core/server/plugins/integration_tests/plugins_service.test.mocks.ts b/src/core/server/plugins/integration_tests/plugins_service.test.mocks.ts index d81a7eb5db4ae..ed73fe9b99be6 100644 --- a/src/core/server/plugins/integration_tests/plugins_service.test.mocks.ts +++ b/src/core/server/plugins/integration_tests/plugins_service.test.mocks.ts @@ -17,11 +17,13 @@ * under the License. */ -export const mockPackage = new Proxy( - { raw: { __dirname: '/tmp' } as any }, - { get: (obj, prop) => obj.raw[prop] } -); -jest.mock('../../../../core/server/utils/package_json', () => ({ pkg: mockPackage })); +export const mockPackage = { + raw: { __dirname: '/tmp' } as any, +}; + +jest.doMock('load-json-file', () => ({ + sync: () => mockPackage.raw, +})); export const mockDiscover = jest.fn(); jest.mock('../discovery/plugins_discovery', () => ({ discover: mockDiscover })); diff --git a/src/core/server/plugins/integration_tests/plugins_service.test.ts b/src/core/server/plugins/integration_tests/plugins_service.test.ts index 5a216b75a83b9..93fee05016ff2 100644 --- a/src/core/server/plugins/integration_tests/plugins_service.test.ts +++ b/src/core/server/plugins/integration_tests/plugins_service.test.ts @@ -17,15 +17,16 @@ * under the License. */ +// must be before mocks imports to avoid conflicting with `REPO_ROOT` accessor. +import { REPO_ROOT } from '@kbn/dev-utils'; import { mockPackage, mockDiscover } from './plugins_service.test.mocks'; import { join } from 'path'; import { PluginsService } from '../plugins_service'; import { ConfigPath, ConfigService, Env } from '../../config'; -import { getEnvOptions } from '../../config/__mocks__/env'; +import { getEnvOptions, rawConfigServiceMock } from '../../config/mocks'; import { BehaviorSubject, from } from 'rxjs'; -import { rawConfigServiceMock } from '../../config/raw_config_service.mock'; import { config } from '../plugins_config'; import { loggingSystemMock } from '../../logging/logging_system.mock'; import { environmentServiceMock } from '../../environment/environment_service.mock'; @@ -93,7 +94,7 @@ describe('PluginsService', () => { }, }; - const env = Env.createDefault(getEnvOptions()); + const env = Env.createDefault(REPO_ROOT, getEnvOptions()); const config$ = new BehaviorSubject>({ plugins: { initialize: true, diff --git a/src/core/server/plugins/plugin.test.ts b/src/core/server/plugins/plugin.test.ts index 1108ffc248161..ae22ead2aa4be 100644 --- a/src/core/server/plugins/plugin.test.ts +++ b/src/core/server/plugins/plugin.test.ts @@ -19,14 +19,14 @@ import { join } from 'path'; import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { schema } from '@kbn/config-schema'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; import { CoreContext } from '../core_context'; import { coreMock } from '../mocks'; -import { configServiceMock } from '../config/config_service.mock'; import { loggingSystemMock } from '../logging/logging_system.mock'; +import { getEnvOptions, configServiceMock } from '../config/mocks'; import { PluginWrapper } from './plugin'; import { PluginManifest } from './types'; @@ -77,7 +77,7 @@ const setupDeps = coreMock.createInternalSetup(); beforeEach(() => { coreId = Symbol('core'); - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); instanceInfo = { uuid: 'instance-uuid', }; diff --git a/src/core/server/plugins/plugin_context.test.ts b/src/core/server/plugins/plugin_context.test.ts index 578c5f39d71ea..cb4e8f20be982 100644 --- a/src/core/server/plugins/plugin_context.test.ts +++ b/src/core/server/plugins/plugin_context.test.ts @@ -19,15 +19,16 @@ import { duration } from 'moment'; import { first } from 'rxjs/operators'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { createPluginInitializerContext, InstanceInfo } from './plugin_context'; import { CoreContext } from '../core_context'; import { Env } from '../config'; import { loggingSystemMock } from '../logging/logging_system.mock'; -import { rawConfigServiceMock } from '../config/raw_config_service.mock'; -import { getEnvOptions } from '../config/__mocks__/env'; +import { rawConfigServiceMock, getEnvOptions } from '../config/mocks'; import { PluginManifest } from './types'; import { Server } from '../server'; import { fromRoot } from '../utils'; +import { ByteSizeValue } from '@kbn/config-schema'; const logger = loggingSystemMock.create(); @@ -58,7 +59,7 @@ describe('createPluginInitializerContext', () => { instanceInfo = { uuid: 'instance-uuid', }; - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); const config$ = rawConfigServiceMock.create({ rawConfig: {} }); server = new Server(config$, env, logger); await server.setupCoreConfig(); @@ -93,6 +94,7 @@ describe('createPluginInitializerContext', () => { startupTimeout: duration(5, 's'), }, path: { data: fromRoot('data') }, + savedObjects: { maxImportPayloadBytes: new ByteSizeValue(10485760) }, }); }); diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index af0b0e19b3227..ab3f471fd7942 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 { PathConfigType, config as pathConfig } from '@kbn/utils'; +import { pick, deepFreeze } from '@kbn/std'; import { CoreContext } from '../core_context'; import { PluginWrapper } from './plugin'; import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service'; @@ -28,13 +30,12 @@ import { PluginOpaqueId, SharedGlobalConfigKeys, } from './types'; -import { PathConfigType, config as pathConfig } from '../path'; import { KibanaConfigType, config as kibanaConfig } from '../kibana_config'; import { ElasticsearchConfigType, config as elasticsearchConfig, } from '../elasticsearch/elasticsearch_config'; -import { pick, deepFreeze } from '../../utils'; +import { SavedObjectsConfigType, savedObjectsConfig } from '../saved_objects/saved_objects_config'; import { CoreSetup, CoreStart } from '..'; export interface InstanceInfo { @@ -91,16 +92,18 @@ export function createPluginInitializerContext( * Note: naming not final here, it will be renamed in a near future (https://github.com/elastic/kibana/issues/46240) * @deprecated */ - globalConfig$: combineLatest( + globalConfig$: combineLatest([ coreContext.configService.atPath(kibanaConfig.path), coreContext.configService.atPath(elasticsearchConfig.path), - coreContext.configService.atPath(pathConfig.path) - ).pipe( - map(([kibana, elasticsearch, path]) => + coreContext.configService.atPath(pathConfig.path), + coreContext.configService.atPath(savedObjectsConfig.path), + ]).pipe( + map(([kibana, elasticsearch, path, savedObjects]) => deepFreeze({ kibana: pick(kibana, SharedGlobalConfigKeys.kibana), elasticsearch: pick(elasticsearch, SharedGlobalConfigKeys.elasticsearch), path: pick(path, SharedGlobalConfigKeys.path), + savedObjects: pick(savedObjects, SharedGlobalConfigKeys.savedObjects), }) ) ), @@ -176,6 +179,10 @@ export function createPluginSetupContext( logging: { configure: (config$) => deps.logging.configure(['plugins', plugin.name], config$), }, + metrics: { + collectionInterval: deps.metrics.collectionInterval, + getOpsMetrics$: deps.metrics.getOpsMetrics$, + }, savedObjects: { setClientFactoryProvider: deps.savedObjects.setClientFactoryProvider, addClientWrapper: deps.savedObjects.addClientWrapper, @@ -188,6 +195,7 @@ export function createPluginSetupContext( set: deps.status.plugins.set.bind(null, plugin.name), dependencies$: deps.status.plugins.getDependenciesStatus$(plugin.name), derivedStatus$: deps.status.plugins.getDerivedStatus$(plugin.name), + isStatusPageAnonymous: deps.status.isStatusPageAnonymous, }, uiSettings: { register: deps.uiSettings.register, diff --git a/src/core/server/plugins/plugins_config.test.ts b/src/core/server/plugins/plugins_config.test.ts index 180d6093e0404..2a64e79d19bda 100644 --- a/src/core/server/plugins/plugins_config.test.ts +++ b/src/core/server/plugins/plugins_config.test.ts @@ -17,13 +17,14 @@ * under the License. */ +import { REPO_ROOT } from '@kbn/dev-utils'; +import { getEnvOptions } from '../config/mocks'; import { PluginsConfig, PluginsConfigType } from './plugins_config'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; describe('PluginsConfig', () => { it('retrieves additionalPluginPaths from config.paths when in production mode', () => { - const env = Env.createDefault(getEnvOptions({ cliArgs: { dev: false } })); + const env = Env.createDefault(REPO_ROOT, getEnvOptions({ cliArgs: { dev: false } })); const rawConfig: PluginsConfigType = { initialize: true, paths: ['some-path', 'another-path'], @@ -33,7 +34,7 @@ describe('PluginsConfig', () => { }); it('retrieves additionalPluginPaths from config.paths when in development mode', () => { - const env = Env.createDefault(getEnvOptions({ cliArgs: { dev: true } })); + const env = Env.createDefault(REPO_ROOT, getEnvOptions({ cliArgs: { dev: true } })); const rawConfig: PluginsConfigType = { initialize: true, paths: ['some-path', 'another-path'], diff --git a/src/core/server/plugins/plugins_service.mock.ts b/src/core/server/plugins/plugins_service.mock.ts index a40566767ddae..14d6de889dd42 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.test.mocks.ts b/src/core/server/plugins/plugins_service.test.mocks.ts index 8d4ba12c8375c..15e4187ef95ed 100644 --- a/src/core/server/plugins/plugins_service.test.mocks.ts +++ b/src/core/server/plugins/plugins_service.test.mocks.ts @@ -17,11 +17,25 @@ * under the License. */ -export const mockPackage = new Proxy( - { raw: { __dirname: '/tmp' } as any }, - { get: (obj, prop) => obj.raw[prop] } -); -jest.mock('../../../core/server/utils/package_json', () => ({ pkg: mockPackage })); +import { REPO_ROOT } from '@kbn/utils'; +import { resolve } from 'path'; + +const loadJsonFile = jest.requireActual('load-json-file'); +const kibanaPackagePath = resolve(REPO_ROOT, 'package.json'); + +export const mockPackage = { + raw: { __dirname: '/tmp', name: 'kibana' } as any, +}; + +jest.doMock('load-json-file', () => ({ + ...loadJsonFile, + sync: (path: string) => { + if (path === kibanaPackagePath) { + return mockPackage.raw; + } + return loadJsonFile.sync(path); + }, +})); export const mockDiscover = jest.fn(); jest.mock('./discovery/plugins_discovery', () => ({ discover: mockDiscover })); diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 5e613343c302f..d36fd2251176a 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -22,11 +22,10 @@ import { mockDiscover, mockPackage } from './plugins_service.test.mocks'; import { resolve, join } from 'path'; import { BehaviorSubject, from } from 'rxjs'; import { schema } from '@kbn/config-schema'; -import { createAbsolutePathSerializer } from '@kbn/dev-utils'; +import { createAbsolutePathSerializer, REPO_ROOT } from '@kbn/dev-utils'; import { ConfigPath, ConfigService, Env } from '../config'; -import { rawConfigServiceMock } from '../config/raw_config_service.mock'; -import { getEnvOptions } from '../config/__mocks__/env'; +import { rawConfigServiceMock, getEnvOptions } from '../config/mocks'; import { coreMock } from '../mocks'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { environmentServiceMock } from '../environment/environment_service.mock'; @@ -116,7 +115,7 @@ describe('PluginsService', () => { }; coreId = Symbol('core'); - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); config$ = new BehaviorSubject>({ plugins: { initialize: true } }); const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index 30cd47c9d44e1..e8fe42ee491ca 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.test.ts b/src/core/server/plugins/plugins_system.test.ts index 71ac31db13f92..ae9267ca5cf60 100644 --- a/src/core/server/plugins/plugins_system.test.ts +++ b/src/core/server/plugins/plugins_system.test.ts @@ -24,10 +24,10 @@ import { import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; +import { configServiceMock, getEnvOptions } from '../config/mocks'; import { CoreContext } from '../core_context'; -import { configServiceMock } from '../config/config_service.mock'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { PluginWrapper } from './plugin'; @@ -74,7 +74,7 @@ const setupDeps = coreMock.createInternalSetup(); const startDeps = coreMock.createInternalStart(); beforeEach(() => { - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); coreContext = { coreId: Symbol(), env, logger, configService: configService as any }; diff --git a/src/core/server/plugins/plugins_system.ts b/src/core/server/plugins/plugins_system.ts index b2acd9a6fd04b..72d2cfe158b37 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/plugins/types.ts b/src/core/server/plugins/types.ts index 517261b5bc9bb..9de181124a349 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -20,12 +20,13 @@ import { Observable } from 'rxjs'; import { Type } from '@kbn/config-schema'; import { RecursiveReadonly } from '@kbn/utility-types'; +import { PathConfigType } from '@kbn/utils'; import { ConfigPath, EnvironmentMode, PackageInfo, ConfigDeprecationProvider } from '../config'; import { LoggerFactory } from '../logging'; import { KibanaConfigType } from '../kibana_config'; import { ElasticsearchConfigType } from '../elasticsearch/elasticsearch_config'; -import { PathConfigType } from '../path'; +import { SavedObjectsConfigType } from '../saved_objects/saved_objects_config'; import { CoreSetup, CoreStart } from '..'; /** @@ -263,6 +264,7 @@ export const SharedGlobalConfigKeys = { kibana: ['index', 'autocompleteTerminateAfter', 'autocompleteTimeout'] as const, elasticsearch: ['shardTimeout', 'requestTimeout', 'pingTimeout', 'startupTimeout'] as const, path: ['data'] as const, + savedObjects: ['maxImportPayloadBytes'] as const, }; /** @@ -272,6 +274,7 @@ export type SharedGlobalConfig = RecursiveReadonly<{ kibana: Pick; elasticsearch: Pick; path: Pick; + savedObjects: Pick; }>; /** diff --git a/src/core/server/rendering/__mocks__/rendering_service.ts b/src/core/server/rendering/__mocks__/rendering_service.ts index 2e35568873c9a..179a09b8619b0 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/root/index.test.mocks.ts b/src/core/server/root/index.test.mocks.ts index ef4a40fa3db2d..d81b1575adb88 100644 --- a/src/core/server/root/index.test.mocks.ts +++ b/src/core/server/root/index.test.mocks.ts @@ -23,15 +23,14 @@ jest.doMock('../logging/logging_system', () => ({ LoggingSystem: jest.fn(() => logger), })); -import { configServiceMock } from '../config/config_service.mock'; -export const configService = configServiceMock.create(); -jest.doMock('../config/config_service', () => ({ - ConfigService: jest.fn(() => configService), -})); +const realKbnConfig = jest.requireActual('@kbn/config'); -import { rawConfigServiceMock } from '../config/raw_config_service.mock'; +import { configServiceMock, rawConfigServiceMock } from '../config/mocks'; +export const configService = configServiceMock.create(); export const rawConfigService = rawConfigServiceMock.create(); -jest.doMock('../config/raw_config_service', () => ({ +jest.doMock('@kbn/config', () => ({ + ...realKbnConfig, + ConfigService: jest.fn(() => configService), RawConfigService: jest.fn(() => rawConfigService), })); diff --git a/src/core/server/root/index.test.ts b/src/core/server/root/index.test.ts index 5b853903ea4be..4d3fe24c7ba83 100644 --- a/src/core/server/root/index.test.ts +++ b/src/core/server/root/index.test.ts @@ -21,11 +21,12 @@ import { rawConfigService, configService, logger, mockServer } from './index.tes import { BehaviorSubject } from 'rxjs'; import { filter, first } from 'rxjs/operators'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import { getEnvOptions } from '../config/mocks'; import { Root } from '.'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; -const env = new Env('.', getEnvOptions()); +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); let mockConsoleError: jest.SpyInstance; 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 0000000000000..68d582e3cae09 --- /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 fae33bc050dee..23d8c4518d3ab 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 e3d44c20dd190..bd76658c21731 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 d0035294226ea..bb840e459bf22 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 60e8aa0afdda4..0608035ce51a2 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 d19f06d74e419..be36807f0d02b 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 7d30875b90796..352ce4c1c16eb 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 85c47029e36d5..4adc92df31805 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 3ff72a86c2f89..642d51c70766e 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 ddf20606800c8..aa79a10b2a9be 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 50c118ca64ffb..1885f5ec50139 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 ec457704e89c7..1dcf8a22e9cfd 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -21,6 +21,7 @@ import { CatSnapshotsParams } from 'elasticsearch'; import { CatTasksParams } from 'elasticsearch'; import { CatThreadPoolParams } from 'elasticsearch'; import { ClearScrollParams } from 'elasticsearch'; +import { CliArgs } from '@kbn/config'; import { Client } from 'elasticsearch'; import { ClientOptions } from '@elastic/elasticsearch'; import { ClusterAllocationExplainParams } from 'elasticsearch'; @@ -31,7 +32,13 @@ import { ClusterPutSettingsParams } from 'elasticsearch'; import { ClusterRerouteParams } from 'elasticsearch'; import { ClusterStateParams } from 'elasticsearch'; import { ClusterStatsParams } from 'elasticsearch'; +import { ConfigDeprecation } from '@kbn/config'; +import { ConfigDeprecationFactory } from '@kbn/config'; +import { ConfigDeprecationLogger } from '@kbn/config'; +import { ConfigDeprecationProvider } from '@kbn/config'; import { ConfigOptions } from 'elasticsearch'; +import { ConfigPath } from '@kbn/config'; +import { ConfigService } from '@kbn/config'; import { CountParams } from 'elasticsearch'; import { CreateDocumentParams } from 'elasticsearch'; import { DeleteDocumentByQueryParams } from 'elasticsearch'; @@ -40,7 +47,7 @@ import { DeleteScriptParams } from 'elasticsearch'; import { DeleteTemplateParams } from 'elasticsearch'; import { DetailedPeerCertificate } from 'tls'; import { Duration } from 'moment'; -import { ErrorToastOptions } from 'src/core/public/notifications'; +import { EnvironmentMode } from '@kbn/config'; import { ExistsParams } from 'elasticsearch'; import { ExplainParams } from 'elasticsearch'; import { FieldStatsParams } from 'elasticsearch'; @@ -95,6 +102,11 @@ import { IngestPutPipelineParams } from 'elasticsearch'; import { IngestSimulateParams } from 'elasticsearch'; import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType } from 'src/core/server/kibana_config'; +import { Logger } from '@kbn/logging'; +import { LoggerFactory } from '@kbn/logging'; +import { LogLevel } from '@kbn/logging'; +import { LogMeta } from '@kbn/logging'; +import { LogRecord } from '@kbn/logging'; import { MGetParams } from 'elasticsearch'; import { MGetResponse } from 'elasticsearch'; import { MSearchParams } from 'elasticsearch'; @@ -106,7 +118,8 @@ import { NodesInfoParams } from 'elasticsearch'; import { NodesStatsParams } from 'elasticsearch'; import { ObjectType } from '@kbn/config-schema'; import { Observable } from 'rxjs'; -import { ParsedQuery } from 'query-string'; +import { PackageInfo } from '@kbn/config'; +import { PathConfigType } from '@kbn/utils'; import { PeerCertificate } from 'tls'; import { PingParams } from 'elasticsearch'; import { PutScriptParams } from 'elasticsearch'; @@ -119,7 +132,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 +155,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 +171,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) @@ -369,45 +377,17 @@ export const config: { }; }; -// @public -export type ConfigDeprecation = (config: Record, fromPath: string, logger: ConfigDeprecationLogger) => Record; +export { ConfigDeprecation } -// @public -export interface ConfigDeprecationFactory { - rename(oldKey: string, newKey: string): ConfigDeprecation; - renameFromRoot(oldKey: string, newKey: string, silent?: boolean): ConfigDeprecation; - unused(unusedKey: string): ConfigDeprecation; - unusedFromRoot(unusedKey: string): ConfigDeprecation; -} +export { ConfigDeprecationFactory } -// @public -export type ConfigDeprecationLogger = (message: string) => void; +export { ConfigDeprecationLogger } -// @public -export type ConfigDeprecationProvider = (factory: ConfigDeprecationFactory) => ConfigDeprecation[]; +export { ConfigDeprecationProvider } -// @public (undocumented) -export type ConfigPath = string | string[]; +export { ConfigPath } -// @internal (undocumented) -export class ConfigService { - // Warning: (ae-forgotten-export) The symbol "RawConfigurationProvider" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "Env" needs to be exported by the entry point index.d.ts - constructor(rawConfigProvider: RawConfigurationProvider, env: Env, logger: LoggerFactory); - addDeprecationProvider(path: ConfigPath, provider: ConfigDeprecationProvider): void; - atPath(path: ConfigPath): Observable; - // Warning: (ae-forgotten-export) The symbol "Config" needs to be exported by the entry point index.d.ts - getConfig$(): Observable; - // (undocumented) - getUnusedPaths(): Promise; - // (undocumented) - getUsedPaths(): Promise; - // (undocumented) - isEnabledAtPath(path: ConfigPath): Promise; - optionalAtPath(path: ConfigPath): Observable; - setSchema(path: ConfigPath, schema: Type): Promise; - validate(): Promise; - } +export { ConfigService } // @public export interface ContextSetup { @@ -436,6 +416,8 @@ export interface CoreSetup(object: T): RecursiveReadonly; - // @internal (undocumented) export const DEFAULT_APP_CATEGORIES: Readonly<{ kibana: { @@ -682,15 +658,7 @@ export interface ElasticsearchStatusMeta { warningNodes: NodesVersionCompatibility['warningNodes']; } -// @public (undocumented) -export interface EnvironmentMode { - // (undocumented) - dev: boolean; - // (undocumented) - name: 'development' | 'production'; - // (undocumented) - prod: boolean; -} +export { EnvironmentMode } // @public export interface ErrorHttpResponseOptions { @@ -716,11 +684,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 +693,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 +924,6 @@ export interface IScopedClusterClient { readonly asInternalUser: ElasticsearchClient; } -// @public -export function isRelativeUrl(candidatePath: string): boolean; - // @public export interface IUiSettingsClient { get: (key: string) => Promise; @@ -1441,18 +1396,7 @@ export interface LegacyUiExports { // @public export type LifecycleResponseFactory = typeof lifecycleResponseFactory; -// @public -export interface Logger { - debug(message: string, meta?: LogMeta): void; - error(errorOrMessage: string | Error, meta?: LogMeta): void; - fatal(errorOrMessage: string | Error, meta?: LogMeta): void; - get(...childContextPaths: string[]): Logger; - info(message: string, meta?: LogMeta): void; - // @internal (undocumented) - log(record: LogRecord): void; - trace(message: string, meta?: LogMeta): void; - warn(errorOrMessage: string | Error, meta?: LogMeta): void; -} +export { Logger } // Warning: (ae-forgotten-export) The symbol "loggerSchema" needs to be exported by the entry point index.d.ts // @@ -1467,69 +1411,18 @@ export interface LoggerContextConfigInput { loggers?: LoggerConfigType[]; } -// @public -export interface LoggerFactory { - get(...contextParts: string[]): Logger; -} +export { LoggerFactory } // @public export interface LoggingServiceSetup { configure(config$: Observable): void; } -// @internal -export class LogLevel { - // (undocumented) - static readonly All: LogLevel; - // (undocumented) - static readonly Debug: LogLevel; - // (undocumented) - static readonly Error: LogLevel; - // (undocumented) - static readonly Fatal: LogLevel; - static fromId(level: LogLevelId): LogLevel; - // Warning: (ae-forgotten-export) The symbol "LogLevelId" needs to be exported by the entry point index.d.ts - // - // (undocumented) - readonly id: LogLevelId; - // (undocumented) - static readonly Info: LogLevel; - // (undocumented) - static readonly Off: LogLevel; - supports(level: LogLevel): boolean; - // (undocumented) - static readonly Trace: LogLevel; - // (undocumented) - readonly value: number; - // (undocumented) - static readonly Warn: LogLevel; -} +export { LogLevel } -// @public -export interface LogMeta { - // (undocumented) - [key: string]: any; -} +export { LogMeta } -// @internal -export interface LogRecord { - // (undocumented) - context: string; - // (undocumented) - error?: Error; - // (undocumented) - level: LogLevel; - // (undocumented) - message: string; - // (undocumented) - meta?: { - [name: string]: any; - }; - // (undocumented) - pid: number; - // (undocumented) - timestamp: Date; -} +export { LogRecord } // @public export interface MetricsServiceSetup { @@ -1537,15 +1430,15 @@ export interface MetricsServiceSetup { getOpsMetrics$: () => Observable; } +// @public +export type MetricsServiceStart = MetricsServiceSetup; + // @public @deprecated (undocumented) 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'; @@ -1691,19 +1584,7 @@ export interface OpsServerMetrics { }; } -// @public (undocumented) -export interface PackageInfo { - // (undocumented) - branch: string; - // (undocumented) - buildNum: number; - // (undocumented) - buildSha: string; - // (undocumented) - dist: boolean; - // (undocumented) - version: string; -} +export { PackageInfo } // @public export interface Plugin { @@ -1717,6 +1598,7 @@ export interface Plugin { + // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported deprecations?: ConfigDeprecationProvider; exposeToBrowser?: { [P in keyof T]?: boolean; @@ -1754,6 +1636,7 @@ export interface PluginInitializerContext { // @public export interface PluginManifest { + // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported readonly configPath: ConfigPath; // @deprecated readonly extraPublicDirs?: string[]; @@ -2708,18 +2591,22 @@ export const ServiceStatusLevels: Readonly<{ available: Readonly<{ toString: () => "available"; valueOf: () => 0; + toJSON: () => "available"; }>; degraded: Readonly<{ toString: () => "degraded"; valueOf: () => 1; + toJSON: () => "degraded"; }>; unavailable: Readonly<{ toString: () => "unavailable"; valueOf: () => 2; + toJSON: () => "unavailable"; }>; critical: Readonly<{ toString: () => "critical"; valueOf: () => 3; + toJSON: () => "critical"; }>; }>; @@ -2780,6 +2667,7 @@ export type SharedGlobalConfig = RecursiveReadonly<{ kibana: Pick; elasticsearch: Pick; path: Pick; + savedObjects: Pick; }>; // @public @@ -2794,6 +2682,7 @@ export interface StatusServiceSetup { dependencies$: Observable>; // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "StatusSetup" derivedStatus$: Observable; + isStatusPageAnonymous: () => boolean; overall$: Observable; set(status$: Observable): void; } @@ -2848,26 +2737,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) @@ -2884,8 +2753,8 @@ export const validBodyOutput: readonly ["data", "stream"]; // // src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts // src/core/server/legacy/types.ts:135:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:272:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:272:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:274:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:274:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:274:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:277:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts ``` diff --git a/src/core/server/server.test.mocks.ts b/src/core/server/server.test.mocks.ts index 471e482a20e96..77f2787b75412 100644 --- a/src/core/server/server.test.mocks.ts +++ b/src/core/server/server.test.mocks.ts @@ -41,9 +41,12 @@ jest.mock('./legacy/legacy_service', () => ({ LegacyService: jest.fn(() => mockLegacyService), })); -import { configServiceMock } from './config/config_service.mock'; +const realKbnConfig = jest.requireActual('@kbn/config'); + +import { configServiceMock } from './config/mocks'; export const mockConfigService = configServiceMock.create(); -jest.doMock('./config/config_service', () => ({ +jest.doMock('@kbn/config', () => ({ + ...realKbnConfig, ConfigService: jest.fn(() => mockConfigService), })); diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index 8bf16d9130ef5..3258840d09df2 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -35,14 +35,14 @@ import { } from './server.test.mocks'; import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import { rawConfigServiceMock, getEnvOptions } from './config/mocks'; import { Env } from './config'; import { Server } from './server'; -import { getEnvOptions } from './config/__mocks__/env'; import { loggingSystemMock } from './logging/logging_system.mock'; -import { rawConfigServiceMock } from './config/raw_config_service.mock'; -const env = new Env('.', getEnvOptions()); +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); const logger = loggingSystemMock.create(); const rawConfigService = rawConfigServiceMock.create({}); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index a02b0f51b559f..8502f563cb0c2 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -17,6 +17,8 @@ * under the License. */ +import { config as pathConfig } from '@kbn/utils'; +import { mapToObject } from '@kbn/std'; import { ConfigService, Env, RawConfigurationProvider, coreDeprecationProvider } from './config'; import { CoreApp } from './core_app'; import { AuditTrailService } from './audit_trail'; @@ -31,7 +33,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'; @@ -39,12 +41,10 @@ import { config as elasticsearchConfig } from './elasticsearch'; import { config as httpConfig } from './http'; import { config as loggingConfig } from './logging'; import { config as devConfig } from './dev'; -import { config as pathConfig } from './path'; 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'; @@ -152,12 +152,15 @@ export class Server { savedObjects: savedObjectsSetup, }); - await this.metrics.setup({ http: httpSetup }); + const metricsSetup = await this.metrics.setup({ http: httpSetup }); const statusSetup = await this.status.setup({ elasticsearch: elasticsearchServiceSetup, pluginDependencies: pluginTree.asNames, savedObjects: savedObjectsSetup, + environment: environmentSetup, + http: httpSetup, + metrics: metricsSetup, }); const renderingSetup = await this.rendering.setup({ @@ -189,6 +192,7 @@ export class Server { httpResources: httpResourcesSetup, auditTrail: auditTrailSetup, logging: loggingSetup, + metrics: metricsSetup, }; const pluginsSetup = await this.plugins.setup(coreSetup); @@ -310,6 +314,7 @@ export class Server { uiSettingsConfig, opsConfig, statusConfig, + pidConfig, ]; this.configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider); diff --git a/src/core/server/status/legacy_status.test.ts b/src/core/server/status/legacy_status.test.ts new file mode 100644 index 0000000000000..e3e55442cabd2 --- /dev/null +++ b/src/core/server/status/legacy_status.test.ts @@ -0,0 +1,114 @@ +/* + * 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 { ServiceStatus, ServiceStatusLevels } from './types'; +import { calculateLegacyStatus } from './legacy_status'; + +const available: ServiceStatus = { level: ServiceStatusLevels.available, summary: 'Available' }; +const degraded: ServiceStatus = { + level: ServiceStatusLevels.degraded, + summary: 'This is degraded!', +}; +const unavailable: ServiceStatus = { + level: ServiceStatusLevels.unavailable, + summary: 'This is unavailable!', +}; +const critical: ServiceStatus = { + level: ServiceStatusLevels.critical, + summary: 'This is critical!', +}; + +describe('calculateLegacyStatus', () => { + it('translates the overall status to the legacy format', () => { + const legacyStatus = calculateLegacyStatus({ + overall: available, + core: {} as any, + plugins: {}, + versionWithoutSnapshot: '1.1.1', + }); + + expect(legacyStatus.overall).toEqual({ + state: 'green', + title: 'Green', + nickname: 'Looking good', + icon: 'success', + uiColor: 'secondary', + since: expect.any(String), + }); + }); + + it('combines core and plugins statuses into statuses array in legacy format', () => { + const legacyStatus = calculateLegacyStatus({ + overall: available, + core: { + elasticsearch: degraded, + savedObjects: critical, + }, + plugins: { + a: available, + b: unavailable, + c: degraded, + }, + versionWithoutSnapshot: '1.1.1', + }); + + expect(legacyStatus.statuses).toEqual([ + { + icon: 'warning', + id: 'core:elasticsearch@1.1.1', + message: 'This is degraded!', + since: expect.any(String), + state: 'yellow', + uiColor: 'warning', + }, + { + icon: 'danger', + id: 'core:savedObjects@1.1.1', + message: 'This is critical!', + since: expect.any(String), + state: 'red', + uiColor: 'danger', + }, + { + icon: 'success', + id: 'plugin:a@1.1.1', + message: 'Available', + since: expect.any(String), + state: 'green', + uiColor: 'secondary', + }, + { + icon: 'danger', + id: 'plugin:b@1.1.1', + message: 'This is unavailable!', + since: expect.any(String), + state: 'red', + uiColor: 'danger', + }, + { + icon: 'warning', + id: 'plugin:c@1.1.1', + message: 'This is degraded!', + since: expect.any(String), + state: 'yellow', + uiColor: 'warning', + }, + ]); + }); +}); diff --git a/src/core/server/status/legacy_status.ts b/src/core/server/status/legacy_status.ts new file mode 100644 index 0000000000000..41777ae97c3da --- /dev/null +++ b/src/core/server/status/legacy_status.ts @@ -0,0 +1,158 @@ +/* + * 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 { pick } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { deepFreeze } from '@kbn/std'; + +import { ServiceStatusLevels, ServiceStatus, CoreStatus } from './types'; +import { PluginName } from '../plugins'; + +interface Deps { + overall: ServiceStatus; + core: CoreStatus; + plugins: Record; + versionWithoutSnapshot: string; +} + +export interface LegacyStatusInfo { + overall: LegacyStatusOverall; + statuses: StatusComponentHttp[]; +} + +interface LegacyStatusOverall { + state: LegacyStatusState; + title: string; + nickname: string; + uiColor: LegacyStatusUiColor; + /** ISO-8601 date string w/o timezone */ + since: string; + icon?: string; +} + +type LegacyStatusState = 'green' | 'yellow' | 'red'; +type LegacyStatusIcon = 'danger' | 'warning' | 'success'; +type LegacyStatusUiColor = 'secondary' | 'warning' | 'danger'; + +interface LegacyStateAttr { + id: LegacyStatusState; + state: LegacyStatusState; + title: string; + icon: LegacyStatusIcon; + uiColor: LegacyStatusUiColor; + nickname: string; +} + +export const calculateLegacyStatus = ({ + core, + overall, + plugins, + versionWithoutSnapshot, +}: Deps): LegacyStatusInfo => { + const since = new Date().toISOString(); + const overallLegacy: LegacyStatusOverall = { + since, + ...pick(STATUS_LEVEL_LEGACY_ATTRS[overall.level.toString()], [ + 'state', + 'title', + 'nickname', + 'icon', + 'uiColor', + ]), + }; + const coreStatuses = Object.entries(core).map(([serviceName, s]) => + serviceStatusToHttpComponent(`core:${serviceName}@${versionWithoutSnapshot}`, s, since) + ); + const pluginStatuses = Object.entries(plugins).map(([pluginName, s]) => + serviceStatusToHttpComponent(`plugin:${pluginName}@${versionWithoutSnapshot}`, s, since) + ); + + const componentStatuses: StatusComponentHttp[] = [...coreStatuses, ...pluginStatuses]; + + return { + overall: overallLegacy, + statuses: componentStatuses, + }; +}; + +interface StatusComponentHttp { + id: string; + state: LegacyStatusState; + message: string; + uiColor: LegacyStatusUiColor; + icon: string; + since: string; +} + +const serviceStatusToHttpComponent = ( + serviceName: string, + status: ServiceStatus, + since: string +): StatusComponentHttp => ({ + id: serviceName, + message: status.summary, + since, + ...serviceStatusAttrs(status), +}); + +const serviceStatusAttrs = (status: ServiceStatus) => + pick(STATUS_LEVEL_LEGACY_ATTRS[status.level.toString()], ['state', 'icon', 'uiColor']); + +const STATUS_LEVEL_LEGACY_ATTRS = deepFreeze>({ + [ServiceStatusLevels.critical.toString()]: { + id: 'red', + state: 'red', + title: i18n.translate('core.status.redTitle', { + defaultMessage: 'Red', + }), + icon: 'danger', + uiColor: 'danger', + nickname: 'Danger Will Robinson! Danger!', + }, + [ServiceStatusLevels.unavailable.toString()]: { + id: 'red', + state: 'red', + title: i18n.translate('core.status.redTitle', { + defaultMessage: 'Red', + }), + icon: 'danger', + uiColor: 'danger', + nickname: 'Danger Will Robinson! Danger!', + }, + [ServiceStatusLevels.degraded.toString()]: { + id: 'yellow', + state: 'yellow', + title: i18n.translate('core.status.yellowTitle', { + defaultMessage: 'Yellow', + }), + icon: 'warning', + uiColor: 'warning', + nickname: "I'll be back", + }, + [ServiceStatusLevels.available.toString()]: { + id: 'green', + state: 'green', + title: i18n.translate('core.status.greenTitle', { + defaultMessage: 'Green', + }), + icon: 'success', + uiColor: 'secondary', + nickname: 'Looking good', + }, +}); diff --git a/src/core/server/status/routes/index.ts b/src/core/server/status/routes/index.ts new file mode 100644 index 0000000000000..db2e8daf0b9ac --- /dev/null +++ b/src/core/server/status/routes/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { registerStatusRoute } from './status'; diff --git a/src/core/server/status/routes/integration_tests/status.test.ts b/src/core/server/status/routes/integration_tests/status.test.ts new file mode 100644 index 0000000000000..e0f86342e3a8a --- /dev/null +++ b/src/core/server/status/routes/integration_tests/status.test.ts @@ -0,0 +1,322 @@ +/* + * 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 { BehaviorSubject } from 'rxjs'; +import { first } from 'rxjs/operators'; +import supertest from 'supertest'; +import { omit } from 'lodash'; + +import { createCoreContext, createHttpServer } from '../../../http/test_utils'; +import { ContextService } from '../../../context'; +import { metricsServiceMock } from '../../../metrics/metrics_service.mock'; +import { MetricsServiceSetup } from '../../../metrics'; +import { HttpService, InternalHttpServiceSetup } from '../../../http'; + +import { registerStatusRoute } from '../status'; +import { ServiceStatus, ServiceStatusLevels } from '../../types'; +import { statusServiceMock } from '../../status_service.mock'; + +const coreId = Symbol('core'); + +describe('GET /api/status', () => { + let server: HttpService; + let httpSetup: InternalHttpServiceSetup; + let metrics: jest.Mocked; + + const setupServer = async ({ allowAnonymous = true }: { allowAnonymous?: boolean } = {}) => { + const coreContext = createCoreContext({ coreId }); + const contextService = new ContextService(coreContext); + + server = createHttpServer(coreContext); + httpSetup = await server.setup({ + context: contextService.setup({ pluginDependencies: new Map() }), + }); + + metrics = metricsServiceMock.createSetupContract(); + const status = statusServiceMock.createSetupContract(); + const pluginsStatus$ = new BehaviorSubject>({ + a: { level: ServiceStatusLevels.available, summary: 'a is available' }, + b: { level: ServiceStatusLevels.degraded, summary: 'b is degraded' }, + c: { level: ServiceStatusLevels.unavailable, summary: 'c is unavailable' }, + d: { level: ServiceStatusLevels.critical, summary: 'd is critical' }, + }); + + const router = httpSetup.createRouter(''); + registerStatusRoute({ + router, + config: { + allowAnonymous, + packageInfo: { + branch: 'xbranch', + buildNum: 1234, + buildSha: 'xsha', + dist: true, + version: '9.9.9-SNAPSHOT', + }, + serverName: 'xkibana', + uuid: 'xxxx-xxxxx', + }, + metrics, + status: { + overall$: status.overall$, + core$: status.core$, + plugins$: pluginsStatus$, + }, + }); + + // Register dummy auth provider for testing auth + httpSetup.registerAuth((req, res, auth) => { + if (req.headers.authorization === 'let me in') { + return auth.authenticated(); + } else { + return auth.notHandled(); + } + }); + + await server.start(); + }; + + afterEach(async () => { + await server.stop(); + }); + + describe('allowAnonymous: false', () => { + it('rejects requests with no credentials', async () => { + await setupServer({ allowAnonymous: false }); + await supertest(httpSetup.server.listener).get('/api/status').expect(401); + }); + + it('rejects requests with bad credentials', async () => { + await setupServer({ allowAnonymous: false }); + await supertest(httpSetup.server.listener) + .get('/api/status') + .set('Authorization', 'fake creds') + .expect(401); + }); + + it('accepts authenticated requests', async () => { + await setupServer({ allowAnonymous: false }); + await supertest(httpSetup.server.listener) + .get('/api/status') + .set('Authorization', 'let me in') + .expect(200); + }); + }); + + it('returns basic server info & metrics', async () => { + await setupServer(); + const result = await supertest(httpSetup.server.listener).get('/api/status').expect(200); + + expect(result.body.name).toEqual('xkibana'); + expect(result.body.uuid).toEqual('xxxx-xxxxx'); + expect(result.body.version).toEqual({ + number: '9.9.9', + build_hash: 'xsha', + build_number: 1234, + build_snapshot: true, + }); + const metricsMockValue = await metrics.getOpsMetrics$().pipe(first()).toPromise(); + expect(result.body.metrics).toEqual({ + last_updated: expect.any(String), + collection_interval_in_millis: metrics.collectionInterval, + ...omit(metricsMockValue, ['collected_at']), + requests: { + ...metricsMockValue.requests, + status_codes: metricsMockValue.requests.statusCodes, + }, + }); + }); + + describe('legacy status format', () => { + it('returns legacy status format when no query params provided', async () => { + await setupServer(); + const result = await supertest(httpSetup.server.listener).get('/api/status').expect(200); + expect(result.body.status).toEqual({ + overall: { + icon: 'success', + nickname: 'Looking good', + since: expect.any(String), + state: 'green', + title: 'Green', + uiColor: 'secondary', + }, + statuses: [ + { + icon: 'success', + id: 'core:elasticsearch@9.9.9', + message: 'Service is working', + since: expect.any(String), + state: 'green', + uiColor: 'secondary', + }, + { + icon: 'success', + id: 'core:savedObjects@9.9.9', + message: 'Service is working', + since: expect.any(String), + state: 'green', + uiColor: 'secondary', + }, + { + icon: 'success', + id: 'plugin:a@9.9.9', + message: 'a is available', + since: expect.any(String), + state: 'green', + uiColor: 'secondary', + }, + { + icon: 'warning', + id: 'plugin:b@9.9.9', + message: 'b is degraded', + since: expect.any(String), + state: 'yellow', + uiColor: 'warning', + }, + { + icon: 'danger', + id: 'plugin:c@9.9.9', + message: 'c is unavailable', + since: expect.any(String), + state: 'red', + uiColor: 'danger', + }, + { + icon: 'danger', + id: 'plugin:d@9.9.9', + message: 'd is critical', + since: expect.any(String), + state: 'red', + uiColor: 'danger', + }, + ], + }); + }); + + it('returns legacy status format when v8format=false is provided', async () => { + await setupServer(); + const result = await supertest(httpSetup.server.listener) + .get('/api/status?v8format=false') + .expect(200); + expect(result.body.status).toEqual({ + overall: { + icon: 'success', + nickname: 'Looking good', + since: expect.any(String), + state: 'green', + title: 'Green', + uiColor: 'secondary', + }, + statuses: [ + { + icon: 'success', + id: 'core:elasticsearch@9.9.9', + message: 'Service is working', + since: expect.any(String), + state: 'green', + uiColor: 'secondary', + }, + { + icon: 'success', + id: 'core:savedObjects@9.9.9', + message: 'Service is working', + since: expect.any(String), + state: 'green', + uiColor: 'secondary', + }, + { + icon: 'success', + id: 'plugin:a@9.9.9', + message: 'a is available', + since: expect.any(String), + state: 'green', + uiColor: 'secondary', + }, + { + icon: 'warning', + id: 'plugin:b@9.9.9', + message: 'b is degraded', + since: expect.any(String), + state: 'yellow', + uiColor: 'warning', + }, + { + icon: 'danger', + id: 'plugin:c@9.9.9', + message: 'c is unavailable', + since: expect.any(String), + state: 'red', + uiColor: 'danger', + }, + { + icon: 'danger', + id: 'plugin:d@9.9.9', + message: 'd is critical', + since: expect.any(String), + state: 'red', + uiColor: 'danger', + }, + ], + }); + }); + }); + + describe('v8format', () => { + it('returns new status format when v8format=true is provided', async () => { + await setupServer(); + const result = await supertest(httpSetup.server.listener) + .get('/api/status?v8format=true') + .expect(200); + expect(result.body.status).toEqual({ + core: { + elasticsearch: { + level: 'available', + summary: 'Service is working', + }, + savedObjects: { + level: 'available', + summary: 'Service is working', + }, + }, + overall: { + level: 'available', + summary: 'Service is working', + }, + plugins: { + a: { + level: 'available', + summary: 'a is available', + }, + b: { + level: 'degraded', + summary: 'b is degraded', + }, + c: { + level: 'unavailable', + summary: 'c is unavailable', + }, + d: { + level: 'critical', + summary: 'd is critical', + }, + }, + }); + }); + }); +}); diff --git a/src/core/server/status/routes/status.ts b/src/core/server/status/routes/status.ts new file mode 100644 index 0000000000000..da01a44095529 --- /dev/null +++ b/src/core/server/status/routes/status.ts @@ -0,0 +1,177 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Observable, combineLatest, ReplaySubject } from 'rxjs'; +import { first } from 'rxjs/operators'; +import { schema } from '@kbn/config-schema'; + +import { IRouter } from '../../http'; +import { MetricsServiceSetup } from '../../metrics'; +import { ServiceStatus, CoreStatus } from '../types'; +import { PluginName } from '../../plugins'; +import { calculateLegacyStatus, LegacyStatusInfo } from '../legacy_status'; +import { PackageInfo } from '../../config'; + +const SNAPSHOT_POSTFIX = /-SNAPSHOT$/; + +interface Deps { + router: IRouter; + config: { + allowAnonymous: boolean; + packageInfo: PackageInfo; + serverName: string; + uuid: string; + }; + metrics: MetricsServiceSetup; + status: { + overall$: Observable; + core$: Observable; + plugins$: Observable>; + }; +} + +interface StatusInfo { + overall: ServiceStatus; + core: CoreStatus; + plugins: Record; +} + +interface StatusHttpBody { + name: string; + uuid: string; + version: { + number: string; + build_hash: string; + build_number: number; + build_snapshot: boolean; + }; + status: StatusInfo | LegacyStatusInfo; + metrics: { + /** ISO-8601 date string w/o timezone */ + last_updated: string; + collection_interval_in_millis: number; + process: { + memory: { + heap: { + total_in_bytes: number; + used_in_bytes: number; + size_limit: number; + }; + resident_set_size_in_bytes: number; + }; + event_loop_delay: number; + pid: number; + uptime_in_millis: number; + }; + os: { + load: Record; + memory: { + total_in_bytes: number; + used_in_bytes: number; + free_in_bytes: number; + }; + uptime_in_millis: number; + platform: string; + platformRelease: string; + }; + response_times: { + max_in_millis: number; + }; + requests: { + total: number; + disconnects: number; + statusCodes: Record; + status_codes: Record; + }; + concurrent_connections: number; + }; +} + +export const registerStatusRoute = ({ router, config, metrics, status }: Deps) => { + // Since the status.plugins$ observable is not subscribed to elsewhere, we need to subscribe it here to eagerly load + // the plugins status when Kibana starts up so this endpoint responds quickly on first boot. + const combinedStatus$ = new ReplaySubject< + [ServiceStatus, CoreStatus, Record>] + >(1); + combineLatest([status.overall$, status.core$, status.plugins$]).subscribe(combinedStatus$); + + router.get( + { + path: '/api/status', + options: { + authRequired: !config.allowAnonymous, + tags: ['api'], // ensures that unauthenticated calls receive a 401 rather than a 302 redirect to login page + }, + validate: { + query: schema.object({ + v8format: schema.boolean({ defaultValue: false }), + }), + }, + }, + async (context, req, res) => { + const { version, buildSha, buildNum } = config.packageInfo; + const versionWithoutSnapshot = version.replace(SNAPSHOT_POSTFIX, ''); + const [overall, core, plugins] = await combinedStatus$.pipe(first()).toPromise(); + + let statusInfo: StatusInfo | LegacyStatusInfo; + if (req.query?.v8format) { + statusInfo = { + overall, + core, + plugins, + }; + } else { + statusInfo = calculateLegacyStatus({ + overall, + core, + plugins, + versionWithoutSnapshot, + }); + } + + const lastMetrics = await metrics.getOpsMetrics$().pipe(first()).toPromise(); + + const body: StatusHttpBody = { + name: config.serverName, + uuid: config.uuid, + version: { + number: versionWithoutSnapshot, + build_hash: buildSha, + build_number: buildNum, + build_snapshot: SNAPSHOT_POSTFIX.test(version), + }, + status: statusInfo, + metrics: { + last_updated: lastMetrics.collected_at.toISOString(), + collection_interval_in_millis: metrics.collectionInterval, + os: lastMetrics.os, + process: lastMetrics.process, + response_times: lastMetrics.response_times, + concurrent_connections: lastMetrics.concurrent_connections, + requests: { + ...lastMetrics.requests, + status_codes: lastMetrics.requests.statusCodes, + }, + }, + }; + + return res.ok({ body }); + } + ); +}; diff --git a/src/core/server/status/status_service.mock.ts b/src/core/server/status/status_service.mock.ts index 42b3eecdca310..0ee2d03229a78 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, @@ -43,6 +43,7 @@ const createSetupContractMock = () => { set: jest.fn(), dependencies$: new BehaviorSubject({}), derivedStatus$: new BehaviorSubject(available), + isStatusPageAnonymous: jest.fn().mockReturnValue(false), }; return setupContract; diff --git a/src/core/server/status/status_service.test.ts b/src/core/server/status/status_service.test.ts index dcb1e0a559f5d..afacaff044b6f 100644 --- a/src/core/server/status/status_service.test.ts +++ b/src/core/server/status/status_service.test.ts @@ -24,6 +24,9 @@ import { StatusService } from './status_service'; import { first } from 'rxjs/operators'; import { mockCoreContext } from '../core_context.mock'; import { ServiceStatusLevelSnapshotSerializer } from './test_utils'; +import { environmentServiceMock } from '../environment/environment_service.mock'; +import { httpServiceMock } from '../http/http_service.mock'; +import { metricsServiceMock } from '../metrics/metrics_service.mock'; expect.addSnapshotSerializer(ServiceStatusLevelSnapshotSerializer); @@ -44,18 +47,36 @@ describe('StatusService', () => { summary: 'This is degraded!', }; + type SetupDeps = Parameters[0]; + const setupDeps = (overrides: Partial): SetupDeps => { + return { + elasticsearch: { + status$: of(available), + }, + savedObjects: { + status$: of(available), + }, + pluginDependencies: new Map(), + environment: environmentServiceMock.createSetupContract(), + http: httpServiceMock.createInternalSetupContract(), + metrics: metricsServiceMock.createInternalSetupContract(), + ...overrides, + }; + }; + describe('setup', () => { describe('core$', () => { it('rolls up core status observables into single observable', async () => { - const setup = await service.setup({ - elasticsearch: { - status$: of(available), - }, - savedObjects: { - status$: of(degraded), - }, - pluginDependencies: new Map(), - }); + const setup = await service.setup( + setupDeps({ + elasticsearch: { + status$: of(available), + }, + savedObjects: { + status$: of(degraded), + }, + }) + ); expect(await setup.core$.pipe(first()).toPromise()).toEqual({ elasticsearch: available, savedObjects: degraded, @@ -63,15 +84,16 @@ describe('StatusService', () => { }); it('replays last event', async () => { - const setup = await service.setup({ - elasticsearch: { - status$: of(available), - }, - savedObjects: { - status$: of(degraded), - }, - pluginDependencies: new Map(), - }); + const setup = await service.setup( + setupDeps({ + elasticsearch: { + status$: of(available), + }, + savedObjects: { + status$: of(degraded), + }, + }) + ); const subResult1 = await setup.core$.pipe(first()).toPromise(); const subResult2 = await setup.core$.pipe(first()).toPromise(); const subResult3 = await setup.core$.pipe(first()).toPromise(); @@ -92,15 +114,16 @@ describe('StatusService', () => { it('does not emit duplicate events', async () => { const elasticsearch$ = new BehaviorSubject(available); const savedObjects$ = new BehaviorSubject(degraded); - const setup = await service.setup({ - elasticsearch: { - status$: elasticsearch$, - }, - savedObjects: { - status$: savedObjects$, - }, - pluginDependencies: new Map(), - }); + const setup = await service.setup( + setupDeps({ + elasticsearch: { + status$: elasticsearch$, + }, + savedObjects: { + status$: savedObjects$, + }, + }) + ); const statusUpdates: CoreStatus[] = []; const subscription = setup.core$.subscribe((status) => statusUpdates.push(status)); @@ -155,15 +178,16 @@ describe('StatusService', () => { describe('overall$', () => { it('exposes an overall summary', async () => { - const setup = await service.setup({ - elasticsearch: { - status$: of(degraded), - }, - savedObjects: { - status$: of(degraded), - }, - pluginDependencies: new Map(), - }); + const setup = await service.setup( + setupDeps({ + elasticsearch: { + status$: of(degraded), + }, + savedObjects: { + status$: of(degraded), + }, + }) + ); expect(await setup.overall$.pipe(first()).toPromise()).toMatchObject({ level: ServiceStatusLevels.degraded, summary: '[2] services are degraded', @@ -171,15 +195,16 @@ describe('StatusService', () => { }); it('replays last event', async () => { - const setup = await service.setup({ - elasticsearch: { - status$: of(degraded), - }, - savedObjects: { - status$: of(degraded), - }, - pluginDependencies: new Map(), - }); + const setup = await service.setup( + setupDeps({ + elasticsearch: { + status$: of(degraded), + }, + savedObjects: { + status$: of(degraded), + }, + }) + ); const subResult1 = await setup.overall$.pipe(first()).toPromise(); const subResult2 = await setup.overall$.pipe(first()).toPromise(); const subResult3 = await setup.overall$.pipe(first()).toPromise(); @@ -200,15 +225,16 @@ describe('StatusService', () => { it('does not emit duplicate events', async () => { const elasticsearch$ = new BehaviorSubject(available); const savedObjects$ = new BehaviorSubject(degraded); - const setup = await service.setup({ - elasticsearch: { - status$: elasticsearch$, - }, - savedObjects: { - status$: savedObjects$, - }, - pluginDependencies: new Map(), - }); + const setup = await service.setup( + setupDeps({ + elasticsearch: { + status$: elasticsearch$, + }, + savedObjects: { + status$: savedObjects$, + }, + }) + ); const statusUpdates: ServiceStatus[] = []; const subscription = setup.overall$.subscribe((status) => statusUpdates.push(status)); @@ -256,15 +282,16 @@ describe('StatusService', () => { it('debounces events in quick succession', async () => { const savedObjects$ = new BehaviorSubject(available); - const setup = await service.setup({ - elasticsearch: { - status$: new BehaviorSubject(available), - }, - savedObjects: { - status$: savedObjects$, - }, - pluginDependencies: new Map(), - }); + const setup = await service.setup( + setupDeps({ + elasticsearch: { + status$: new BehaviorSubject(available), + }, + savedObjects: { + status$: savedObjects$, + }, + }) + ); const statusUpdates: ServiceStatus[] = []; const subscription = setup.overall$.subscribe((status) => statusUpdates.push(status)); diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index 8fe65eddb61d3..9acf93f2f8197 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Observable, combineLatest } from 'rxjs'; +import { Observable, combineLatest, Subscription } from 'rxjs'; import { map, distinctUntilChanged, shareReplay, take, debounceTime } from 'rxjs/operators'; import { isDeepStrictEqual } from 'util'; @@ -25,8 +25,12 @@ import { CoreService } from '../../types'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; import { InternalElasticsearchServiceSetup } from '../elasticsearch'; +import { InternalHttpServiceSetup } from '../http'; import { InternalSavedObjectsServiceSetup } from '../saved_objects'; import { PluginName } from '../plugins'; +import { InternalMetricsServiceSetup } from '../metrics'; +import { registerStatusRoute } from './routes'; +import { InternalEnvironmentServiceSetup } from '../environment'; import { config, StatusConfigType } from './status_config'; import { ServiceStatus, CoreStatus, InternalStatusServiceSetup } from './types'; @@ -35,7 +39,10 @@ import { PluginsStatusService } from './plugins_status'; interface SetupDeps { elasticsearch: Pick; + environment: InternalEnvironmentServiceSetup; pluginDependencies: ReadonlyMap; + http: InternalHttpServiceSetup; + metrics: InternalMetricsServiceSetup; savedObjects: Pick; } @@ -44,13 +51,21 @@ export class StatusService implements CoreService { private readonly config$: Observable; private pluginsStatus?: PluginsStatusService; + private overallSubscription?: Subscription; - constructor(coreContext: CoreContext) { + constructor(private readonly coreContext: CoreContext) { this.logger = coreContext.logger.get('status'); this.config$ = coreContext.configService.atPath(config.path); } - public async setup({ elasticsearch, pluginDependencies, savedObjects }: SetupDeps) { + public async setup({ + elasticsearch, + pluginDependencies, + http, + metrics, + savedObjects, + environment, + }: SetupDeps) { const statusConfig = await this.config$.pipe(take(1)).toPromise(); const core$ = this.setupCoreStatus({ elasticsearch, savedObjects }); this.pluginsStatus = new PluginsStatusService({ core$, pluginDependencies }); @@ -73,6 +88,26 @@ export class StatusService implements CoreService { shareReplay(1) ); + // Create an unused subscription to ensure all underlying lazy observables are started. + this.overallSubscription = overall$.subscribe(); + + const router = http.createRouter(''); + registerStatusRoute({ + router, + config: { + allowAnonymous: statusConfig.allowAnonymous, + packageInfo: this.coreContext.env.packageInfo, + serverName: http.getServerInfo().name, + uuid: environment.instanceUuid, + }, + metrics, + status: { + overall$, + plugins$: this.pluginsStatus.getAll$(), + core$, + }, + }); + return { core$, overall$, @@ -87,7 +122,12 @@ export class StatusService implements CoreService { public start() {} - public stop() {} + public stop() { + if (this.overallSubscription) { + this.overallSubscription.unsubscribe(); + this.overallSubscription = undefined; + } + } private setupCoreStatus({ elasticsearch, diff --git a/src/core/server/status/types.ts b/src/core/server/status/types.ts index f884b80316fa8..8efaede79e9d4 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'; /** @@ -71,6 +71,9 @@ export const ServiceStatusLevels = deepFreeze({ available: { toString: () => 'available', valueOf: () => 0, + toJSON() { + return this.toString(); + }, }, /** * Some features may not be working. @@ -78,6 +81,9 @@ export const ServiceStatusLevels = deepFreeze({ degraded: { toString: () => 'degraded', valueOf: () => 1, + toJSON() { + return this.toString(); + }, }, /** * The service is unavailable, but other functions that do not depend on this service should work. @@ -85,6 +91,9 @@ export const ServiceStatusLevels = deepFreeze({ unavailable: { toString: () => 'unavailable', valueOf: () => 2, + toJSON() { + return this.toString(); + }, }, /** * Block all user functions and display the status page, reserved for Core services only. @@ -92,6 +101,9 @@ export const ServiceStatusLevels = deepFreeze({ critical: { toString: () => 'critical', valueOf: () => 3, + toJSON() { + return this.toString(); + }, }, }); @@ -217,11 +229,17 @@ export interface StatusServiceSetup { * through the dependency tree */ derivedStatus$: Observable; + + /** + * Whether or not the status HTTP APIs are available to unauthenticated users when an authentication provider is + * present. + */ + isStatusPageAnonymous: () => boolean; } /** @internal */ -export interface InternalStatusServiceSetup extends Pick { - isStatusPageAnonymous: () => boolean; +export interface InternalStatusServiceSetup + extends Pick { // Namespaced under `plugins` key to improve clarity that these are APIs for plugins specifically. plugins: { set(plugin: PluginName, status$: Observable): void; diff --git a/src/core/server/types.ts b/src/core/server/types.ts index 2433aad1a2be5..f8d2f635671fa 100644 --- a/src/core/server/types.ts +++ b/src/core/server/types.ts @@ -22,4 +22,4 @@ export { PluginOpaqueId } from './plugins/types'; export * from './saved_objects/types'; export * from './ui_settings/types'; export * from './legacy/types'; -export { EnvironmentMode, PackageInfo } from './config/types'; +export type { EnvironmentMode, PackageInfo } from '@kbn/config'; diff --git a/src/core/server/ui_settings/settings/navigation.ts b/src/core/server/ui_settings/settings/navigation.ts index 6483e86a1395a..ec825a2779f38 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 83cea6d7ab3e2..b1ed0dd188cde 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 8598cf7a62287..25062490f5b6b 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/test_helpers/kbn_server.ts b/src/core/test_helpers/kbn_server.ts index 488c4b919d3e4..93a173cdbdece 100644 --- a/src/core/test_helpers/kbn_server.ts +++ b/src/core/test_helpers/kbn_server.ts @@ -17,7 +17,7 @@ * under the License. */ import { Client } from 'elasticsearch'; -import { ToolingLog } from '@kbn/dev-utils'; +import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils'; import { createLegacyEsTestCluster, DEFAULT_SUPERUSER_PASS, @@ -66,7 +66,7 @@ export function createRootWithSettings( settings: Record, cliArgs: Partial = {} ) { - const env = Env.createDefault({ + const env = Env.createDefault(REPO_ROOT, { configs: [], cliArgs: { dev: false, diff --git a/src/core/tsconfig.json b/src/core/tsconfig.json new file mode 100644 index 0000000000000..b8780321e11dd --- /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 0000000000000..a84e1c01d2bd2 --- /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.mock.ts b/src/core/utils/context.mock.ts index de844f3f0f07d..273d64ec8f822 100644 --- a/src/core/utils/context.mock.ts +++ b/src/core/utils/context.mock.ts @@ -21,15 +21,13 @@ import { IContextContainer } from './context'; export type ContextContainerMock = jest.Mocked>; -const createContextMock = () => { +const createContextMock = (mockContext = {}) => { const contextMock: ContextContainerMock = { registerContext: jest.fn(), - createHandler: jest.fn((id, handler) => (...args: any[]) => - Promise.resolve(handler({}, ...args)) - ), + createHandler: jest.fn(), }; contextMock.createHandler.mockImplementation((pluginId, handler) => (...args) => - handler({}, ...args) + handler(mockContext, ...args) ); return contextMock; }; diff --git a/src/core/utils/context.ts b/src/core/utils/context.ts index 941bbceb0cd92..f28d3330b8e36 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 a6df0992f6cc6..c620e4e5ee155 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/build/lib/build.test.ts b/src/dev/build/lib/build.test.ts index 9fdf21cee6567..7ccd5b8e13a2c 100644 --- a/src/dev/build/lib/build.test.ts +++ b/src/dev/build/lib/build.test.ts @@ -17,7 +17,8 @@ * under the License. */ -import { REPO_ROOT, createAbsolutePathSerializer } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { createAbsolutePathSerializer } from '@kbn/dev-utils'; import { Config } from './config'; import { Build } from './build'; diff --git a/src/dev/build/lib/config.test.ts b/src/dev/build/lib/config.test.ts index 0539adc840a6a..bb4d4c000e9d5 100644 --- a/src/dev/build/lib/config.test.ts +++ b/src/dev/build/lib/config.test.ts @@ -19,7 +19,8 @@ import { resolve } from 'path'; -import { createAbsolutePathSerializer, REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { createAbsolutePathSerializer } from '@kbn/dev-utils'; import pkg from '../../../../package.json'; import { Config } from './config'; diff --git a/src/dev/build/tasks/build_kibana_platform_plugins.ts b/src/dev/build/tasks/build_kibana_platform_plugins.ts index 48625078e9bd1..44b441ad898c3 100644 --- a/src/dev/build/tasks/build_kibana_platform_plugins.ts +++ b/src/dev/build/tasks/build_kibana_platform_plugins.ts @@ -17,7 +17,8 @@ * under the License. */ -import { CiStatsReporter, REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; +import { CiStatsReporter } from '@kbn/dev-utils'; import { runOptimizer, OptimizerConfig, diff --git a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts index a5b9e01714f38..cd197db251093 100644 --- a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts @@ -20,12 +20,12 @@ import { readFileSync } from 'fs'; import Path from 'path'; +import { REPO_ROOT } from '@kbn/utils'; import { ToolingLog, ToolingLogCollectingWriter, createAbsolutePathSerializer, createRecursiveSerializer, - REPO_ROOT, } from '@kbn/dev-utils'; import { Config } from '../../lib'; diff --git a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts index 1a850890a33fe..9b03dcd828cf9 100644 --- a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts @@ -20,12 +20,12 @@ import Path from 'path'; import Fs from 'fs'; +import { REPO_ROOT } from '@kbn/utils'; import { ToolingLog, ToolingLogCollectingWriter, createAnyInstanceSerializer, createRecursiveSerializer, - REPO_ROOT, } from '@kbn/dev-utils'; import { Config, Platform } from '../../lib'; diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile index d235bfe9d6fbc..24649a52b729b 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile @@ -85,7 +85,6 @@ RUN groupadd --gid 1000 kibana && \ useradd --uid 1000 --gid 1000 \ --home-dir /usr/share/kibana --no-create-home \ kibana -USER kibana LABEL org.label-schema.build-date="{{dockerBuildDate}}" \ org.label-schema.license="{{license}}" \ @@ -115,8 +114,13 @@ LABEL name="Kibana" \ release="1" \ summary="Kibana" \ description="Your window into the Elastic Stack." + +RUN mkdir /licenses && \ + cp LICENSE.txt /licenses/LICENSE {{/ubi}} +USER kibana + ENTRYPOINT ["/usr/local/bin/dumb-init", "--"] CMD ["/usr/local/bin/kibana-docker"] diff --git a/src/dev/ci_setup/setup.sh b/src/dev/ci_setup/setup.sh index 3351170c29e01..aabc1e75b9025 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 5757d72f99582..72ec73ad810e6 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/constants.ts b/src/dev/constants.ts index d1b89719c69b7..9ce18ffdc9bcd 100644 --- a/src/dev/constants.ts +++ b/src/dev/constants.ts @@ -17,10 +17,6 @@ * under the License. */ -import { dirname } from 'path'; - -export const REPO_ROOT = dirname(require.resolve('../../package.json')); - // Files in directories of this name will be treated as Jest integration tests with instances of // Elasticsearch and the Kibana server. export const RESERVED_DIR_JEST_INTEGRATION_TESTS = 'integration_tests'; diff --git a/src/dev/eslint/lint_files.ts b/src/dev/eslint/lint_files.ts index ba16163fc9bd3..ebd6bedb5f56c 100644 --- a/src/dev/eslint/lint_files.ts +++ b/src/dev/eslint/lint_files.ts @@ -19,9 +19,9 @@ import { CLIEngine } from 'eslint'; +import { REPO_ROOT } from '@kbn/utils'; import { createFailError, ToolingLog } from '@kbn/dev-utils'; import { File } from '../file'; -import { REPO_ROOT } from '../constants'; /** * Lints a list of files with eslint. eslint reports are written to the log diff --git a/src/dev/license_checker/run_check_licenses_cli.ts b/src/dev/license_checker/run_check_licenses_cli.ts index 0267a1a90d4fe..70ebb43c788f9 100644 --- a/src/dev/license_checker/run_check_licenses_cli.ts +++ b/src/dev/license_checker/run_check_licenses_cli.ts @@ -17,12 +17,12 @@ * under the License. */ +import { REPO_ROOT } from '@kbn/utils'; import { run } from '@kbn/dev-utils'; import { getInstalledPackages } from '../npm'; import { LICENSE_WHITELIST, DEV_ONLY_LICENSE_WHITELIST, LICENSE_OVERRIDES } from './config'; import { assertLicensesValid } from './valid'; -import { REPO_ROOT } from '../constants'; run( async ({ log, flags }) => { diff --git a/src/dev/notice/cli.js b/src/dev/notice/cli.js index 34217ef48a33b..ea0f884bb1ea0 100644 --- a/src/dev/notice/cli.js +++ b/src/dev/notice/cli.js @@ -22,9 +22,9 @@ import { resolve } from 'path'; import getopts from 'getopts'; import dedent from 'dedent'; +import { REPO_ROOT } from '@kbn/utils'; import { ToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; -import { REPO_ROOT } from '../constants'; import { generateNoticeFromSource } from './generate_notice_from_source'; const unknownFlags = []; diff --git a/src/dev/npm/integration_tests/installed_packages.test.ts b/src/dev/npm/integration_tests/installed_packages.test.ts index 58c954cbc12f7..3aeb5b106c2ec 100644 --- a/src/dev/npm/integration_tests/installed_packages.test.ts +++ b/src/dev/npm/integration_tests/installed_packages.test.ts @@ -21,8 +21,8 @@ import { resolve, sep } from 'path'; import { uniq } from 'lodash'; +import { REPO_ROOT } from '@kbn/utils'; import { getInstalledPackages, InstalledPackage } from '../installed_packages'; -import { REPO_ROOT } from '../../constants'; const FIXTURE1_ROOT = resolve(__dirname, '__fixtures__/fixture1'); diff --git a/src/dev/precommit_hook/get_files_for_commit.js b/src/dev/precommit_hook/get_files_for_commit.js index dc5560be0d1ad..e700b58782174 100644 --- a/src/dev/precommit_hook/get_files_for_commit.js +++ b/src/dev/precommit_hook/get_files_for_commit.js @@ -20,7 +20,7 @@ import SimpleGit from 'simple-git'; import { fromNode as fcb } from 'bluebird'; -import { REPO_ROOT } from '../constants'; +import { REPO_ROOT } from '@kbn/utils'; import { File } from '../file'; /** diff --git a/src/dev/run_check_file_casing.js b/src/dev/run_check_file_casing.js index bdbf1827897e7..3acff74430abe 100644 --- a/src/dev/run_check_file_casing.js +++ b/src/dev/run_check_file_casing.js @@ -19,9 +19,9 @@ import globby from 'globby'; +import { REPO_ROOT } from '@kbn/utils'; import { run } from '@kbn/dev-utils'; import { File } from './file'; -import { REPO_ROOT } from './constants'; import { checkFileCasing } from './precommit_hook/check_file_casing'; run(async ({ log }) => { diff --git a/src/dev/run_check_lockfile_symlinks.js b/src/dev/run_check_lockfile_symlinks.js index 5ebb7952e2cf3..c4e040c4bffa3 100644 --- a/src/dev/run_check_lockfile_symlinks.js +++ b/src/dev/run_check_lockfile_symlinks.js @@ -21,9 +21,9 @@ import { existsSync, lstatSync, readFileSync, readlinkSync } from 'fs'; import globby from 'globby'; import { dirname } from 'path'; +import { REPO_ROOT } from '@kbn/utils'; import { run, createFailError } from '@kbn/dev-utils'; -import { REPO_ROOT } from './constants'; import { File } from './file'; import { matchesAnyGlob } from './globs'; diff --git a/src/dev/storybook/commands/clean.ts b/src/dev/storybook/commands/clean.ts index 328c4d9e2c23c..539757d0e7ad2 100644 --- a/src/dev/storybook/commands/clean.ts +++ b/src/dev/storybook/commands/clean.ts @@ -18,7 +18,7 @@ */ import { ToolingLog } from '@kbn/dev-utils'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { join } from 'path'; import del from 'del'; diff --git a/src/dev/storybook/run_storybook_cli.ts b/src/dev/storybook/run_storybook_cli.ts index 7f97cff91aaaa..b1e8d882e5352 100644 --- a/src/dev/storybook/run_storybook_cli.ts +++ b/src/dev/storybook/run_storybook_cli.ts @@ -19,7 +19,7 @@ import { join } from 'path'; import { run, createFlagError } from '@kbn/dev-utils'; -import { REPO_ROOT } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; import { storybookAliases } from './aliases'; import { clean } from './commands/clean'; diff --git a/src/dev/typescript/get_ts_project_for_absolute_path.ts b/src/dev/typescript/get_ts_project_for_absolute_path.ts index 54d7e950834a4..0057263660b77 100644 --- a/src/dev/typescript/get_ts_project_for_absolute_path.ts +++ b/src/dev/typescript/get_ts_project_for_absolute_path.ts @@ -18,7 +18,7 @@ */ import { relative, resolve } from 'path'; -import { REPO_ROOT } from '../constants'; +import { REPO_ROOT } from '@kbn/utils'; import { File } from '../file'; import { Project } from './project'; import { PROJECTS } from './projects'; diff --git a/src/dev/typescript/project.ts b/src/dev/typescript/project.ts index 4cf87d812a0b3..811c1792f1988 100644 --- a/src/dev/typescript/project.ts +++ b/src/dev/typescript/project.ts @@ -23,7 +23,7 @@ import { basename, dirname, relative, resolve } from 'path'; import { IMinimatch, Minimatch } from 'minimatch'; import { parseConfigFileTextToJson } from 'typescript'; -import { REPO_ROOT } from '../constants'; +import { REPO_ROOT } from '@kbn/utils'; function makeMatchers(directory: string, patterns: string[]) { return patterns.map( diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index e18c82b5b9e96..4d1e549e192b6 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -19,12 +19,13 @@ import glob from 'glob'; import { resolve } from 'path'; -import { REPO_ROOT } from '../constants'; +import { REPO_ROOT } from '@kbn/utils'; 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_check_ts_projects_cli.ts b/src/dev/typescript/run_check_ts_projects_cli.ts index b0c125ea47829..a095bef047711 100644 --- a/src/dev/typescript/run_check_ts_projects_cli.ts +++ b/src/dev/typescript/run_check_ts_projects_cli.ts @@ -22,8 +22,8 @@ import { resolve } from 'path'; import execa from 'execa'; import { run } from '@kbn/dev-utils'; +import { REPO_ROOT } from '@kbn/utils'; -const REPO_ROOT = resolve(__dirname, '../../../'); import { File } from '../file'; import { PROJECTS } from './projects'; diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts index e1fca23274a5a..00968b7259a30 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 ce7a500a00dc8..f8736fb30f90e 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 e895f83fe6901..cb86c3220bec1 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 663542618375a..8827dc53c5275 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 a5eefd140c8fa..107e5f6387833 100644 --- a/src/legacy/server/kbn_server.js +++ b/src/legacy/server/kbn_server.js @@ -28,8 +28,6 @@ import httpMixin from './http'; 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'; @@ -91,10 +89,6 @@ export default class KbnServer { loggingMixin, 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 0754557044583..2298d83aa2857 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 c99373ebe0a63..492d1ffc8d167 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 d7b9da1292252..0000000000000 --- 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/legacy/server/plugins/lib/plugin.js b/src/legacy/server/plugins/lib/plugin.js index 2b392d13d595a..48389061199ff 100644 --- a/src/legacy/server/plugins/lib/plugin.js +++ b/src/legacy/server/plugins/lib/plugin.js @@ -79,12 +79,7 @@ export class Plugin { ); } - // Many of the plugins are simply adding static assets to the server and we don't need - // to track their "status". Since plugins must have an init() function to even set its status - // we shouldn't even create a status unless the plugin can use it. if (this.externalInit) { - this.status = kbnServer.status.createForPlugin(this); - server.expose('status', this.status); await this.externalInit(server, options); } }; @@ -93,12 +88,6 @@ export class Plugin { plugin: { register, name: id, version }, options: config.has(configPrefix) ? config.get(configPrefix) : null, }); - - // Only change the plugin status to green if the - // initial status has not been changed - if (this.status && this.status.state === 'uninitialized') { - this.status.green('Ready'); - } } async postInit() { diff --git a/src/legacy/server/status/index.js b/src/legacy/server/status/index.js deleted file mode 100644 index ab7ec471a67ff..0000000000000 --- a/src/legacy/server/status/index.js +++ /dev/null @@ -1,61 +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 ServerStatus from './server_status'; -import { Metrics } from './lib/metrics'; -import { registerStatusApi, registerStatsApi } from './routes'; -import Oppsy from 'oppsy'; -import { cloneDeep } from 'lodash'; -import { getOSInfo } from './lib/get_os_info'; - -export function statusMixin(kbnServer, server, config) { - kbnServer.status = new ServerStatus(kbnServer.server); - const { usageCollection } = server.newPlatform.setup.plugins; - - const metrics = new Metrics(config, server); - - const oppsy = new Oppsy(server); - oppsy.on('ops', (event) => { - // Oppsy has a bad race condition that will modify this data before - // we ship it off to the buffer. Let's create our copy first. - event = cloneDeep(event); - // Oppsy used to provide this, but doesn't anymore. Grab it ourselves. - server.listener.getConnections((_, count) => { - event.concurrent_connections = count; - - // captures (performs transforms on) the latest event data and stashes - // the metrics for status/stats API payload - metrics.capture(event).then((data) => { - kbnServer.metrics = data; - }); - }); - }); - oppsy.start(config.get('ops.interval')); - - server.events.on('stop', () => { - oppsy.stop(); - }); - - // init routes - registerStatusApi(kbnServer, server, config); - registerStatsApi(usageCollection, server, config, kbnServer); - - // expore shared functionality - server.decorate('server', 'getOSInfo', getOSInfo); -} diff --git a/src/legacy/server/status/lib/__mocks__/_fs_stubs.js b/src/legacy/server/status/lib/__mocks__/_fs_stubs.js deleted file mode 100644 index 2be6402baa5fe..0000000000000 --- a/src/legacy/server/status/lib/__mocks__/_fs_stubs.js +++ /dev/null @@ -1,86 +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. - */ - -export function cGroups(hierarchy) { - if (!hierarchy) { - hierarchy = Math.random().toString(36).substring(7); - } - - const cpuAcctDir = `/sys/fs/cgroup/cpuacct/${hierarchy}`; - const cpuDir = `/sys/fs/cgroup/cpu/${hierarchy}`; - - const cGroupContents = [ - '10:freezer:/', - '9:net_cls,net_prio:/', - '8:pids:/', - '7:blkio:/', - '6:memory:/', - '5:devices:/user.slice', - '4:hugetlb:/', - '3:perf_event:/', - '2:cpu,cpuacct,cpuset:/' + hierarchy, - '1:name=systemd:/user.slice/user-1000.slice/session-2359.scope', - ].join('\n'); - - const cpuStatContents = ['nr_periods 0', 'nr_throttled 10', 'throttled_time 20'].join('\n'); - - return { - hierarchy, - cGroupContents, - cpuStatContents, - cpuAcctDir, - cpuDir, - files: { - '/proc/self/cgroup': cGroupContents, - [`${cpuAcctDir}/cpuacct.usage`]: '357753491408', - [`${cpuDir}/cpu.cfs_period_us`]: '100000', - [`${cpuDir}/cpu.cfs_quota_us`]: '5000', - [`${cpuDir}/cpu.stat`]: cpuStatContents, - }, - }; -} - -class FSError extends Error { - constructor(fileName, code) { - super('Stub File System Stub Error: ' + fileName); - this.code = code; - this.stack = null; - } -} - -let _mockFiles = Object.create({}); - -export const setMockFiles = (mockFiles) => { - _mockFiles = Object.create({}); - if (mockFiles) { - const files = Object.keys(mockFiles); - for (const file of files) { - _mockFiles[file] = mockFiles[file]; - } - } -}; - -export const readFileMock = (fileName, callback) => { - if (_mockFiles.hasOwnProperty(fileName)) { - callback(null, _mockFiles[fileName]); - } else { - const err = new FSError(fileName, 'ENOENT'); - callback(err, null); - } -}; diff --git a/src/legacy/server/status/lib/cgroup.js b/src/legacy/server/status/lib/cgroup.js deleted file mode 100644 index 4d21cafbedcaa..0000000000000 --- a/src/legacy/server/status/lib/cgroup.js +++ /dev/null @@ -1,173 +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 fs from 'fs'; -import { promisify } from 'bluebird'; -import { join as joinPath } from 'path'; - -// Logic from elasticsearch/core/src/main/java/org/elasticsearch/monitor/os/OsProbe.java - -const CONTROL_GROUP_RE = new RegExp('\\d+:([^:]+):(/.*)'); -const CONTROLLER_SEPARATOR_RE = ','; - -const PROC_SELF_CGROUP_FILE = '/proc/self/cgroup'; -const PROC_CGROUP_CPU_DIR = '/sys/fs/cgroup/cpu'; -const PROC_CGROUP_CPUACCT_DIR = '/sys/fs/cgroup/cpuacct'; - -const GROUP_CPUACCT = 'cpuacct'; -const CPUACCT_USAGE_FILE = 'cpuacct.usage'; - -const GROUP_CPU = 'cpu'; -const CPU_FS_PERIOD_US_FILE = 'cpu.cfs_period_us'; -const CPU_FS_QUOTA_US_FILE = 'cpu.cfs_quota_us'; -const CPU_STATS_FILE = 'cpu.stat'; - -const readFile = promisify(fs.readFile); - -export function readControlGroups() { - return readFile(PROC_SELF_CGROUP_FILE).then((data) => { - const response = {}; - - data - .toString() - .split(/\n/) - .forEach((line) => { - const matches = line.match(CONTROL_GROUP_RE); - - if (matches === null) { - return; - } - - const controllers = matches[1].split(CONTROLLER_SEPARATOR_RE); - controllers.forEach((controller) => { - response[controller] = matches[2]; - }); - }); - - return response; - }); -} - -function fileContentsToInteger(path) { - return readFile(path).then((data) => { - return parseInt(data.toString(), 10); - }); -} - -function readCPUAcctUsage(controlGroup) { - return fileContentsToInteger(joinPath(PROC_CGROUP_CPUACCT_DIR, controlGroup, CPUACCT_USAGE_FILE)); -} - -function readCPUFsPeriod(controlGroup) { - return fileContentsToInteger(joinPath(PROC_CGROUP_CPU_DIR, controlGroup, CPU_FS_PERIOD_US_FILE)); -} - -function readCPUFsQuota(controlGroup) { - return fileContentsToInteger(joinPath(PROC_CGROUP_CPU_DIR, controlGroup, CPU_FS_QUOTA_US_FILE)); -} - -export function readCPUStat(controlGroup) { - return new Promise((resolve, reject) => { - const stat = { - number_of_elapsed_periods: -1, - number_of_times_throttled: -1, - time_throttled_nanos: -1, - }; - - readFile(joinPath(PROC_CGROUP_CPU_DIR, controlGroup, CPU_STATS_FILE)) - .then((data) => { - data - .toString() - .split(/\n/) - .forEach((line) => { - const fields = line.split(/\s+/); - - switch (fields[0]) { - case 'nr_periods': - stat.number_of_elapsed_periods = parseInt(fields[1], 10); - break; - - case 'nr_throttled': - stat.number_of_times_throttled = parseInt(fields[1], 10); - break; - - case 'throttled_time': - stat.time_throttled_nanos = parseInt(fields[1], 10); - break; - } - }); - - resolve(stat); - }) - .catch((err) => { - if (err.code === 'ENOENT') { - return resolve(stat); - } - - reject(err); - }); - }); -} - -export function getAllStats(options = {}) { - return new Promise((resolve, reject) => { - readControlGroups() - .then((groups) => { - const cpuPath = options.cpuPath || groups[GROUP_CPU]; - const cpuAcctPath = options.cpuAcctPath || groups[GROUP_CPUACCT]; - - // prevents undefined cgroup paths - if (!cpuPath || !cpuAcctPath) { - return resolve(null); - } - - return Promise.all([ - readCPUAcctUsage(cpuAcctPath), - readCPUFsPeriod(cpuPath), - readCPUFsQuota(cpuPath), - readCPUStat(cpuPath), - ]) - .then(([cpuAcctUsage, cpuFsPeriod, cpuFsQuota, cpuStat]) => { - resolve({ - cpuacct: { - control_group: cpuAcctPath, - usage_nanos: cpuAcctUsage, - }, - - cpu: { - control_group: cpuPath, - cfs_period_micros: cpuFsPeriod, - cfs_quota_micros: cpuFsQuota, - stat: cpuStat, - }, - }); - }) - .catch(rejectUnlessFileNotFound); - }) - .catch(rejectUnlessFileNotFound); - - function rejectUnlessFileNotFound(err) { - if (err.code === 'ENOENT') { - resolve(null); - } else { - reject(err); - } - } - }); -} diff --git a/src/legacy/server/status/lib/cgroup.test.js b/src/legacy/server/status/lib/cgroup.test.js deleted file mode 100644 index 62feba45d1b3c..0000000000000 --- a/src/legacy/server/status/lib/cgroup.test.js +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock('fs', () => ({ - readFile: jest.fn(), -})); - -import fs from 'fs'; -import { cGroups as cGroupsFsStub, setMockFiles, readFileMock } from './__mocks__/_fs_stubs'; -import { getAllStats, readControlGroups, readCPUStat } from './cgroup'; - -describe('Control Group', function () { - const fsStub = cGroupsFsStub(); - - beforeAll(() => { - fs.readFile.mockImplementation(readFileMock); - }); - - afterEach(() => { - setMockFiles(); - }); - - describe('readControlGroups', () => { - it('parses the file', async () => { - setMockFiles({ '/proc/self/cgroup': fsStub.cGroupContents }); - const cGroup = await readControlGroups(); - - expect(cGroup).toEqual({ - freezer: '/', - net_cls: '/', - net_prio: '/', - pids: '/', - blkio: '/', - memory: '/', - devices: '/user.slice', - hugetlb: '/', - perf_event: '/', - cpu: `/${fsStub.hierarchy}`, - cpuacct: `/${fsStub.hierarchy}`, - cpuset: `/${fsStub.hierarchy}`, - 'name=systemd': '/user.slice/user-1000.slice/session-2359.scope', - }); - }); - }); - - describe('readCPUStat', () => { - it('parses the file', async () => { - setMockFiles({ '/sys/fs/cgroup/cpu/fakeGroup/cpu.stat': fsStub.cpuStatContents }); - const cpuStat = await readCPUStat('fakeGroup'); - - expect(cpuStat).toEqual({ - number_of_elapsed_periods: 0, - number_of_times_throttled: 10, - time_throttled_nanos: 20, - }); - }); - - it('returns default stats for missing file', async () => { - setMockFiles(); - const cpuStat = await readCPUStat('fakeGroup'); - - expect(cpuStat).toEqual({ - number_of_elapsed_periods: -1, - number_of_times_throttled: -1, - time_throttled_nanos: -1, - }); - }); - }); - - describe('getAllStats', () => { - it('can override the cpu group path', async () => { - setMockFiles({ - '/proc/self/cgroup': fsStub.cGroupContents, - [`${fsStub.cpuAcctDir}/cpuacct.usage`]: '357753491408', - '/sys/fs/cgroup/cpu/docker/cpu.cfs_period_us': '100000', - '/sys/fs/cgroup/cpu/docker/cpu.cfs_quota_us': '5000', - '/sys/fs/cgroup/cpu/docker/cpu.stat': fsStub.cpuStatContents, - }); - - const stats = await getAllStats({ cpuPath: '/docker' }); - - expect(stats).toEqual({ - cpuacct: { - control_group: `/${fsStub.hierarchy}`, - usage_nanos: 357753491408, - }, - cpu: { - control_group: '/docker', - cfs_period_micros: 100000, - cfs_quota_micros: 5000, - stat: { - number_of_elapsed_periods: 0, - number_of_times_throttled: 10, - time_throttled_nanos: 20, - }, - }, - }); - }); - - it('handles an undefined control group', async () => { - setMockFiles({ - '/proc/self/cgroup': '', - [`${fsStub.cpuAcctDir}/cpuacct.usage`]: '357753491408', - [`${fsStub.cpuDir}/cpu.stat`]: fsStub.cpuStatContents, - [`${fsStub.cpuDir}/cpu.cfs_period_us`]: '100000', - [`${fsStub.cpuDir}/cpu.cfs_quota_us`]: '5000', - }); - - const stats = await getAllStats(); - - expect(stats).toBe(null); - }); - - it('can override the cpuacct group path', async () => { - setMockFiles({ - '/proc/self/cgroup': fsStub.cGroupContents, - '/sys/fs/cgroup/cpuacct/docker/cpuacct.usage': '357753491408', - [`${fsStub.cpuDir}/cpu.cfs_period_us`]: '100000', - [`${fsStub.cpuDir}/cpu.cfs_quota_us`]: '5000', - [`${fsStub.cpuDir}/cpu.stat`]: fsStub.cpuStatContents, - }); - - const stats = await getAllStats({ cpuAcctPath: '/docker' }); - - expect(stats).toEqual({ - cpuacct: { - control_group: '/docker', - usage_nanos: 357753491408, - }, - cpu: { - control_group: `/${fsStub.hierarchy}`, - cfs_period_micros: 100000, - cfs_quota_micros: 5000, - stat: { - number_of_elapsed_periods: 0, - number_of_times_throttled: 10, - time_throttled_nanos: 20, - }, - }, - }); - }); - - it('extracts control group stats', async () => { - setMockFiles(fsStub.files); - const stats = await getAllStats(); - - expect(stats).toEqual({ - cpuacct: { - control_group: `/${fsStub.hierarchy}`, - usage_nanos: 357753491408, - }, - cpu: { - control_group: `/${fsStub.hierarchy}`, - cfs_period_micros: 100000, - cfs_quota_micros: 5000, - stat: { - number_of_elapsed_periods: 0, - number_of_times_throttled: 10, - time_throttled_nanos: 20, - }, - }, - }); - }); - - it('returns null when all files are missing', async () => { - setMockFiles(); - const stats = await getAllStats(); - expect(stats).toBeNull(); - }); - - it('returns null if CPU accounting files are missing', async () => { - setMockFiles({ - '/proc/self/cgroup': fsStub.cGroupContents, - [`${fsStub.cpuDir}/cpu.stat`]: fsStub.cpuStatContents, - }); - const stats = await getAllStats(); - - expect(stats).toBeNull(); - }); - - it('returns -1 stat values if cpuStat file is missing', async () => { - setMockFiles({ - '/proc/self/cgroup': fsStub.cGroupContents, - [`${fsStub.cpuAcctDir}/cpuacct.usage`]: '357753491408', - [`${fsStub.cpuDir}/cpu.cfs_period_us`]: '100000', - [`${fsStub.cpuDir}/cpu.cfs_quota_us`]: '5000', - }); - const stats = await getAllStats(); - - expect(stats).toEqual({ - cpu: { - cfs_period_micros: 100000, - cfs_quota_micros: 5000, - control_group: `/${fsStub.hierarchy}`, - stat: { - number_of_elapsed_periods: -1, - number_of_times_throttled: -1, - time_throttled_nanos: -1, - }, - }, - cpuacct: { - control_group: `/${fsStub.hierarchy}`, - usage_nanos: 357753491408, - }, - }); - }); - }); -}); diff --git a/src/legacy/server/status/lib/get_kibana_info_for_stats.js b/src/legacy/server/status/lib/get_kibana_info_for_stats.js deleted file mode 100644 index 62628a2c40ff9..0000000000000 --- a/src/legacy/server/status/lib/get_kibana_info_for_stats.js +++ /dev/null @@ -1,47 +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 { get } from 'lodash'; - -const snapshotRegex = /-snapshot/i; - -/** - * This provides a meta data attribute along with Kibana stats. - * - * @param {Object} kbnServer manager of Kibana services - see `src/legacy/server/kbn_server` in Kibana core - * @param {Object} config Server config - * @param {String} host Kibana host - * @return {Object} The object containing a "kibana" field and source instance details. - */ -export function getKibanaInfoForStats(server, kbnServer) { - const config = server.config(); - const status = kbnServer.status.toJSON(); - - return { - uuid: config.get('server.uuid'), - name: config.get('server.name'), - index: config.get('kibana.index'), - host: config.get('server.host'), - locale: config.get('i18n.locale'), - transport_address: `${config.get('server.host')}:${config.get('server.port')}`, - version: kbnServer.version.replace(snapshotRegex, ''), - snapshot: snapshotRegex.test(kbnServer.version), - status: get(status, 'overall.state'), - }; -} diff --git a/src/legacy/server/status/lib/get_os_info.js b/src/legacy/server/status/lib/get_os_info.js deleted file mode 100644 index e3835fec34c88..0000000000000 --- a/src/legacy/server/status/lib/get_os_info.js +++ /dev/null @@ -1,48 +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 os from 'os'; -import getos from 'getos'; -import { promisify } from 'util'; - -/** - * Returns an object of OS information/ - */ -export async function getOSInfo() { - const osInfo = { - platform: os.platform(), - // Include the platform name in the release to avoid grouping unrelated platforms together. - // release 1.0 across windows, linux, and darwin don't mean anything useful. - platformRelease: `${os.platform()}-${os.release()}`, - }; - - // Get distribution information for linux - if (os.platform() === 'linux') { - try { - const distro = await promisify(getos)(); - osInfo.distro = distro.dist; - // Include distro name in release for same reason as above. - osInfo.distroRelease = `${distro.dist}-${distro.release}`; - } catch (e) { - // ignore errors - } - } - - return osInfo; -} diff --git a/src/legacy/server/status/lib/get_os_info.test.js b/src/legacy/server/status/lib/get_os_info.test.js deleted file mode 100644 index 11af7e1588090..0000000000000 --- a/src/legacy/server/status/lib/get_os_info.test.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock('os', () => ({ - platform: jest.fn(), - release: jest.fn(), -})); -jest.mock('getos'); - -import os from 'os'; -import getos from 'getos'; - -import { getOSInfo } from './get_os_info'; - -describe('getOSInfo', () => { - it('returns basic OS info on non-linux', async () => { - os.platform.mockImplementation(() => 'darwin'); - os.release.mockImplementation(() => '1.0.0'); - - const osInfo = await getOSInfo(); - - expect(osInfo).toEqual({ - platform: 'darwin', - platformRelease: 'darwin-1.0.0', - }); - }); - - it('returns basic OS info and distro info on linux', async () => { - os.platform.mockImplementation(() => 'linux'); - os.release.mockImplementation(() => '4.9.93-linuxkit-aufs'); - - // Mock getos response - getos.mockImplementation((cb) => - cb(null, { - os: 'linux', - dist: 'Ubuntu Linux', - codename: 'precise', - release: '12.04', - }) - ); - - const osInfo = await getOSInfo(); - - expect(osInfo).toEqual({ - platform: 'linux', - platformRelease: 'linux-4.9.93-linuxkit-aufs', - // linux distro info - distro: 'Ubuntu Linux', - distroRelease: 'Ubuntu Linux-12.04', - }); - }); -}); diff --git a/src/legacy/server/status/lib/metrics.js b/src/legacy/server/status/lib/metrics.js deleted file mode 100644 index 478bf0829b1aa..0000000000000 --- a/src/legacy/server/status/lib/metrics.js +++ /dev/null @@ -1,146 +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 os from 'os'; -import v8 from 'v8'; -import { get, isObject, merge } from 'lodash'; -import { keysToSnakeCaseShallow } from './case_conversion'; -import { getAllStats as cGroupStats } from './cgroup'; -import { getOSInfo } from './get_os_info'; - -const requestDefaults = { - disconnects: 0, - statusCodes: {}, - total: 0, -}; - -export class Metrics { - constructor(config, server) { - this.config = config; - this.server = server; - this.checkCGroupStats = true; - } - - static getStubMetrics() { - return { - process: { - memory: { - heap: {}, - }, - }, - os: { - cpu: {}, - memory: {}, - }, - response_times: {}, - requests: {}, - }; - } - - async capture(hapiEvent) { - const timestamp = new Date().toISOString(); - const event = await this.captureEvent(hapiEvent); - const cgroup = await this.captureCGroupsIfAvailable(); - - const metrics = { - last_updated: timestamp, - collection_interval_in_millis: this.config.get('ops.interval'), - }; - - return merge(metrics, event, cgroup); - } - - async captureEvent(hapiEvent) { - const heapStats = v8.getHeapStatistics(); - const port = this.config.get('server.port'); - const avgInMillis = get(hapiEvent, ['responseTimes', port, 'avg']); // sadly, it's possible for this to be NaN - const maxInMillis = get(hapiEvent, ['responseTimes', port, 'max']); - - return { - process: { - memory: { - heap: { - // https://nodejs.org/docs/latest-v8.x/api/process.html#process_process_memoryusage - total_in_bytes: get(hapiEvent, 'psmem.heapTotal'), - used_in_bytes: get(hapiEvent, 'psmem.heapUsed'), - size_limit: heapStats.heap_size_limit, - }, - resident_set_size_in_bytes: get(hapiEvent, 'psmem.rss'), - }, - event_loop_delay: get(hapiEvent, 'psdelay'), - pid: process.pid, - uptime_in_millis: process.uptime() * 1000, - }, - os: { - load: { - '1m': get(hapiEvent, 'osload.0'), - '5m': get(hapiEvent, 'osload.1'), - '15m': get(hapiEvent, 'osload.2'), - }, - memory: { - total_in_bytes: os.totalmem(), - free_in_bytes: os.freemem(), - used_in_bytes: get(hapiEvent, 'osmem.total') - get(hapiEvent, 'osmem.free'), - }, - uptime_in_millis: os.uptime() * 1000, - ...(await getOSInfo()), - }, - response_times: { - avg_in_millis: isNaN(avgInMillis) ? undefined : avgInMillis, // convert NaN to undefined - max_in_millis: maxInMillis, - }, - requests: { - ...requestDefaults, - ...keysToSnakeCaseShallow(get(hapiEvent, ['requests', port])), - }, - concurrent_connections: hapiEvent.concurrent_connections, - }; - } - - async captureCGroups() { - try { - const cgroup = await cGroupStats({ - cpuPath: this.config.get('ops.cGroupOverrides.cpuPath'), - cpuAcctPath: this.config.get('ops.cGroupOverrides.cpuAcctPath'), - }); - - if (isObject(cgroup)) { - return { - os: { - cgroup, - }, - }; - } - } catch (e) { - this.server.log(['error', 'metrics', 'cgroup'], e); - } - } - - async captureCGroupsIfAvailable() { - if (this.checkCGroupStats === true) { - const cgroup = await this.captureCGroups(); - - if (isObject(cgroup)) { - return cgroup; - } - - this.checkCGroupStats = false; - } - } -} diff --git a/src/legacy/server/status/lib/metrics.test.js b/src/legacy/server/status/lib/metrics.test.js deleted file mode 100644 index cc9c2607a2b59..0000000000000 --- a/src/legacy/server/status/lib/metrics.test.js +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock('fs', () => ({ - readFile: jest.fn(), -})); - -jest.mock('os', () => ({ - freemem: jest.fn(), - totalmem: jest.fn(), - uptime: jest.fn(), - platform: jest.fn(), - release: jest.fn(), -})); - -jest.mock('process', () => ({ - uptime: jest.fn(), -})); - -import fs from 'fs'; -import os from 'os'; -import _ from 'lodash'; -import sinon from 'sinon'; -import { cGroups as cGroupsFsStub, setMockFiles, readFileMock } from './__mocks__/_fs_stubs'; -import { Metrics } from './metrics'; - -describe('Metrics', function () { - fs.readFile.mockImplementation(readFileMock); - - const sampleConfig = { - ops: { - interval: 5000, - }, - server: { - port: 5603, - }, - }; - const config = { get: (path) => _.get(sampleConfig, path) }; - - let metrics; - - beforeEach(() => { - const server = { log: sinon.mock() }; - - metrics = new Metrics(config, server); - }); - - afterEach(() => { - setMockFiles(); - }); - - describe('capture', () => { - it('merges all metrics', async () => { - setMockFiles(); - sinon - .stub(metrics, 'captureEvent') - .returns({ a: [{ b: 2 }, { d: 4 }], process: { uptime_ms: 1980 } }); - sinon.stub(metrics, 'captureCGroupsIfAvailable').returns({ a: [{ c: 3 }, { e: 5 }] }); - sinon.stub(Date.prototype, 'toISOString').returns('2017-04-14T18:35:41.534Z'); - - const capturedMetrics = await metrics.capture(); - expect(capturedMetrics).toMatchObject({ - last_updated: '2017-04-14T18:35:41.534Z', - collection_interval_in_millis: 5000, - a: [ - { b: 2, c: 3 }, - { d: 4, e: 5 }, - ], - process: { uptime_ms: 1980 }, - }); - }); - }); - - describe('captureEvent', () => { - it('parses the hapi event', async () => { - sinon.stub(os, 'uptime').returns(12000); - sinon.stub(process, 'uptime').returns(5000); - - os.freemem.mockImplementation(() => 12); - os.totalmem.mockImplementation(() => 24); - - const pidMock = jest.fn(); - pidMock.mockReturnValue(8675309); - Object.defineProperty(process, 'pid', { get: pidMock }); // - - const hapiEvent = { - requests: { 5603: { total: 22, disconnects: 0, statusCodes: { 200: 22 } } }, - responseTimes: { 5603: { avg: 1.8636363636363635, max: 4 } }, - osload: [2.20751953125, 2.02294921875, 1.89794921875], - osmem: { total: 17179869184, free: 102318080 }, - osup: 1008991, - psup: 7.168, - psmem: { rss: 193716224, heapTotal: 168194048, heapUsed: 130553400, external: 1779619 }, - concurrent_connections: 0, - psdelay: 1.6091690063476562, - host: 'blahblah.local', - }; - - expect(await metrics.captureEvent(hapiEvent)).toMatchObject({ - concurrent_connections: 0, - os: { - load: { - '15m': 1.89794921875, - '1m': 2.20751953125, - '5m': 2.02294921875, - }, - memory: { - free_in_bytes: 12, - total_in_bytes: 24, - }, - uptime_in_millis: 12000000, - }, - process: { - memory: { - heap: { - total_in_bytes: 168194048, - used_in_bytes: 130553400, - }, - resident_set_size_in_bytes: 193716224, - }, - pid: 8675309, - }, - requests: { - disconnects: 0, - total: 22, - }, - response_times: { - avg_in_millis: 1.8636363636363635, - max_in_millis: 4, - }, - }); - }); - - it('parses event with missing fields / NaN for responseTimes.avg', async () => { - const hapiEvent = { - requests: { - 5603: { total: 22, disconnects: 0, statusCodes: { 200: 22 } }, - }, - responseTimes: { 5603: { avg: NaN, max: 4 } }, - host: 'blahblah.local', - }; - - expect(await metrics.captureEvent(hapiEvent)).toMatchObject({ - process: { memory: { heap: {} }, pid: 8675309, uptime_in_millis: 5000000 }, - os: { - load: {}, - memory: { free_in_bytes: 12, total_in_bytes: 24 }, - }, - response_times: { max_in_millis: 4 }, - requests: { total: 22, disconnects: 0 }, - }); - }); - }); - - describe('captureCGroups', () => { - afterEach(() => { - setMockFiles(); - }); - - it('returns undefined if cgroups do not exist', async () => { - setMockFiles(); - - const stats = await metrics.captureCGroups(); - - expect(stats).toBe(undefined); - }); - - it('returns cgroups', async () => { - const fsStub = cGroupsFsStub(); - setMockFiles(fsStub.files); - - const capturedMetrics = await metrics.captureCGroups(); - - expect(capturedMetrics).toMatchObject({ - os: { - cgroup: { - cpuacct: { - control_group: `/${fsStub.hierarchy}`, - usage_nanos: 357753491408, - }, - cpu: { - control_group: `/${fsStub.hierarchy}`, - cfs_period_micros: 100000, - cfs_quota_micros: 5000, - stat: { - number_of_elapsed_periods: 0, - number_of_times_throttled: 10, - time_throttled_nanos: 20, - }, - }, - }, - }, - }); - }); - }); - - describe('captureCGroupsIfAvailable', () => { - afterEach(() => { - setMockFiles(); - }); - - it('marks cgroups as unavailable and prevents subsequent calls', async () => { - setMockFiles(); - sinon.spy(metrics, 'captureCGroups'); - - expect(metrics.checkCGroupStats).toBe(true); - - await metrics.captureCGroupsIfAvailable(); - expect(metrics.checkCGroupStats).toBe(false); - - await metrics.captureCGroupsIfAvailable(); - sinon.assert.calledOnce(metrics.captureCGroups); - }); - - it('allows subsequent calls if cgroups are available', async () => { - const fsStub = cGroupsFsStub(); - setMockFiles(fsStub.files); - sinon.spy(metrics, 'captureCGroups'); - - expect(metrics.checkCGroupStats).toBe(true); - - await metrics.captureCGroupsIfAvailable(); - expect(metrics.checkCGroupStats).toBe(true); - - await metrics.captureCGroupsIfAvailable(); - sinon.assert.calledTwice(metrics.captureCGroups); - }); - }); -}); diff --git a/src/legacy/server/status/routes/api/register_stats.js b/src/legacy/server/status/routes/api/register_stats.js deleted file mode 100644 index 2cd780d21f681..0000000000000 --- a/src/legacy/server/status/routes/api/register_stats.js +++ /dev/null @@ -1,164 +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 Joi from 'joi'; -import boom from 'boom'; -import { defaultsDeep } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { wrapAuthConfig } from '../../wrap_auth_config'; -import { getKibanaInfoForStats } from '../../lib'; - -const STATS_NOT_READY_MESSAGE = i18n.translate('server.stats.notReadyMessage', { - defaultMessage: 'Stats are not ready yet. Please try again later.', -}); - -/* - * API for Kibana meta info and accumulated operations stats - * Including ?extended in the query string fetches Elasticsearch cluster_uuid and usageCollection data - * - Requests to set isExtended = true - * GET /api/stats?extended=true - * GET /api/stats?extended - * - No value or 'false' is isExtended = false - * - Any other value causes a statusCode 400 response (Bad Request) - * Including ?exclude_usage in the query string excludes the usage stats from the response. Same value semantics as ?extended - */ -export function registerStatsApi(usageCollection, server, config, kbnServer) { - const wrapAuth = wrapAuthConfig(config.get('status.allowAnonymous')); - - const getClusterUuid = async (callCluster) => { - const { cluster_uuid: uuid } = await callCluster('info', { filterPath: 'cluster_uuid' }); - return uuid; - }; - - const getUsage = async (callCluster) => { - const usage = await usageCollection.bulkFetchUsage(callCluster); - return usageCollection.toObject(usage); - }; - - let lastMetrics = null; - /* kibana_stats gets singled out from the collector set as it is used - * for health-checking Kibana and fetch does not rely on fetching data - * from ES */ - server.newPlatform.start.core.metrics.getOpsMetrics$().subscribe((metrics) => { - lastMetrics = { - ...metrics, - timestamp: new Date().toISOString(), - }; - }); - - server.route( - wrapAuth({ - method: 'GET', - path: '/api/stats', - config: { - validate: { - query: Joi.object({ - extended: Joi.string().valid('', 'true', 'false'), - legacy: Joi.string().valid('', 'true', 'false'), - exclude_usage: Joi.string().valid('', 'true', 'false'), - }), - }, - tags: ['api'], - }, - async handler(req) { - const isExtended = req.query.extended !== undefined && req.query.extended !== 'false'; - const isLegacy = req.query.legacy !== undefined && req.query.legacy !== 'false'; - const shouldGetUsage = - req.query.exclude_usage === undefined || req.query.exclude_usage === 'false'; - - let extended; - if (isExtended) { - const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('admin'); - const callCluster = (...args) => callWithRequest(req, ...args); - const collectorsReady = await usageCollection.areAllCollectorsReady(); - - if (shouldGetUsage && !collectorsReady) { - return boom.serverUnavailable(STATS_NOT_READY_MESSAGE); - } - - const usagePromise = shouldGetUsage ? getUsage(callCluster) : Promise.resolve({}); - try { - const [usage, clusterUuid] = await Promise.all([ - usagePromise, - getClusterUuid(callCluster), - ]); - - let modifiedUsage = usage; - if (isLegacy) { - // In an effort to make telemetry more easily augmented, we need to ensure - // we can passthrough the data without every part of the process needing - // to know about the change; however, to support legacy use cases where this - // wasn't true, we need to be backwards compatible with how the legacy data - // looked and support those use cases here. - modifiedUsage = Object.keys(usage).reduce((accum, usageKey) => { - if (usageKey === 'kibana') { - accum = { - ...accum, - ...usage[usageKey], - }; - } else if (usageKey === 'reporting') { - accum = { - ...accum, - xpack: { - ...accum.xpack, - reporting: usage[usageKey], - }, - }; - } else { - // I don't think we need to it this for the above conditions, but do it for most as it will - // match the behavior done in monitoring/bulk_uploader - defaultsDeep(accum, { [usageKey]: usage[usageKey] }); - } - - return accum; - }, {}); - - extended = { - usage: modifiedUsage, - clusterUuid, - }; - } else { - extended = usageCollection.toApiFieldNames({ - usage: modifiedUsage, - clusterUuid, - }); - } - } catch (e) { - throw boom.boomify(e); - } - } - - if (!lastMetrics) { - return boom.serverUnavailable(STATS_NOT_READY_MESSAGE); - } - const kibanaStats = usageCollection.toApiFieldNames({ - ...lastMetrics, - kibana: getKibanaInfoForStats(server, kbnServer), - last_updated: new Date().toISOString(), - collection_interval_in_millis: config.get('ops.interval'), - }); - - return { - ...kibanaStats, - ...extended, - }; - }, - }) - ); -} diff --git a/src/legacy/server/status/routes/api/register_status.js b/src/legacy/server/status/routes/api/register_status.js deleted file mode 100644 index 259a00667810f..0000000000000 --- a/src/legacy/server/status/routes/api/register_status.js +++ /dev/null @@ -1,50 +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 { wrapAuthConfig } from '../../wrap_auth_config'; - -const matchSnapshot = /-SNAPSHOT$/; - -export function registerStatusApi(kbnServer, server, config) { - const wrapAuth = wrapAuthConfig(config.get('status.allowAnonymous')); - - server.route( - wrapAuth({ - method: 'GET', - path: '/api/status', - config: { - tags: ['api'], - }, - async handler() { - return { - name: config.get('server.name'), - uuid: config.get('server.uuid'), - version: { - number: config.get('pkg.version').replace(matchSnapshot, ''), - build_hash: config.get('pkg.buildSha'), - build_number: config.get('pkg.buildNum'), - build_snapshot: matchSnapshot.test(config.get('pkg.version')), - }, - status: kbnServer.status.toJSON(), - metrics: kbnServer.metrics, - }; - }, - }) - ); -} diff --git a/src/legacy/server/status/server_status.js b/src/legacy/server/status/server_status.js deleted file mode 100644 index 81d07de55faaf..0000000000000 --- a/src/legacy/server/status/server_status.js +++ /dev/null @@ -1,116 +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 * as states from './states'; -import Status from './status'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { pkg } from '../../../core/server/utils'; - -export default class ServerStatus { - constructor(server) { - this.server = server; - this._created = {}; - } - - create(id) { - const status = new Status(id, this.server); - this._created[status.id] = status; - return status; - } - - createForPlugin(plugin) { - if (plugin.version === 'kibana') plugin.version = pkg.version; - const status = this.create(`plugin:${plugin.id}@${plugin.version}`); - status.plugin = plugin; - return status; - } - - each(fn) { - const self = this; - _.forOwn(self._created, function (status, i, list) { - if (status.state !== 'disabled') { - fn.call(self, status, i, list); - } - }); - } - - get(id) { - return this._created[id]; - } - - getForPluginId(pluginId) { - return _.find(this._created, (s) => s.plugin && s.plugin.id === pluginId); - } - - getState(id) { - const status = this.get(id); - if (!status) return undefined; - return status.state || 'uninitialized'; - } - - getStateForPluginId(pluginId) { - const status = this.getForPluginId(pluginId); - if (!status) return undefined; - return status.state || 'uninitialized'; - } - - overall() { - const state = Object - // take all created status objects - .values(this._created) - // get the state descriptor for each status - .map((status) => states.get(status.state)) - // reduce to the state with the highest severity, defaulting to green - .reduce((a, b) => (a.severity > b.severity ? a : b), states.get('green')); - - const statuses = _.filter(this._created, { state: state.id }); - const since = _.get(_.sortBy(statuses, 'since'), [0, 'since']); - - return { - state: state.id, - title: state.title, - nickname: _.sample(state.nicknames), - icon: state.icon, - uiColor: states.get(state.id).uiColor, - since: since, - }; - } - - isGreen() { - return this.overall().state === 'green'; - } - - notGreen() { - return !this.isGreen(); - } - - toString() { - const overall = this.overall(); - return `${overall.title} – ${overall.nickname}`; - } - - toJSON() { - return { - overall: this.overall(), - statuses: _.values(this._created), - }; - } -} diff --git a/src/legacy/server/status/server_status.test.js b/src/legacy/server/status/server_status.test.js deleted file mode 100644 index bf94d693b1310..0000000000000 --- a/src/legacy/server/status/server_status.test.js +++ /dev/null @@ -1,145 +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 { find } from 'lodash'; -import sinon from 'sinon'; - -import * as states from './states'; -import Status from './status'; -import ServerStatus from './server_status'; - -describe('ServerStatus class', function () { - const plugin = { id: 'name', version: '1.2.3' }; - - let server; - let serverStatus; - - beforeEach(function () { - server = { expose: sinon.stub(), logWithMetadata: sinon.stub() }; - serverStatus = new ServerStatus(server); - }); - - describe('#create(id)', () => { - it('should create a new plugin with an id', () => { - const status = serverStatus.create('someid'); - expect(status).toBeInstanceOf(Status); - }); - }); - - describe('#createForPlugin(plugin)', function () { - it('should create a new status by plugin', function () { - const status = serverStatus.createForPlugin(plugin); - expect(status).toBeInstanceOf(Status); - }); - }); - - describe('#get(id)', () => { - it('exposes statuses by their id', () => { - const status = serverStatus.create('statusid'); - expect(serverStatus.get('statusid')).toBe(status); - }); - - it('does not get the status for a plugin', () => { - serverStatus.createForPlugin(plugin); - expect(serverStatus.get(plugin)).toBe(undefined); - }); - }); - - describe('#getForPluginId(plugin)', function () { - it('exposes plugin status for the plugin', function () { - const status = serverStatus.createForPlugin(plugin); - expect(serverStatus.getForPluginId(plugin.id)).toBe(status); - }); - - it('does not get plain statuses by their id', function () { - serverStatus.create('someid'); - expect(serverStatus.getForPluginId('someid')).toBe(undefined); - }); - }); - - describe('#getState(id)', function () { - it('should expose the state of a status by id', function () { - const status = serverStatus.create('someid'); - status.green(); - expect(serverStatus.getState('someid')).toBe('green'); - }); - }); - - describe('#getStateForPluginId(plugin)', function () { - it('should expose the state of a plugin by id', function () { - const status = serverStatus.createForPlugin(plugin); - status.green(); - expect(serverStatus.getStateForPluginId(plugin.id)).toBe('green'); - }); - }); - - describe('#overall()', function () { - it('considers each status to produce a summary', function () { - const status = serverStatus.createForPlugin(plugin); - - expect(serverStatus.overall().state).toBe('uninitialized'); - - const match = function (overall, state) { - expect(overall).toHaveProperty('state', state.id); - expect(overall).toHaveProperty('title', state.title); - expect(overall).toHaveProperty('icon', state.icon); - expect(overall).toHaveProperty('uiColor', state.uiColor); - expect(state.nicknames).toContain(overall.nickname); - }; - - status.green(); - match(serverStatus.overall(), states.get('green')); - - status.yellow(); - match(serverStatus.overall(), states.get('yellow')); - - status.red(); - match(serverStatus.overall(), states.get('red')); - }); - }); - - describe('#toJSON()', function () { - it('serializes to overall status and individuals', function () { - const pluginOne = { id: 'one', version: '1.0.0' }; - const pluginTwo = { id: 'two', version: '2.0.0' }; - const pluginThree = { id: 'three', version: 'kibana' }; - - const service = serverStatus.create('some service'); - const p1 = serverStatus.createForPlugin(pluginOne); - const p2 = serverStatus.createForPlugin(pluginTwo); - const p3 = serverStatus.createForPlugin(pluginThree); - - service.green(); - p1.yellow(); - p2.red(); - - const json = JSON.parse(JSON.stringify(serverStatus)); - expect(json).toHaveProperty('overall'); - expect(json.overall.state).toEqual(serverStatus.overall().state); - expect(json.statuses).toHaveLength(4); - - const out = (status) => find(json.statuses, { id: status.id }); - expect(out(service)).toHaveProperty('state', 'green'); - expect(out(p1)).toHaveProperty('state', 'yellow'); - expect(out(p2)).toHaveProperty('state', 'red'); - expect(out(p3)).toHaveProperty('id'); - expect(out(p3).id).not.toContain('undefined'); - }); - }); -}); diff --git a/src/legacy/server/status/states.js b/src/legacy/server/status/states.js deleted file mode 100644 index 4a34684571c3c..0000000000000 --- a/src/legacy/server/status/states.js +++ /dev/null @@ -1,85 +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 { i18n } from '@kbn/i18n'; - -export const getAll = () => [ - { - id: 'red', - title: i18n.translate('server.status.redTitle', { - defaultMessage: 'Red', - }), - icon: 'danger', - uiColor: 'danger', - severity: 1000, - nicknames: ['Danger Will Robinson! Danger!'], - }, - { - id: 'uninitialized', - title: i18n.translate('server.status.uninitializedTitle', { - defaultMessage: 'Uninitialized', - }), - icon: 'spinner', - uiColor: 'default', - severity: 900, - nicknames: ['Initializing'], - }, - { - id: 'yellow', - title: i18n.translate('server.status.yellowTitle', { - defaultMessage: 'Yellow', - }), - icon: 'warning', - uiColor: 'warning', - severity: 800, - nicknames: ['S.N.A.F.U', "I'll be back", 'brb'], - }, - { - id: 'green', - title: i18n.translate('server.status.greenTitle', { - defaultMessage: 'Green', - }), - icon: 'success', - uiColor: 'secondary', - severity: 0, - nicknames: ['Looking good'], - }, - { - id: 'disabled', - title: i18n.translate('server.status.disabledTitle', { - defaultMessage: 'Disabled', - }), - severity: -1, - icon: 'toggle-off', - uiColor: 'default', - nicknames: ['Am I even a thing?'], - }, -]; - -export const getAllById = () => _.keyBy(exports.getAll(), 'id'); - -export const defaults = { - icon: 'question', - severity: Infinity, -}; - -export function get(id) { - return exports.getAllById()[id] || _.defaults({ id: id }, exports.defaults); -} diff --git a/src/legacy/server/status/status.js b/src/legacy/server/status/status.js deleted file mode 100644 index 10e94da3ac352..0000000000000 --- a/src/legacy/server/status/status.js +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import * as states from './states'; -import { EventEmitter } from 'events'; - -export default class Status extends EventEmitter { - constructor(id, server) { - super(); - - if (!id || typeof id !== 'string') { - throw new TypeError('Status constructor requires an `id` string'); - } - - this.id = id; - this.since = new Date(); - this.state = 'uninitialized'; - this.message = 'uninitialized'; - - this.on('change', function (previous, previousMsg) { - this.since = new Date(); - - const tags = ['status', this.id, this.state === 'red' ? 'error' : 'info']; - - server.logWithMetadata( - tags, - `Status changed from ${previous} to ${this.state}${ - this.message ? ' - ' + this.message : '' - }`, - { - state: this.state, - message: this.message, - prevState: previous, - prevMsg: previousMsg, - } - ); - }); - } - - toJSON() { - return { - id: this.id, - state: this.state, - icon: states.get(this.state).icon, - message: this.message, - uiColor: states.get(this.state).uiColor, - since: this.since, - }; - } - - on(eventName, handler) { - super.on(eventName, handler); - - if (eventName === this.state) { - setImmediate(() => handler(this.state, this.message)); - } - } - - once(eventName, handler) { - if (eventName === this.state) { - setImmediate(() => handler(this.state, this.message)); - } else { - super.once(eventName, handler); - } - } -} - -states.getAll().forEach(function (state) { - Status.prototype[state.id] = function (message) { - if (this.state === 'disabled') return; - - const previous = this.state; - const previousMsg = this.message; - - this.error = null; - this.message = message || state.title; - this.state = state.id; - - if (message instanceof Error) { - this.error = message; - this.message = message.message; - } - - if (previous === this.state && previousMsg === this.message) { - // noop - return; - } - - this.emit(state.id, previous, previousMsg, this.state, this.message); - this.emit('change', previous, previousMsg, this.state, this.message); - }; -}); diff --git a/src/legacy/server/status/status.test.js b/src/legacy/server/status/status.test.js deleted file mode 100644 index def7b5a2182e1..0000000000000 --- a/src/legacy/server/status/status.test.js +++ /dev/null @@ -1,147 +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 sinon from 'sinon'; -import ServerStatus from './server_status'; - -describe('Status class', function () { - const plugin = { id: 'test', version: '1.2.3' }; - - let server; - let serverStatus; - - beforeEach(function () { - server = { expose: sinon.stub(), logWithMetadata: sinon.stub() }; - serverStatus = new ServerStatus(server); - }); - - it('should have an "uninitialized" state initially', () => { - expect(serverStatus.createForPlugin(plugin)).toHaveProperty('state', 'uninitialized'); - }); - - it('emits change when the status is set', function (done) { - const status = serverStatus.createForPlugin(plugin); - - status.once('change', function (prevState, prevMsg, newState, newMsg) { - expect(newState).toBe('green'); - expect(newMsg).toBe('GREEN'); - expect(prevState).toBe('uninitialized'); - - status.once('change', function (prevState, prevMsg, newState, newMsg) { - expect(newState).toBe('red'); - expect(newMsg).toBe('RED'); - expect(prevState).toBe('green'); - expect(prevMsg).toBe('GREEN'); - - done(); - }); - - status.red('RED'); - }); - - status.green('GREEN'); - }); - - it('should only trigger the change listener when something changes', function () { - const status = serverStatus.createForPlugin(plugin); - const stub = sinon.stub(); - status.on('change', stub); - status.green('Ready'); - status.green('Ready'); - status.red('Not Ready'); - sinon.assert.calledTwice(stub); - }); - - it('should create a JSON representation of the status', function () { - const status = serverStatus.createForPlugin(plugin); - status.green('Ready'); - - const json = status.toJSON(); - expect(json.id).toEqual(status.id); - expect(json.state).toEqual('green'); - expect(json.message).toEqual('Ready'); - }); - - it('should call on handler if status is already matched', function (done) { - const status = serverStatus.createForPlugin(plugin); - const msg = 'Test Ready'; - status.green(msg); - - status.on('green', function (prev, prevMsg) { - expect(arguments.length).toBe(2); - expect(prev).toBe('green'); - expect(prevMsg).toBe(msg); - expect(status.message).toBe(msg); - done(); - }); - }); - - it('should call once handler if status is already matched', function (done) { - const status = serverStatus.createForPlugin(plugin); - const msg = 'Test Ready'; - status.green(msg); - - status.once('green', function (prev, prevMsg) { - expect(arguments.length).toBe(2); - expect(prev).toBe('green'); - expect(prevMsg).toBe(msg); - expect(status.message).toBe(msg); - done(); - }); - }); - - function testState(color) { - it(`should change the state to ${color} when #${color}() is called`, function () { - const status = serverStatus.createForPlugin(plugin); - const message = 'testing ' + color; - status[color](message); - expect(status).toHaveProperty('state', color); - expect(status).toHaveProperty('message', message); - }); - - it(`should trigger the "change" listener when #${color}() is called`, function (done) { - const status = serverStatus.createForPlugin(plugin); - const message = 'testing ' + color; - status.on('change', function (prev, prevMsg) { - expect(status.state).toBe(color); - expect(status.message).toBe(message); - - expect(prev).toBe('uninitialized'); - expect(prevMsg).toBe('uninitialized'); - done(); - }); - status[color](message); - }); - - it(`should trigger the "${color}" listener when #${color}() is called`, function (done) { - const status = serverStatus.createForPlugin(plugin); - const message = 'testing ' + color; - status.on(color, function () { - expect(status.state).toBe(color); - expect(status.message).toBe(message); - done(); - }); - status[color](message); - }); - } - - testState('green'); - testState('yellow'); - testState('red'); -}); diff --git a/src/legacy/server/status/wrap_auth_config.test.js b/src/legacy/server/status/wrap_auth_config.test.js deleted file mode 100644 index fa0230a96a587..0000000000000 --- a/src/legacy/server/status/wrap_auth_config.test.js +++ /dev/null @@ -1,60 +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 { wrapAuthConfig } from './wrap_auth_config'; - -describe('Status wrapAuthConfig', () => { - let options; - - beforeEach(() => { - options = { - method: 'GET', - path: '/status', - handler: function (request, h) { - return h.response(); - }, - }; - }); - - it('should return a function', () => { - expect(typeof wrapAuthConfig()).toBe('function'); - expect(typeof wrapAuthConfig(true)).toBe('function'); - expect(typeof wrapAuthConfig(false)).toBe('function'); - }); - - it('should not add auth config by default', () => { - const wrapAuth = wrapAuthConfig(); - const wrapped = wrapAuth(options); - expect(wrapped).not.toHaveProperty('config'); - }); - - it('should not add auth config if allowAnonymous is false', () => { - const wrapAuth = wrapAuthConfig(false); - const wrapped = wrapAuth(options); - expect(wrapped).not.toHaveProperty('config'); - }); - - it('should add auth config if allowAnonymous is true', () => { - const wrapAuth = wrapAuthConfig(true); - const wrapped = wrapAuth(options); - expect(wrapped).toHaveProperty('config'); - expect(wrapped.config).toHaveProperty('auth'); - expect(wrapped.config.auth).toBe(false); - }); -}); 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 bbc3f3632bf64..8c9e3847844d9 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 d2d2e38947f76..0000000000000 --- 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 8d768d200fdd2..0000000000000 --- 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 2ef4ef1d20ce9..0000000000000 --- 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 5533f684870d9..0378d816fd2c3 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 92d6f2ed91dde..dd5eb1ee5ccaa 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 a36363d22d87d..84df05154fb63 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 49584f62215ea..5a45229a58a7d 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 6d3019e75d483..894bd1812f084 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/common/index_patterns/index_patterns/index_patterns.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts index c79c7900148ea..d3b3a73a4b50f 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts @@ -113,15 +113,13 @@ describe('IndexPatterns', () => { test('caches saved objects', async () => { await indexPatterns.getIds(); await indexPatterns.getTitles(); - await indexPatterns.getFields(['id', 'title']); expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); }); test('can refresh the saved objects caches', async () => { await indexPatterns.getIds(); await indexPatterns.getTitles(true); - await indexPatterns.getFields(['id', 'title'], true); - expect(savedObjectsClient.find).toHaveBeenCalledTimes(3); + expect(savedObjectsClient.find).toHaveBeenCalledTimes(2); }); test('deletes the index pattern', async () => { diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 88a7e9f6cef4c..47484f8ec75bb 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -41,8 +41,6 @@ const indexPatternCache = createIndexPatternCache(); const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; const savedObjectType = 'index-pattern'; -type IndexPatternCachedFieldType = 'id' | 'title'; - export interface IndexPatternSavedObjectAttrs { title: string; } @@ -116,22 +114,6 @@ export class IndexPatternsService { return this.savedObjectsCache.map((obj) => obj?.attributes?.title); }; - getFields = async (fields: IndexPatternCachedFieldType[], refresh: boolean = false) => { - if (!this.savedObjectsCache || refresh) { - await this.refreshSavedObjectsCache(); - } - if (!this.savedObjectsCache) { - return []; - } - return this.savedObjectsCache.map((obj: Record) => { - const result: Partial> = {}; - fields.forEach( - (f: IndexPatternCachedFieldType) => (result[f] = obj[f] || obj?.attributes?.[f]) - ); - return result; - }); - }; - getFieldsForTimePattern = (options: GetFieldsOptions = {}) => { return this.apiClient.getFieldsForTimePattern(options); }; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index fa5d3cd85f430..7ce53a219fb44 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -17,6 +17,7 @@ import { CoreSetup as CoreSetup_2 } from 'kibana/public'; import { CoreStart } from 'kibana/public'; import { CoreStart as CoreStart_2 } from 'src/core/public'; import { Ensure } from '@kbn/utility-types'; +import { EnvironmentMode } from '@kbn/config'; import { ErrorToastOptions } from 'src/core/public/notifications'; import { EuiBreadcrumb } from '@elastic/eui'; import { EuiButtonEmptyProps } from '@elastic/eui'; @@ -28,7 +29,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'; @@ -39,16 +39,20 @@ import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType } from 'src/core/server/kibana_config'; import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; +import { Logger } from '@kbn/logging'; +import { LogMeta } from '@kbn/logging'; import { MaybePromise } from '@kbn/utility-types'; import { METRIC_TYPE } from '@kbn/analytics'; import { Moment } from 'moment'; import moment from 'moment'; import { NameList } from 'elasticsearch'; import { Observable } from 'rxjs'; +import { PackageInfo } from '@kbn/config'; 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 +1689,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 +2021,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 224a597766909..cdf10d8f1a1b0 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 943a02d22088d..0a7913b0a734f 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 d66796b9427a1..b87affdd59c54 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/vis_type_timeseries/server/lib/search_strategies/search_requests/single_search_request.js b/src/plugins/data/public/search/legacy/call_msearch.ts similarity index 60% rename from src/plugins/vis_type_timeseries/server/lib/search_strategies/search_requests/single_search_request.js rename to src/plugins/data/public/search/legacy/call_msearch.ts index 7d8b60a7e4595..fd4f8a07919f8 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/search_requests/single_search_request.js +++ b/src/plugins/data/public/search/legacy/call_msearch.ts @@ -16,22 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -import { AbstractSearchRequest } from './abstract_request'; -import { UI_SETTINGS } from '../../../../../data/server'; -const SEARCH_METHOD = 'search'; +import { HttpStart } from 'src/core/public'; +import { LegacyFetchHandlers } from './types'; -export class SingleSearchRequest extends AbstractSearchRequest { - async search([{ body, index }]) { - const includeFrozen = await this.req - .getUiSettingsService() - .get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN); - const resp = await this.callWithRequest(this.req, SEARCH_METHOD, { - ignore_throttled: !includeFrozen, - body, - index, +/** + * 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, }); - - return [resp]; - } + }; } 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 e74ab49131430..ad59e5c6c9625 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 cbcd0da20207f..bed86cb75cca6 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 d38a41cf5ffbc..7243ab158009a 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 37c3827bb7bba..1c0573aa895d7 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 ed17db464feff..740bc22a7485c 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 6b73761c5a437..c41e1f78ee74e 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 bc1c7c06c8806..6b6cfb0c9b1ca 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 adf53bee33fe1..f582861e37c15 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 282a33e6d01f7..d9a9fb2f4fef3 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 a39898e6a9f52..4afee223454e4 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 3606bfbaeb1f9..1f124291669ec 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/data/server/server.api.md b/src/plugins/data/server/server.api.md index cd0369a5c4551..14f176176f647 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -32,6 +32,7 @@ import { ClusterPutSettingsParams } from 'elasticsearch'; import { ClusterRerouteParams } from 'elasticsearch'; import { ClusterStateParams } from 'elasticsearch'; import { ClusterStatsParams } from 'elasticsearch'; +import { ConfigDeprecationProvider } from '@kbn/config'; import { CoreSetup } from 'src/core/server'; import { CoreSetup as CoreSetup_2 } from 'kibana/server'; import { CoreStart } from 'src/core/server'; @@ -43,6 +44,7 @@ import { DeleteScriptParams } from 'elasticsearch'; import { DeleteTemplateParams } from 'elasticsearch'; import { Duration } from 'moment'; import { Ensure } from '@kbn/utility-types'; +import { EnvironmentMode } from '@kbn/config'; import { ErrorToastOptions } from 'src/core/public/notifications'; import { ExistsParams } from 'elasticsearch'; import { ExplainParams } from 'elasticsearch'; @@ -104,7 +106,10 @@ import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType as KibanaConfigType_2 } from 'src/core/server/kibana_config'; import { KibanaRequest } from 'kibana/server'; import { LegacyAPICaller as LegacyAPICaller_2 } from 'kibana/server'; +import { Logger } from '@kbn/logging'; import { Logger as Logger_2 } from 'kibana/server'; +import { LoggerFactory } from '@kbn/logging'; +import { LogMeta } from '@kbn/logging'; import { MGetParams } from 'elasticsearch'; import { MGetResponse } from 'elasticsearch'; import { Moment } from 'moment'; @@ -117,6 +122,8 @@ import { NodesHotThreadsParams } from 'elasticsearch'; import { NodesInfoParams } from 'elasticsearch'; import { NodesStatsParams } from 'elasticsearch'; import { Observable } from 'rxjs'; +import { PackageInfo } from '@kbn/config'; +import { PathConfigType } from '@kbn/utils'; import { PingParams } from 'elasticsearch'; import { Plugin as Plugin_2 } from 'src/core/server'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/server'; diff --git a/src/plugins/dev_tools/public/index.scss b/src/plugins/dev_tools/public/index.scss index c9d8dc7470656..4bec602ea42db 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 fcc6a57361a94..8c4743c93fab3 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/_discover.scss b/src/plugins/discover/public/application/_discover.scss index 69df2a75b8d75..bc704439d161b 100644 --- a/src/plugins/discover/public/application/_discover.scss +++ b/src/plugins/discover/public/application/_discover.scss @@ -5,6 +5,11 @@ overflow: hidden; } +.dscAppContainer { + > * { + position: relative; + } +} discover-app { flex-grow: 1; } @@ -17,9 +22,12 @@ discover-app { // SASSTODO: replace the z-index value with a variable .dscWrapper { + padding-left: $euiSizeXL; padding-right: $euiSizeS; - padding-left: 21px; z-index: 1; + @include euiBreakpoint('xs', 's', 'm') { + padding-left: $euiSizeS; + } } @include euiPanel('.dscWrapper__content'); @@ -104,14 +112,51 @@ discover-app { top: $euiSizeXS; } -[fixed-scroll] { +.dscTableFixedScroll { overflow-x: auto; padding-bottom: 0; - + .fixed-scroll-scroller { + + .dscTableFixedScroll__scroller { position: fixed; bottom: 0; overflow-x: auto; overflow-y: hidden; } } + +.dscCollapsibleSidebar { + position: relative; + z-index: $euiZLevel1; + + .dscCollapsibleSidebar__collapseButton { + position: absolute; + top: 0; + right: -$euiSizeXL + 4; + cursor: pointer; + z-index: -1; + min-height: $euiSizeM; + min-width: $euiSizeM; + padding: $euiSizeXS * .5; + } + + &.closed { + width: 0 !important; + border-right-width: 0; + border-left-width: 0; + .dscCollapsibleSidebar__collapseButton { + right: -$euiSizeL + 4; + } + } +} + +@include euiBreakpoint('xs', 's', 'm') { + .dscCollapsibleSidebar { + &.closed { + display: none; + } + + .dscCollapsibleSidebar__collapseButton { + display: none; + } + } +} diff --git a/src/plugins/discover/public/application/angular/directives/__snapshots__/no_results.test.js.snap b/src/plugins/discover/public/application/angular/directives/__snapshots__/no_results.test.js.snap index e7aea41e2d08e..e69e10e29e801 100644 --- a/src/plugins/discover/public/application/angular/directives/__snapshots__/no_results.test.js.snap +++ b/src/plugins/discover/public/application/angular/directives/__snapshots__/no_results.test.js.snap @@ -167,132 +167,6 @@ Array [ ] `; -exports[`DiscoverNoResults props shardFailures doesn't render failures list when there are no failures 1`] = ` -Array [ -
, -
-
-
-
- -
-
-
, -] -`; - -exports[`DiscoverNoResults props shardFailures renders failures list when there are failures 1`] = ` -Array [ -
, -
-
-
-
- -
-
-
-

- Address shard failures -

-

- The following shard failures occurred: -

-
-
- - Index ‘A’ - - , shard ‘1’ -
-
-
-
-              
-                {"reason":"Awful error"}
-              
-            
-
-
-
-
-
- - Index ‘B’ - - , shard ‘2’ -
-
-
-
-              
-                {"reason":"Bad error"}
-              
-            
-
-
-
-
-
, -] -`; - exports[`DiscoverNoResults props timeFieldName renders time range feedback 1`] = ` Array [
* { - visibility: hidden; - } - - .kbnCollapsibleSidebar__collapseButton { - visibility: visible; - - .chevron-cont:before { - content: "\F138"; - } - } - } -} - -@include euiBreakpoint('xs', 's', 'm') { - .collapsible-sidebar { - &.closed { - display: none; - } - - .kbnCollapsibleSidebar__collapseButton { - display: none; - } - } -} diff --git a/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_depth.scss b/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_depth.scss deleted file mode 100644 index 4bc59001f9931..0000000000000 --- a/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_depth.scss +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 1. The local nav contains tooltips which should pop over the filter bar. - * 2. The filter and local nav components should always appear above the dashboard grid items. - * 3. The filter and local nav components should always appear above the discover content. - * 4. The sidebar collapser button should appear above the main Discover content but below the top elements. - * 5. Dragged panels in dashboard should always appear above other panels. - */ -$kbnFilterBarDepth: 4; /* 1 */ -$kbnLocalNavDepth: 5; /* 1 */ -$kbnDashboardGridDepth: 1; /* 2 */ -$kbnDashboardDraggingGridDepth: 2; /* 5 */ -$kbnDiscoverWrapperDepth: 1; /* 3 */ -$kbnDiscoverSidebarDepth: 2; /* 4 */ diff --git a/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_index.scss b/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_index.scss deleted file mode 100644 index 1409920d11aa7..0000000000000 --- a/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'depth'; -@import 'collapsible_sidebar'; diff --git a/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/collapsible_sidebar.ts b/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/collapsible_sidebar.ts deleted file mode 100644 index 16fbb0af9f3fd..0000000000000 --- a/src/plugins/discover/public/application/angular/directives/collapsible_sidebar/collapsible_sidebar.ts +++ /dev/null @@ -1,88 +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 $ from 'jquery'; -import { IScope } from 'angular'; - -interface LazyScope extends IScope { - [key: string]: any; -} - -export function CollapsibleSidebarProvider() { - // simply a list of all of all of angulars .col-md-* classes except 12 - const listOfWidthClasses = _.times(11, function (i) { - return 'col-md-' + i; - }); - - return { - restrict: 'C', - link: ($scope: LazyScope, $elem: any) => { - let isCollapsed = false; - const $collapser = $( - `` - ); - // If the collapsable element has an id, also set aria-controls - if ($elem.attr('id')) { - $collapser.attr('aria-controls', $elem.attr('id')); - } - const $icon = $(''); - $collapser.append($icon); - const $siblings = $elem.siblings(); - - const siblingsClass = listOfWidthClasses.reduce((prev: string, className: string) => { - if (prev) return prev; - return $siblings.hasClass(className) && className; - }, ''); - - // If there is are only two elements we can assume the other one will take 100% of the width. - const hasSingleSibling = $siblings.length === 1 && siblingsClass; - - $collapser.on('click', function () { - if (isCollapsed) { - isCollapsed = false; - $elem.removeClass('closed'); - $icon.addClass('fa-chevron-circle-left'); - $icon.removeClass('fa-chevron-circle-right'); - $collapser.attr('aria-expanded', 'true'); - } else { - isCollapsed = true; - $elem.addClass('closed'); - $icon.removeClass('fa-chevron-circle-left'); - $icon.addClass('fa-chevron-circle-right'); - $collapser.attr('aria-expanded', 'false'); - } - - if (hasSingleSibling) { - $siblings.toggleClass(siblingsClass + ' col-md-12'); - } - - if ($scope.toggleSidebar) $scope.toggleSidebar(); - }); - - $collapser.appendTo($elem); - }, - }; -} diff --git a/src/plugins/discover/public/application/angular/directives/debounce/debounce.js b/src/plugins/discover/public/application/angular/directives/debounce/debounce.js index 586e8ed4fab59..8ce2b042c0efe 100644 --- a/src/plugins/discover/public/application/angular/directives/debounce/debounce.js +++ b/src/plugins/discover/public/application/angular/directives/debounce/debounce.js @@ -21,7 +21,7 @@ import _ from 'lodash'; // Debounce service, angularized version of lodash debounce // borrowed heavily from https://github.com/shahata/angular-debounce -export function DebounceProviderTimeout($timeout) { +export function createDebounceProviderTimeout($timeout) { return function (func, wait, options) { let timeout; let args; @@ -66,7 +66,3 @@ export function DebounceProviderTimeout($timeout) { return debounce; }; } - -export function DebounceProvider(debounce) { - return debounce; -} diff --git a/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts b/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts index ccdee153002e4..0cdc214cf97f5 100644 --- a/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts +++ b/src/plugins/discover/public/application/angular/directives/debounce/debounce.test.ts @@ -24,7 +24,7 @@ import 'angular-sanitize'; import 'angular-route'; // @ts-ignore -import { DebounceProvider } from './index'; +import { createDebounceProviderTimeout } from './debounce'; import { coreMock } from '../../../../../../../core/public/mocks'; import { initializeInnerAngularModule } from '../../../../get_inner_angular'; import { navigationPluginMock } from '../../../../../../navigation/public/mocks'; @@ -33,7 +33,6 @@ import { initAngularBootstrap } from '../../../../../../kibana_legacy/public'; describe('debounce service', function () { let debounce: (fn: () => void, timeout: number, options?: any) => any; - let debounceFromProvider: (fn: () => void, timeout: number, options?: any) => any; let $timeout: ITimeoutService; let spy: SinonSpy; @@ -51,22 +50,17 @@ describe('debounce service', function () { angular.mock.module('app/discover'); - angular.mock.inject( - ($injector: auto.IInjectorService, _$timeout_: ITimeoutService, Private: any) => { - $timeout = _$timeout_; + angular.mock.inject(($injector: auto.IInjectorService, _$timeout_: ITimeoutService) => { + $timeout = _$timeout_; - debounce = $injector.get('debounce'); - debounceFromProvider = Private(DebounceProvider); - } - ); + debounce = createDebounceProviderTimeout($timeout); + }); }); it('should have a cancel method', function () { const bouncer = debounce(() => {}, 100); - const bouncerFromProvider = debounceFromProvider(() => {}, 100); expect(bouncer).toHaveProperty('cancel'); - expect(bouncerFromProvider).toHaveProperty('cancel'); }); describe('delayed execution', function () { @@ -77,7 +71,6 @@ describe('debounce service', function () { it('should delay execution', function () { const bouncer = debounce(spy, 100); - const bouncerFromProvider = debounceFromProvider(spy, 100); bouncer(); sinon.assert.notCalled(spy); @@ -85,16 +78,10 @@ describe('debounce service', function () { sinon.assert.calledOnce(spy); spy.resetHistory(); - - bouncerFromProvider(); - sinon.assert.notCalled(spy); - $timeout.flush(); - sinon.assert.calledOnce(spy); }); it('should fire on leading edge', function () { const bouncer = debounce(spy, 100, { leading: true }); - const bouncerFromProvider = debounceFromProvider(spy, 100, { leading: true }); bouncer(); sinon.assert.calledOnce(spy); @@ -102,19 +89,10 @@ describe('debounce service', function () { sinon.assert.calledTwice(spy); spy.resetHistory(); - - bouncerFromProvider(); - sinon.assert.calledOnce(spy); - $timeout.flush(); - sinon.assert.calledTwice(spy); }); it('should only fire on leading edge', function () { const bouncer = debounce(spy, 100, { leading: true, trailing: false }); - const bouncerFromProvider = debounceFromProvider(spy, 100, { - leading: true, - trailing: false, - }); bouncer(); sinon.assert.calledOnce(spy); @@ -122,17 +100,11 @@ describe('debounce service', function () { sinon.assert.calledOnce(spy); spy.resetHistory(); - - bouncerFromProvider(); - sinon.assert.calledOnce(spy); - $timeout.flush(); - sinon.assert.calledOnce(spy); }); it('should reset delayed execution', function () { const cancelSpy = sinon.spy($timeout, 'cancel'); const bouncer = debounce(spy, 100); - const bouncerFromProvider = debounceFromProvider(spy, 100); bouncer(); sandbox.clock.tick(1); @@ -145,15 +117,6 @@ describe('debounce service', function () { spy.resetHistory(); cancelSpy.resetHistory(); - - bouncerFromProvider(); - sandbox.clock.tick(1); - - bouncerFromProvider(); - sinon.assert.notCalled(spy); - $timeout.flush(); - sinon.assert.calledOnce(spy); - sinon.assert.calledOnce(cancelSpy); }); }); @@ -161,7 +124,6 @@ describe('debounce service', function () { it('should cancel the $timeout', function () { const cancelSpy = sinon.spy($timeout, 'cancel'); const bouncer = debounce(spy, 100); - const bouncerFromProvider = debounceFromProvider(spy, 100); bouncer(); bouncer.cancel(); @@ -170,12 +132,6 @@ describe('debounce service', function () { $timeout.verifyNoPendingTasks(); cancelSpy.resetHistory(); - - bouncerFromProvider(); - bouncerFromProvider.cancel(); - sinon.assert.calledOnce(cancelSpy); - // throws if pending timeouts - $timeout.verifyNoPendingTasks(); }); }); }); diff --git a/src/plugins/discover/public/application/angular/directives/debounce/index.js b/src/plugins/discover/public/application/angular/directives/debounce/index.js index 35b8339263626..3c51895f19828 100644 --- a/src/plugins/discover/public/application/angular/directives/debounce/index.js +++ b/src/plugins/discover/public/application/angular/directives/debounce/index.js @@ -17,6 +17,4 @@ * under the License. */ -import './debounce'; - -export { DebounceProvider } from './debounce'; +export { createDebounceProviderTimeout } from './debounce'; diff --git a/src/plugins/discover/public/application/angular/directives/fixed_scroll.js b/src/plugins/discover/public/application/angular/directives/fixed_scroll.js index 182b4aeca9a23..e2d5f10a0faf7 100644 --- a/src/plugins/discover/public/application/angular/directives/fixed_scroll.js +++ b/src/plugins/discover/public/application/angular/directives/fixed_scroll.js @@ -19,7 +19,7 @@ import $ from 'jquery'; import _ from 'lodash'; -import { DebounceProvider } from './debounce'; +import { createDebounceProviderTimeout } from './debounce'; const SCROLLER_HEIGHT = 20; @@ -28,124 +28,128 @@ const SCROLLER_HEIGHT = 20; * to the target element's real scrollbar. This is useful when the target element's horizontal scrollbar * might be waaaay down the page, like the doc table on Discover. */ -export function FixedScrollProvider(Private) { - const debounce = Private(DebounceProvider); - +export function FixedScrollProvider($timeout) { return { restrict: 'A', link: function ($scope, $el) { - let $window = $(window); - let $scroller = $('
').height(SCROLLER_HEIGHT); - - /** - * Remove the listeners bound in listen() - * @type {function} - */ - let unlisten = _.noop; - - /** - * Listen for scroll events on the $scroller and the $el, sets unlisten() - * - * unlisten must be called before calling or listen() will throw an Error - * - * Since the browser emits "scroll" events after setting scrollLeft - * the listeners also prevent tug-of-war - * - * @throws {Error} If unlisten was not called first - * @return {undefined} - */ - function listen() { - if (unlisten !== _.noop) { - throw new Error( - 'fixedScroll listeners were not cleaned up properly before re-listening!' - ); - } - - let blockTo; - function bind($from, $to) { - function handler() { - if (blockTo === $to) return (blockTo = null); - $to.scrollLeft((blockTo = $from).scrollLeft()); - } - - $from.on('scroll', handler); - return function () { - $from.off('scroll', handler); - }; - } - - unlisten = _.flow(bind($el, $scroller), bind($scroller, $el), function () { - unlisten = _.noop; - }); - } - - /** - * Revert DOM changes and event listeners - * @return {undefined} - */ - function cleanUp() { - unlisten(); - $scroller.detach(); - $el.css('padding-bottom', 0); - } - - /** - * Modify the DOM and attach event listeners based on need. - * Is called many times to re-setup, must be idempotent - * @return {undefined} - */ - function setup() { - cleanUp(); - - const containerWidth = $el.width(); - const contentWidth = $el.prop('scrollWidth'); - const containerHorizOverflow = contentWidth - containerWidth; - - const elTop = $el.offset().top - $window.scrollTop(); - const elBottom = elTop + $el.height(); - const windowVertOverflow = elBottom - $window.height(); - - const requireScroller = containerHorizOverflow > 0 && windowVertOverflow > 0; - if (!requireScroller) return; - - // push the content away from the scroller - $el.css('padding-bottom', SCROLLER_HEIGHT); - - // fill the scroller with a dummy element that mimics the content - $scroller - .width(containerWidth) - .html($('
').css({ width: contentWidth, height: SCROLLER_HEIGHT })) - .insertAfter($el); + return createFixedScroll($scope, $timeout)($el); + }, + }; +} - // listen for scroll events - listen(); +export function createFixedScroll($scope, $timeout) { + const debounce = createDebounceProviderTimeout($timeout); + return function (el) { + const $el = typeof el.css === 'function' ? el : $(el); + let $window = $(window); + let $scroller = $('
').height(SCROLLER_HEIGHT); + + /** + * Remove the listeners bound in listen() + * @type {function} + */ + let unlisten = _.noop; + + /** + * Listen for scroll events on the $scroller and the $el, sets unlisten() + * + * unlisten must be called before calling or listen() will throw an Error + * + * Since the browser emits "scroll" events after setting scrollLeft + * the listeners also prevent tug-of-war + * + * @throws {Error} If unlisten was not called first + * @return {undefined} + */ + function listen() { + if (unlisten !== _.noop) { + throw new Error('fixedScroll listeners were not cleaned up properly before re-listening!'); } - let width; - let scrollWidth; - function checkWidth() { - const newScrollWidth = $el.prop('scrollWidth'); - const newWidth = $el.width(); - - if (scrollWidth !== newScrollWidth || width !== newWidth) { - $scope.$apply(setup); - - scrollWidth = newScrollWidth; - width = newWidth; + let blockTo; + function bind($from, $to) { + function handler() { + if (blockTo === $to) return (blockTo = null); + $to.scrollLeft((blockTo = $from).scrollLeft()); } - } - const debouncedCheckWidth = debounce(checkWidth, 100, { - invokeApply: false, - }); - $scope.$watch(debouncedCheckWidth); + $from.on('scroll', handler); + return function () { + $from.off('scroll', handler); + }; + } - // cleanup when the scope is destroyed - $scope.$on('$destroy', function () { - cleanUp(); - debouncedCheckWidth.cancel(); - $scroller = $window = null; + unlisten = _.flow(bind($el, $scroller), bind($scroller, $el), function () { + unlisten = _.noop; }); - }, + } + + /** + * Revert DOM changes and event listeners + * @return {undefined} + */ + function cleanUp() { + unlisten(); + $scroller.detach(); + $el.css('padding-bottom', 0); + } + + /** + * Modify the DOM and attach event listeners based on need. + * Is called many times to re-setup, must be idempotent + * @return {undefined} + */ + function setup() { + cleanUp(); + + const containerWidth = $el.width(); + const contentWidth = $el.prop('scrollWidth'); + const containerHorizOverflow = contentWidth - containerWidth; + + const elTop = $el.offset().top - $window.scrollTop(); + const elBottom = elTop + $el.height(); + const windowVertOverflow = elBottom - $window.height(); + + const requireScroller = containerHorizOverflow > 0 && windowVertOverflow > 0; + if (!requireScroller) return; + + // push the content away from the scroller + $el.css('padding-bottom', SCROLLER_HEIGHT); + + // fill the scroller with a dummy element that mimics the content + $scroller + .width(containerWidth) + .html($('
').css({ width: contentWidth, height: SCROLLER_HEIGHT })) + .insertAfter($el); + + // listen for scroll events + listen(); + } + + let width; + let scrollWidth; + function checkWidth() { + const newScrollWidth = $el.prop('scrollWidth'); + const newWidth = $el.width(); + + if (scrollWidth !== newScrollWidth || width !== newWidth) { + $scope.$apply(setup); + + scrollWidth = newScrollWidth; + width = newWidth; + } + } + + const debouncedCheckWidth = debounce(checkWidth, 100, { + invokeApply: false, + }); + $scope.$watch(debouncedCheckWidth); + + function destroy() { + cleanUp(); + debouncedCheckWidth.cancel(); + $scroller = $window = null; + } + return destroy; }; } diff --git a/src/plugins/discover/public/application/angular/directives/fixed_scroll.test.js b/src/plugins/discover/public/application/angular/directives/fixed_scroll.test.js index 65255d6c0c4a4..e44bb45cf2431 100644 --- a/src/plugins/discover/public/application/angular/directives/fixed_scroll.test.js +++ b/src/plugins/discover/public/application/angular/directives/fixed_scroll.test.js @@ -23,17 +23,12 @@ import $ from 'jquery'; import sinon from 'sinon'; -import { PrivateProvider, initAngularBootstrap } from '../../../../../kibana_legacy/public'; +import { initAngularBootstrap } from '../../../../../kibana_legacy/public'; import { FixedScrollProvider } from './fixed_scroll'; -import { DebounceProviderTimeout } from './debounce/debounce'; const testModuleName = 'fixedScroll'; -angular - .module(testModuleName, []) - .provider('Private', PrivateProvider) - .service('debounce', ['$timeout', DebounceProviderTimeout]) - .directive('fixedScroll', FixedScrollProvider); +angular.module(testModuleName, []).directive('fixedScroll', FixedScrollProvider); describe('FixedScroll directive', function () { const sandbox = sinon.createSandbox(); @@ -127,7 +122,7 @@ describe('FixedScroll directive', function () { return { $container: $el, $content: $content, - $scroller: $parent.find('.fixed-scroll-scroller'), + $scroller: $parent.find('.dscTableFixedScroll__scroller'), }; }; }); diff --git a/src/plugins/discover/public/application/angular/directives/no_results.js b/src/plugins/discover/public/application/angular/directives/no_results.js index 965c1271c2f2c..d8a39d9178e93 100644 --- a/src/plugins/discover/public/application/angular/directives/no_results.js +++ b/src/plugins/discover/public/application/angular/directives/no_results.js @@ -24,7 +24,6 @@ import PropTypes from 'prop-types'; import { EuiCallOut, EuiCode, - EuiCodeBlock, EuiDescriptionList, EuiFlexGroup, EuiFlexItem, @@ -37,72 +36,12 @@ import { getServices } from '../../../kibana_services'; // eslint-disable-next-line react/prefer-stateless-function export class DiscoverNoResults extends Component { static propTypes = { - shardFailures: PropTypes.array, timeFieldName: PropTypes.string, queryLanguage: PropTypes.string, }; render() { - const { shardFailures, timeFieldName, queryLanguage } = this.props; - - let shardFailuresMessage; - - if (shardFailures && shardFailures.length) { - const failures = shardFailures.map((failure, index) => ( -
- - - - - ), - failureShard: `‘${failure.shard}’`, - }} - /> - - - - - {JSON.stringify(failure.reason)} - - {index < shardFailures.length - 1 ? : undefined} -
- )); - - shardFailuresMessage = ( - - - - -

- -

- -

- -

- - {failures} -
-
- ); - } + const { timeFieldName, queryLanguage } = this.props; let timeFieldMessage; @@ -264,8 +203,6 @@ export class DiscoverNoResults extends Component { iconType="help" data-test-subj="discoverNoResults" /> - - {shardFailuresMessage} {timeFieldMessage} {luceneQueryMessage} diff --git a/src/plugins/discover/public/application/angular/directives/no_results.test.js b/src/plugins/discover/public/application/angular/directives/no_results.test.js index 7de792c612993..60c50048a39ef 100644 --- a/src/plugins/discover/public/application/angular/directives/no_results.test.js +++ b/src/plugins/discover/public/application/angular/directives/no_results.test.js @@ -42,35 +42,6 @@ beforeEach(() => { describe('DiscoverNoResults', () => { describe('props', () => { - describe('shardFailures', () => { - test('renders failures list when there are failures', () => { - const shardFailures = [ - { - index: 'A', - shard: '1', - reason: { reason: 'Awful error' }, - }, - { - index: 'B', - shard: '2', - reason: { reason: 'Bad error' }, - }, - ]; - - const component = renderWithIntl(); - - expect(component).toMatchSnapshot(); - }); - - test(`doesn't render failures list when there are no failures`, () => { - const shardFailures = []; - - const component = renderWithIntl(); - - expect(component).toMatchSnapshot(); - }); - }); - describe('timeFieldName', () => { test('renders time range feedback', () => { const component = renderWithIntl(); diff --git a/src/plugins/discover/public/application/angular/discover.html b/src/plugins/discover/public/application/angular/discover.html deleted file mode 100644 index 94f13e1cd8132..0000000000000 --- a/src/plugins/discover/public/application/angular/discover.html +++ /dev/null @@ -1,159 +0,0 @@ - -

{{screenTitle}}

- - - - - -
- -
- diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 22da3e877054a..7871cc4b16464 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -29,18 +29,18 @@ import { getState, splitState } from './discover_state'; import { RequestAdapter } from '../../../../inspector/public'; import { SavedObjectSaveModal, showSaveModal } from '../../../../saved_objects/public'; import { getSortArray, getSortForSearchSource } from './doc_table'; +import { createFixedScroll } from './directives/fixed_scroll'; import * as columnActions from './doc_table/actions/columns'; - -import indexTemplate from './discover.html'; +import indexTemplateLegacy from './discover_legacy.html'; import { showOpenSearchPanel } from '../components/top_nav/show_open_search_panel'; import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; -import '../components/fetch_error'; import { getPainlessError } from './get_painless_error'; import { discoverResponseHandler } from './response_handler'; import { getRequestInspectorStats, getResponseInspectorStats, getServices, + getHeaderActionMenuMounter, getUrlTracker, unhashUrl, subscribeWithScope, @@ -70,7 +70,6 @@ import { indexPatterns as indexPatternsUtils, connectToQueryState, syncQueryStateWithUrl, - search, } from '../../../../data/public'; import { getIndexPatternId } from '../helpers/get_index_pattern_id'; import { addFatalError } from '../../../../kibana_legacy/public'; @@ -114,7 +113,7 @@ app.config(($routeProvider) => { }; const discoverRoute = { ...defaults, - template: indexTemplate, + template: indexTemplateLegacy, reloadOnSearch: false, resolve: { savedObjects: function ($route, Promise) { @@ -307,18 +306,10 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise mode: 'absolute', }); }; - $scope.intervalOptions = search.aggs.intervalOptions; $scope.minimumVisibleRows = 50; $scope.fetchStatus = fetchStatuses.UNINITIALIZED; $scope.showSaveQuery = uiCapabilities.discover.saveQuery; - $scope.$watch( - () => uiCapabilities.discover.saveQuery, - (newCapability) => { - $scope.showSaveQuery = newCapability; - } - ); - let abortController; $scope.$on('$destroy', () => { if (abortController) abortController.abort(); @@ -513,8 +504,6 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise ]); } - $scope.screenTitle = savedSearch.title; - const getFieldCounts = async () => { // the field counts aren't set until we have the data back, // so we wait for the fetch to be done before proceeding @@ -610,6 +599,9 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise timefield: getTimeField(), savedSearch: savedSearch, indexPatternList: $route.current.locals.savedObjects.ip.list, + config: config, + fixedScroll: createFixedScroll($scope, $timeout), + setHeaderActionMenu: getHeaderActionMenuMounter(), }; const shouldSearchOnPageLoad = () => { @@ -769,6 +761,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise if (!init.complete) return; $scope.fetchCounter++; $scope.fetchError = undefined; + $scope.minimumVisibleRows = 50; if (!validateTimeRange(timefilter.getTime(), toastNotifications)) { $scope.resultState = 'none'; return; @@ -866,9 +859,6 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise tabifiedData, getDimensions($scope.vis.data.aggs.aggs, $scope.timeRange) ); - if ($scope.vis.data.aggs.aggs[1]) { - $scope.bucketInterval = $scope.vis.data.aggs.aggs[1].buckets.getInterval(); - } $scope.updateTime(); } diff --git a/src/plugins/discover/public/application/angular/discover_legacy.html b/src/plugins/discover/public/application/angular/discover_legacy.html new file mode 100644 index 0000000000000..8582f71c0cb88 --- /dev/null +++ b/src/plugins/discover/public/application/angular/discover_legacy.html @@ -0,0 +1,36 @@ + + + + diff --git a/src/plugins/discover/public/application/angular/discover_state.ts b/src/plugins/discover/public/application/angular/discover_state.ts index ac0dc054485f0..5ddb6a92b5fd4 100644 --- a/src/plugins/discover/public/application/angular/discover_state.ts +++ b/src/plugins/discover/public/application/angular/discover_state.ts @@ -55,6 +55,10 @@ export interface AppState { * Array of the used sorting [[field,direction],...] */ sort?: string[][]; + /** + * id of the used saved query + */ + savedQuery?: string; } interface GetStateParams { diff --git a/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx new file mode 100644 index 0000000000000..ad2b674af014c --- /dev/null +++ b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx @@ -0,0 +1,131 @@ +/* + * 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 angular, { auto, ICompileService, IScope } from 'angular'; +import { render } from 'react-dom'; +import React, { useRef, useEffect } from 'react'; +import { getServices, IIndexPattern } from '../../../kibana_services'; +import { IndexPatternField } from '../../../../../data/common/index_patterns'; +export type AngularScope = IScope; + +export interface AngularDirective { + template: string; +} + +/** + * Compiles and injects the give angular template into the given dom node + * returns a function to cleanup the injected angular element + */ +export async function injectAngularElement( + domNode: Element, + template: string, + scopeProps: any, + getInjector: () => Promise +): Promise<() => void> { + const $injector = await getInjector(); + const rootScope: AngularScope = $injector.get('$rootScope'); + const $compile: ICompileService = $injector.get('$compile'); + const newScope = Object.assign(rootScope.$new(), scopeProps); + + const $target = angular.element(domNode); + const $element = angular.element(template); + + newScope.$apply(() => { + const linkFn = $compile($element); + $target.empty().append($element); + linkFn(newScope); + }); + + return () => { + newScope.$destroy(); + }; +} + +/** + * Converts a given legacy angular directive to a render function + * for usage in a react component. Note that the rendering is async + */ +export function convertDirectiveToRenderFn( + directive: AngularDirective, + getInjector: () => Promise +) { + return (domNode: Element, props: any) => { + let rejected = false; + + const cleanupFnPromise = injectAngularElement(domNode, directive.template, props, getInjector); + cleanupFnPromise.catch(() => { + rejected = true; + render(
error
, domNode); + }); + + return () => { + if (!rejected) { + // for cleanup + // http://roubenmeschian.com/rubo/?p=51 + cleanupFnPromise.then((cleanup) => cleanup()); + } + }; + }; +} + +export interface DocTableLegacyProps { + columns: string[]; + searchDescription?: string; + searchTitle?: string; + onFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; + rows: Array>; + indexPattern: IIndexPattern; + minimumVisibleRows: number; + onAddColumn: (column: string) => void; + onSort: (sort: string[][]) => void; + onMoveColumn: (columns: string, newIdx: number) => void; + onRemoveColumn: (column: string) => void; + sort?: string[][]; +} + +export function DocTableLegacy(renderProps: DocTableLegacyProps) { + const renderFn = convertDirectiveToRenderFn( + { + template: ``, + }, + () => getServices().getEmbeddableInjector() + ); + const ref = useRef(null); + useEffect(() => { + if (ref && ref.current) { + return renderFn(ref.current, renderProps); + } + }, [renderFn, renderProps]); + return
; +} diff --git a/src/plugins/discover/public/application/angular/doc_table/doc_table.ts b/src/plugins/discover/public/application/angular/doc_table/doc_table.ts index f972c158ff3dd..735ee9f555740 100644 --- a/src/plugins/discover/public/application/angular/doc_table/doc_table.ts +++ b/src/plugins/discover/public/application/angular/doc_table/doc_table.ts @@ -50,10 +50,6 @@ export function createDocTableDirective(pagerFactory: any, $filter: any) { inspectorAdapters: '=?', }, link: ($scope: LazyScope, $el: JQuery) => { - $scope.$watch('minimumVisibleRows', (minimumVisibleRows: number) => { - $scope.limit = Math.max(minimumVisibleRows || 50, $scope.limit || 50); - }); - $scope.persist = { sorting: $scope.sorting, columns: $scope.columns, @@ -77,7 +73,7 @@ export function createDocTableDirective(pagerFactory: any, $filter: any) { if (!hits) return; // Reset infinite scroll limit - $scope.limit = 50; + $scope.limit = $scope.minimumVisibleRows || 50; if (hits.length === 0) { dispatchRenderComplete($el[0]); diff --git a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts new file mode 100644 index 0000000000000..a3502cbb211fa --- /dev/null +++ b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts @@ -0,0 +1,56 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { DiscoverLegacy } from './discover_legacy'; + +export function createDiscoverLegacyDirective(reactDirective: any) { + return reactDirective(DiscoverLegacy, [ + ['addColumn', { watchDepth: 'reference' }], + ['fetch', { watchDepth: 'reference' }], + ['fetchCounter', { watchDepth: 'reference' }], + ['fetchError', { watchDepth: 'reference' }], + ['fieldCounts', { watchDepth: 'reference' }], + ['histogramData', { watchDepth: 'reference' }], + ['hits', { watchDepth: 'reference' }], + ['indexPattern', { watchDepth: 'reference' }], + ['minimumVisibleRows', { watchDepth: 'reference' }], + ['onAddFilter', { watchDepth: 'reference' }], + ['onChangeInterval', { watchDepth: 'reference' }], + ['onMoveColumn', { watchDepth: 'reference' }], + ['onRemoveColumn', { watchDepth: 'reference' }], + ['onSetColumns', { watchDepth: 'reference' }], + ['onSkipBottomButtonClick', { watchDepth: 'reference' }], + ['onSort', { watchDepth: 'reference' }], + ['opts', { watchDepth: 'reference' }], + ['resetQuery', { watchDepth: 'reference' }], + ['resultState', { watchDepth: 'reference' }], + ['rows', { watchDepth: 'reference' }], + ['savedSearch', { watchDepth: 'reference' }], + ['searchSource', { watchDepth: 'reference' }], + ['setIndexPattern', { watchDepth: 'reference' }], + ['showSaveQuery', { watchDepth: 'reference' }], + ['state', { watchDepth: 'reference' }], + ['timefilterUpdateHandler', { watchDepth: 'reference' }], + ['timeRange', { watchDepth: 'reference' }], + ['topNavMenu', { watchDepth: 'reference' }], + ['updateQuery', { watchDepth: 'reference' }], + ['updateSavedQueryId', { watchDepth: 'reference' }], + ['vis', { watchDepth: 'reference' }], + ]); +} diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx new file mode 100644 index 0000000000000..1a98843649259 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -0,0 +1,324 @@ +/* + * 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, { useState, useCallback, useEffect } from 'react'; +import classNames from 'classnames'; +import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; +import { IUiSettingsClient, MountPoint } from 'kibana/public'; +import { HitsCounter } from './hits_counter'; +import { TimechartHeader } from './timechart_header'; +import { DiscoverSidebar } from './sidebar'; +import { getServices, IIndexPattern } from '../../kibana_services'; +// @ts-ignore +import { DiscoverNoResults } from '../angular/directives/no_results'; +import { DiscoverUninitialized } from '../angular/directives/uninitialized'; +import { DiscoverHistogram } from '../angular/directives/histogram'; +import { LoadingSpinner } from './loading_spinner/loading_spinner'; +import { DiscoverFetchError, FetchError } from './fetch_error/fetch_error'; +import { DocTableLegacy } from '../angular/doc_table/create_doc_table_react'; +import { SkipBottomButton } from './skip_bottom_button'; +import { + IndexPatternField, + search, + ISearchSource, + TimeRange, + Query, + IndexPatternAttributes, +} from '../../../../data/public'; +import { Chart } from '../angular/helpers/point_series'; +import { AppState } from '../angular/discover_state'; +import { SavedSearch } from '../../saved_searches'; + +import { SavedObject } from '../../../../../core/types'; +import { Vis } from '../../../../visualizations/public'; +import { TopNavMenuData } from '../../../../navigation/public'; + +export interface DiscoverLegacyProps { + addColumn: (column: string) => void; + fetch: () => void; + fetchCounter: number; + fetchError: FetchError; + fieldCounts: Record; + histogramData: Chart; + hits: number; + indexPattern: IIndexPattern; + minimumVisibleRows: number; + onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; + onChangeInterval: (interval: string) => void; + onMoveColumn: (columns: string, newIdx: number) => void; + onRemoveColumn: (column: string) => void; + onSetColumns: (columns: string[]) => void; + onSkipBottomButtonClick: () => void; + onSort: (sort: string[][]) => void; + opts: { + savedSearch: SavedSearch; + config: IUiSettingsClient; + indexPatternList: Array>; + timefield: string; + sampleSize: number; + fixedScroll: (el: HTMLElement) => void; + setHeaderActionMenu: (menuMount: MountPoint | undefined) => void; + }; + resetQuery: () => void; + resultState: string; + rows: Array>; + searchSource: ISearchSource; + setIndexPattern: (id: string) => void; + showSaveQuery: boolean; + state: AppState; + timefilterUpdateHandler: (ranges: { from: number; to: number }) => void; + timeRange?: { from: string; to: string }; + topNavMenu: TopNavMenuData[]; + updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; + updateSavedQueryId: (savedQueryId?: string) => void; + vis?: Vis; +} + +export function DiscoverLegacy({ + addColumn, + fetch, + fetchCounter, + fetchError, + fieldCounts, + histogramData, + hits, + indexPattern, + minimumVisibleRows, + onAddFilter, + onChangeInterval, + onMoveColumn, + onRemoveColumn, + onSkipBottomButtonClick, + onSort, + opts, + resetQuery, + resultState, + rows, + searchSource, + setIndexPattern, + showSaveQuery, + state, + timefilterUpdateHandler, + timeRange, + topNavMenu, + updateQuery, + updateSavedQueryId, + vis, +}: DiscoverLegacyProps) { + const [isSidebarClosed, setIsSidebarClosed] = useState(false); + const { TopNavMenu } = getServices().navigation.ui; + const { savedSearch, indexPatternList } = opts; + const bucketAggConfig = vis?.data?.aggs?.aggs[1]; + const bucketInterval = + bucketAggConfig && search.aggs.isDateHistogramBucketAggConfig(bucketAggConfig) + ? bucketAggConfig.buckets?.getInterval() + : undefined; + const [fixedScrollEl, setFixedScrollEl] = useState(); + + useEffect(() => (fixedScrollEl ? opts.fixedScroll(fixedScrollEl) : undefined), [ + fixedScrollEl, + opts, + ]); + const fixedScrollRef = useCallback( + (node: HTMLElement) => { + if (node !== null) { + setFixedScrollEl(node); + } + }, + [setFixedScrollEl] + ); + const sidebarClassName = classNames({ + closed: isSidebarClosed, + }); + + const mainSectionClassName = classNames({ + 'col-md-10': !isSidebarClosed, + 'col-md-12': isSidebarClosed, + }); + + return ( + +
+

{savedSearch.title}

+ +
+
+
+ {!isSidebarClosed && ( +
+ +
+ )} + setIsSidebarClosed(!isSidebarClosed)} + data-test-subj="collapseSideBarButton" + aria-controls="discover-sidebar" + aria-expanded={isSidebarClosed ? 'false' : 'true'} + aria-label="Toggle sidebar" + className="dscCollapsibleSidebar__collapseButton" + /> +
+
+
+
+
+ + ); +} diff --git a/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx b/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx index 880a493983adf..dc8f1238eac6f 100644 --- a/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx +++ b/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx @@ -20,18 +20,20 @@ import './fetch_error.scss'; import React, { Fragment } from 'react'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiCallOut, EuiCodeBlock, EuiSpacer } from '@elastic/eui'; -import { getAngularModule, getServices } from '../../../kibana_services'; +import { getServices } from '../../../kibana_services'; + +export interface FetchError { + lang: string; + script: string; + message: string; + error: string; +} interface Props { - fetchError: { - lang: string; - script: string; - message: string; - error: string; - }; + fetchError: FetchError; } -const DiscoverFetchError = ({ fetchError }: Props) => { +export const DiscoverFetchError = ({ fetchError }: Props) => { if (!fetchError) { return null; } @@ -92,9 +94,3 @@ const DiscoverFetchError = ({ fetchError }: Props) => { ); }; - -export function createFetchErrorDirective(reactDirective: any) { - return reactDirective(DiscoverFetchError); -} - -getAngularModule().directive('discoverFetchError', createFetchErrorDirective); diff --git a/src/plugins/discover/public/application/components/fetch_error/index.js b/src/plugins/discover/public/application/components/fetch_error/index.ts similarity index 100% rename from src/plugins/discover/public/application/components/fetch_error/index.js rename to src/plugins/discover/public/application/components/fetch_error/index.ts diff --git a/src/plugins/discover/public/application/components/hits_counter/hits_counter_directive.ts b/src/plugins/discover/public/application/components/hits_counter/hits_counter_directive.ts deleted file mode 100644 index 8d45e28370cad..0000000000000 --- a/src/plugins/discover/public/application/components/hits_counter/hits_counter_directive.ts +++ /dev/null @@ -1,27 +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 { HitsCounter } from './hits_counter'; - -export function createHitsCounterDirective(reactDirective: any) { - return reactDirective(HitsCounter, [ - ['hits', { watchDepth: 'reference' }], - ['showResetButton', { watchDepth: 'reference' }], - ['onResetQuery', { watchDepth: 'reference' }], - ]); -} diff --git a/src/plugins/discover/public/application/components/hits_counter/index.ts b/src/plugins/discover/public/application/components/hits_counter/index.ts index 58e7a9eda7f51..0ce95f061df17 100644 --- a/src/plugins/discover/public/application/components/hits_counter/index.ts +++ b/src/plugins/discover/public/application/components/hits_counter/index.ts @@ -18,4 +18,3 @@ */ export { HitsCounter } from './hits_counter'; -export { createHitsCounterDirective } from './hits_counter_directive'; diff --git a/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.tsx b/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.tsx index 44b922bf0f708..4e1754638d479 100644 --- a/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.tsx +++ b/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.tsx @@ -18,24 +18,18 @@ */ import React from 'react'; import { EuiLoadingSpinner, EuiTitle, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; +import { FormattedMessage } from '@kbn/i18n/react'; export function LoadingSpinner() { return ( - - <> - -

- -

-
- - - -
+ <> + +

+ +

+
+ + + ); } - -export function createLoadingSpinnerDirective(reactDirective: any) { - return reactDirective(LoadingSpinner); -} diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx index 850624888b24a..2407cff181901 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx @@ -68,7 +68,7 @@ export interface DiscoverSidebarProps { /** * Currently selected index pattern */ - selectedIndexPattern: IndexPattern; + selectedIndexPattern?: IndexPattern; /** * Callback function to select another index pattern */ diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar_directive.ts b/src/plugins/discover/public/application/components/sidebar/discover_sidebar_directive.ts deleted file mode 100644 index b271c920e5e01..0000000000000 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar_directive.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { DiscoverSidebar } from './discover_sidebar'; - -export function createDiscoverSidebarDirective(reactDirective: any) { - return reactDirective(DiscoverSidebar, [ - ['columns', { watchDepth: 'reference' }], - ['fieldCounts', { watchDepth: 'reference' }], - ['hits', { watchDepth: 'reference' }], - ['indexPatternList', { watchDepth: 'reference' }], - ['onAddField', { watchDepth: 'reference' }], - ['onAddFilter', { watchDepth: 'reference' }], - ['onRemoveField', { watchDepth: 'reference' }], - ['selectedIndexPattern', { watchDepth: 'reference' }], - ['setIndexPattern', { watchDepth: 'reference' }], - ]); -} diff --git a/src/plugins/discover/public/application/components/sidebar/index.ts b/src/plugins/discover/public/application/components/sidebar/index.ts index 1b837840b52f6..aec8dfc86e817 100644 --- a/src/plugins/discover/public/application/components/sidebar/index.ts +++ b/src/plugins/discover/public/application/components/sidebar/index.ts @@ -18,4 +18,3 @@ */ export { DiscoverSidebar } from './discover_sidebar'; -export { createDiscoverSidebarDirective } from './discover_sidebar_directive'; diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts index 13051f88c9591..22a6e7a628555 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts @@ -25,8 +25,11 @@ export function getDetails( field: IndexPatternField, hits: Array>, columns: string[], - indexPattern: IndexPattern + indexPattern?: IndexPattern ) { + if (!indexPattern) { + return {}; + } const details = { ...fieldCalculator.getFieldValueCounts({ hits, diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_index_pattern_field_list.ts b/src/plugins/discover/public/application/components/sidebar/lib/get_index_pattern_field_list.ts index c96a8f5ce17b9..eff7c2ec3c1c8 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/get_index_pattern_field_list.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/get_index_pattern_field_list.ts @@ -20,8 +20,8 @@ import { difference } from 'lodash'; import { IndexPattern, IndexPatternField } from 'src/plugins/data/public'; export function getIndexPatternFieldList( - indexPattern: IndexPattern, - fieldCounts: Record + indexPattern?: IndexPattern, + fieldCounts?: Record ) { if (!indexPattern || !fieldCounts) return []; diff --git a/src/plugins/discover/public/application/components/skip_bottom_button/index.ts b/src/plugins/discover/public/application/components/skip_bottom_button/index.ts index 2feaa35e0d61f..b3d93e40be0bd 100644 --- a/src/plugins/discover/public/application/components/skip_bottom_button/index.ts +++ b/src/plugins/discover/public/application/components/skip_bottom_button/index.ts @@ -18,4 +18,3 @@ */ export { SkipBottomButton } from './skip_bottom_button'; -export { createSkipBottomButtonDirective } from './skip_bottom_button_directive'; diff --git a/src/plugins/discover/public/application/components/timechart_header/index.ts b/src/plugins/discover/public/application/components/timechart_header/index.ts index 43473319c318c..34bed2cd72a74 100644 --- a/src/plugins/discover/public/application/components/timechart_header/index.ts +++ b/src/plugins/discover/public/application/components/timechart_header/index.ts @@ -18,4 +18,3 @@ */ export { TimechartHeader } from './timechart_header'; -export { createTimechartHeaderDirective } from './timechart_header_directive'; diff --git a/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx b/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx index a4c10e749d868..7889b05a88415 100644 --- a/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx +++ b/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx @@ -29,8 +29,10 @@ describe('timechart header', function () { beforeAll(() => { props = { - from: 'May 14, 2020 @ 11:05:13.590', - to: 'May 14, 2020 @ 11:20:13.590', + timeRange: { + from: 'May 14, 2020 @ 11:05:13.590', + to: 'May 14, 2020 @ 11:20:13.590', + }, stateInterval: 's', options: [ { @@ -47,9 +49,11 @@ describe('timechart header', function () { }, ], onChangeInterval: jest.fn(), - showScaledInfo: undefined, - bucketIntervalDescription: 'second', - bucketIntervalScale: undefined, + bucketInterval: { + scaled: undefined, + description: 'second', + scale: undefined, + }, }; }); @@ -58,8 +62,8 @@ describe('timechart header', function () { expect(component.find(EuiIconTip).length).toBe(0); }); - it('TimechartHeader renders an info text by providing the showScaledInfo property', () => { - props.showScaledInfo = true; + it('TimechartHeader renders an info when bucketInterval.scale is set to true', () => { + props.bucketInterval!.scaled = true; component = mountWithIntl(); expect(component.find(EuiIconTip).length).toBe(1); }); diff --git a/src/plugins/discover/public/application/components/timechart_header/timechart_header.tsx b/src/plugins/discover/public/application/components/timechart_header/timechart_header.tsx index 8789847058aff..1451106827ee0 100644 --- a/src/plugins/discover/public/application/components/timechart_header/timechart_header.tsx +++ b/src/plugins/discover/public/application/components/timechart_header/timechart_header.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, @@ -27,16 +27,28 @@ import { } from '@elastic/eui'; import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import moment from 'moment'; export interface TimechartHeaderProps { /** - * the query from date string + * Format of date to be displayed */ - from: string; + dateFormat?: string; /** - * the query to date string + * Interval for the buckets of the recent request */ - to: string; + bucketInterval?: { + scaled?: boolean; + description?: string; + scale?: number; + }; + /** + * Range of dates to be displayed + */ + timeRange?: { + from: string; + to: string; + }; /** * Interval Options */ @@ -49,31 +61,29 @@ export interface TimechartHeaderProps { * selected interval */ stateInterval: string; - /** - * displays the scaled info of the interval - */ - showScaledInfo: boolean | undefined; - /** - * scaled info description - */ - bucketIntervalDescription: string; - /** - * bucket interval scale - */ - bucketIntervalScale: number | undefined; } export function TimechartHeader({ - from, - to, + bucketInterval, + dateFormat, + timeRange, options, onChangeInterval, stateInterval, - showScaledInfo, - bucketIntervalDescription, - bucketIntervalScale, }: TimechartHeaderProps) { const [interval, setInterval] = useState(stateInterval); + const toMoment = useCallback( + (datetime: string) => { + if (!datetime) { + return ''; + } + if (!dateFormat) { + return datetime; + } + return moment(datetime).format(dateFormat); + }, + [dateFormat] + ); useEffect(() => { setInterval(stateInterval); @@ -84,6 +94,10 @@ export function TimechartHeader({ onChangeInterval(e.target.value); }; + if (!timeRange || !bucketInterval) { + return null; + } + return ( @@ -95,7 +109,7 @@ export function TimechartHeader({ delay="long" > - {`${from} - ${to} ${ + {`${toMoment(timeRange.from)} - ${toMoment(timeRange.to)} ${ interval !== 'auto' ? i18n.translate('discover.timechartHeader.timeIntervalSelect.per', { defaultMessage: 'per', @@ -125,7 +139,7 @@ export function TimechartHeader({ value={interval} onChange={handleIntervalChange} append={ - showScaledInfo ? ( + bucketInterval.scaled ? ( 1 + bucketInterval!.scale && bucketInterval!.scale > 1 ? i18n.translate('discover.bucketIntervalTooltip.tooLargeBucketsText', { defaultMessage: 'buckets that are too large', }) : i18n.translate('discover.bucketIntervalTooltip.tooManyBucketsText', { defaultMessage: 'too many buckets', }), - bucketIntervalDescription, + bucketIntervalDescription: bucketInterval.description, }, })} color="warning" diff --git a/src/plugins/discover/public/application/components/timechart_header/timechart_header_directive.ts b/src/plugins/discover/public/application/components/timechart_header/timechart_header_directive.ts deleted file mode 100644 index 027236cd46521..0000000000000 --- a/src/plugins/discover/public/application/components/timechart_header/timechart_header_directive.ts +++ /dev/null @@ -1,32 +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 { TimechartHeader } from './timechart_header'; - -export function createTimechartHeaderDirective(reactDirective: any) { - return reactDirective(TimechartHeader, [ - ['from', { watchDepth: 'reference' }], - ['to', { watchDepth: 'reference' }], - ['options', { watchDepth: 'reference' }], - ['onChangeInterval', { watchDepth: 'reference' }], - ['stateInterval', { watchDepth: 'reference' }], - ['showScaledInfo', { watchDepth: 'reference' }], - ['bucketIntervalDescription', { watchDepth: 'reference' }], - ['bucketIntervalScale', { watchDepth: 'reference' }], - ]); -} diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index 12562d8571a25..fdb14b3f1f63e 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -44,6 +44,7 @@ import { createSavedSearchesLoader, SavedSearch } from './saved_searches'; import { getHistory } from './kibana_services'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; import { UrlForwardingStart } from '../../url_forwarding/public'; +import { NavigationPublicPluginStart } from '../../navigation/public'; export interface DiscoverServices { addBasePath: (path: string) => string; @@ -58,6 +59,7 @@ export interface DiscoverServices { indexPatterns: IndexPatternsContract; inspector: InspectorPublicPluginStart; metadata: { branch: string }; + navigation: NavigationPublicPluginStart; share?: SharePluginStart; kibanaLegacy: KibanaLegacyStart; urlForwarding: UrlForwardingStart; @@ -65,6 +67,7 @@ export interface DiscoverServices { toastNotifications: ToastsStart; getSavedSearchById: (id: string) => Promise; getSavedSearchUrlById: (id: string) => Promise; + getEmbeddableInjector: any; uiSettings: IUiSettingsClient; visualizations: VisualizationsStart; } @@ -72,7 +75,8 @@ export interface DiscoverServices { export async function buildServices( core: CoreStart, plugins: DiscoverStartPlugins, - context: PluginInitializerContext + context: PluginInitializerContext, + getEmbeddableInjector: any ): Promise { const services: SavedObjectKibanaServices = { savedObjectsClient: core.savedObjects.client, @@ -92,6 +96,7 @@ export async function buildServices( docLinks: core.docLinks, theme: plugins.charts.theme, filterManager: plugins.data.query.filterManager, + getEmbeddableInjector, getSavedSearchById: async (id: string) => savedObjectService.get(id), getSavedSearchUrlById: async (id: string) => savedObjectService.urlFor(id), history: getHistory, @@ -100,6 +105,7 @@ export async function buildServices( metadata: { branch: context.env.packageInfo.branch, }, + navigation: plugins.navigation, share: plugins.share, kibanaLegacy: plugins.kibanaLegacy, urlForwarding: plugins.urlForwarding, diff --git a/src/plugins/discover/public/get_inner_angular.ts b/src/plugins/discover/public/get_inner_angular.ts index 85b0752f13463..1ca0bb20e8723 100644 --- a/src/plugins/discover/public/get_inner_angular.ts +++ b/src/plugins/discover/public/get_inner_angular.ts @@ -40,16 +40,10 @@ import { createTableRowDirective } from './application/angular/doc_table/compone import { createPagerFactory } from './application/angular/doc_table/lib/pager/pager_factory'; import { createInfiniteScrollDirective } from './application/angular/doc_table/infinite_scroll'; import { createDocViewerDirective } from './application/angular/doc_viewer'; -import { CollapsibleSidebarProvider } from './application/angular/directives/collapsible_sidebar/collapsible_sidebar'; -// @ts-ignore -import { FixedScrollProvider } from './application/angular/directives/fixed_scroll'; -// @ts-ignore -import { DebounceProviderTimeout } from './application/angular/directives/debounce/debounce'; import { createRenderCompleteDirective } from './application/angular/directives/render_complete'; import { initAngularBootstrap, configureAppAngularModule, - KbnAccessibleClickProvider, PrivateProvider, PromiseServiceCreator, registerListenEventListener, @@ -57,14 +51,10 @@ import { createTopNavDirective, createTopNavHelper, } from '../../kibana_legacy/public'; -import { createDiscoverSidebarDirective } from './application/components/sidebar'; -import { createHitsCounterDirective } from '././application/components/hits_counter'; -import { createLoadingSpinnerDirective } from '././application/components/loading_spinner/loading_spinner'; -import { createTimechartHeaderDirective } from './application/components/timechart_header'; import { createContextErrorMessageDirective } from './application/components/context_error_message'; import { DiscoverStartPlugins } from './plugin'; import { getScopedHistory } from './kibana_services'; -import { createSkipBottomButtonDirective } from './application/components/skip_bottom_button'; +import { createDiscoverLegacyDirective } from './application/components/create_discover_legacy_directive'; /** * returns the main inner angular module, it contains all the parts of Angular Discover @@ -88,11 +78,9 @@ export function getInnerAngularModule( export function getInnerAngularModuleEmbeddable( name: string, core: CoreStart, - deps: DiscoverStartPlugins, - context: PluginInitializerContext + deps: DiscoverStartPlugins ) { - const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data, true); - return module; + return initializeInnerAngularModule(name, core, deps.navigation, deps.data, true); } let initialized = false; @@ -129,8 +117,7 @@ export function initializeInnerAngularModule( ]) .config(watchMultiDecorator) .directive('icon', (reactDirective) => reactDirective(EuiIcon)) - .directive('renderComplete', createRenderCompleteDirective) - .service('debounce', ['$timeout', DebounceProviderTimeout]); + .directive('renderComplete', createRenderCompleteDirective); } return angular @@ -149,18 +136,9 @@ export function initializeInnerAngularModule( ]) .config(watchMultiDecorator) .run(registerListenEventListener) - .directive('icon', (reactDirective) => reactDirective(EuiIcon)) - .directive('kbnAccessibleClick', KbnAccessibleClickProvider) - .directive('collapsibleSidebar', CollapsibleSidebarProvider) - .directive('fixedScroll', FixedScrollProvider) .directive('renderComplete', createRenderCompleteDirective) - .directive('discoverSidebar', createDiscoverSidebarDirective) - .directive('skipBottomButton', createSkipBottomButtonDirective) - .directive('hitsCounter', createHitsCounterDirective) - .directive('loadingSpinner', createLoadingSpinnerDirective) - .directive('timechartHeader', createTimechartHeaderDirective) - .directive('contextErrorMessage', createContextErrorMessageDirective) - .service('debounce', ['$timeout', DebounceProviderTimeout]); + .directive('discoverLegacy', createDiscoverLegacyDirective) + .directive('contextErrorMessage', createContextErrorMessageDirective); } function createLocalPromiseModule() { diff --git a/src/plugins/discover/public/kibana_services.ts b/src/plugins/discover/public/kibana_services.ts index bc25fa71dcf41..064030b78d10e 100644 --- a/src/plugins/discover/public/kibana_services.ts +++ b/src/plugins/discover/public/kibana_services.ts @@ -19,7 +19,7 @@ import _ from 'lodash'; import { createHashHistory } from 'history'; -import { ScopedHistory } from 'kibana/public'; +import { ScopedHistory, AppMountParameters } from 'kibana/public'; import { UiActionsStart } from 'src/plugins/ui_actions/public'; import { DiscoverServices } from './build_services'; import { createGetterSetter } from '../../kibana_utils/public'; @@ -58,6 +58,10 @@ export function setServices(newServices: any) { export const setUiActions = (pluginUiActions: UiActionsStart) => (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 b6960c8a20abf..440bd3fdf86d3 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(); }; @@ -324,7 +327,12 @@ export class DiscoverPlugin if (this.servicesInitialized) { return { core, plugins }; } - const services = await buildServices(core, plugins, this.initializerContext); + const services = await buildServices( + core, + plugins, + this.initializerContext, + this.getEmbeddableInjector + ); setServices(services); this.servicesInitialized = true; @@ -377,12 +385,7 @@ export class DiscoverPlugin const { core, plugins } = await this.initializeServices(); getServices().kibanaLegacy.loadFontAwesome(); const { getInnerAngularModuleEmbeddable } = await import('./get_inner_angular'); - getInnerAngularModuleEmbeddable( - embeddableAngularName, - core, - plugins, - this.initializerContext - ); + getInnerAngularModuleEmbeddable(embeddableAngularName, core, plugins); const mountpoint = document.createElement('div'); this.embeddableInjector = angular.bootstrap(mountpoint, [embeddableAngularName]); } diff --git a/src/plugins/discover/public/saved_searches/types.ts b/src/plugins/discover/public/saved_searches/types.ts index d601d087afcee..13361cb647ddc 100644 --- a/src/plugins/discover/public/saved_searches/types.ts +++ b/src/plugins/discover/public/saved_searches/types.ts @@ -28,6 +28,7 @@ export interface SavedSearch { columns: string[]; sort: SortOrder[]; destroy: () => void; + lastSavedTitle?: string; } export interface SavedSearchLoader { get: (id: string) => Promise; 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 570a78fc41ea9..b22f16c94aff8 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 c14471991ccd3..dbf53a9f0a359 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 = () =>