From f8358fa886f8d9356d93e3f5432ecf90eba96b18 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 3 Dec 2019 09:34:49 +0100 Subject: [PATCH] Migrate Capabilities to new platform (#51438) * create service skeleton * move registerCapabilitiesModifier to capabilities service and rename to registerCapabilitiesSwitcher * starts to move capabilities logic to CapabilitiesService * move capabilities route to service * add initial integration test for capabilities route * capabilitiesMixin now delegates to capability service * use server-side Capabilities import in server code * update generated doc * remove capabilities from injectedMetadatas * use applications sent from client instead of server-registered navLinks * disable authRequired for capabilities route * (temp) exposes two endpoints for capabilities * Add fetch-mock on capabilities call for karma tests * adapt xpack Capabilities test - first attempt * adapt x-pack ui_capabilities test * add '/status' to the list of anonymous pages * Add documentation on Capabilities APIs * move Capabilities to core/types * update generated docs * add service tests * protecting resolveCapabilities against added/removed capabilities * update generated docs * adapt mocks due to rebase * add forgotten exports * improve capabilities routes registering * name capabilities registering methods * resolve conflicts due to merge * address review issues * add comment about reason for exposing two routes * extract createHttpServer test helper * fix merge conflicts * improve documentation * remove `/status` anon registration as now done in NP status plugin * fix merge conflicts --- .eslintrc.js | 1 + ...na-plugin-server.capabilities.catalogue.md | 13 + ...a-plugin-server.capabilities.management.md | 15 + .../kibana-plugin-server.capabilities.md | 22 + ...ana-plugin-server.capabilities.navlinks.md | 13 + ...bana-plugin-server.capabilitiesprovider.md | 13 + .../kibana-plugin-server.capabilitiessetup.md | 27 + ...rver.capabilitiessetup.registerprovider.md | 46 + ...rver.capabilitiessetup.registerswitcher.md | 47 + .../kibana-plugin-server.capabilitiesstart.md | 20 + ...r.capabilitiesstart.resolvecapabilities.md | 24 + ...bana-plugin-server.capabilitiesswitcher.md | 13 + ...na-plugin-server.coresetup.capabilities.md | 13 + .../server/kibana-plugin-server.coresetup.md | 1 + ...na-plugin-server.corestart.capabilities.md | 13 + .../server/kibana-plugin-server.corestart.md | 1 + .../core/server/kibana-plugin-server.md | 5 + .../application/application_service.test.tsx | 4 +- .../application/application_service.tsx | 2 +- .../capabilities/capabilities_service.test.ts | 50 +- .../capabilities/capabilities_service.tsx | 51 +- src/core/public/http/http_service.mock.ts | 4 +- .../injected_metadata_service.mock.ts | 2 - .../injected_metadata_service.ts | 7 - .../capabilities/capabilities_service.mock.ts | 50 + .../capabilities/capabilities_service.test.ts | 188 + .../capabilities/capabilities_service.ts | 156 + src/core/server/capabilities/index.ts | 21 + .../capabilities_service.test.ts | 95 + .../capabilities/merge_capabilities.test.ts | 76 + .../server/capabilities/merge_capabilities.ts | 31 +- .../capabilities/resolve_capabilities.test.ts | 164 + .../capabilities/resolve_capabilities.ts | 85 + src/core/server/capabilities/routes/index.ts | 27 + .../routes/resolve_capabilities.ts | 51 + .../capabilities/types.ts} | 35 +- src/core/server/http/http_service.mock.ts | 23 +- .../http/integration_tests/lifecycle.test.ts | 27 +- .../http/integration_tests/router.test.ts | 27 +- .../server/http/router/router.mock.ts} | 31 +- src/core/server/http/test_utils.ts | 63 + src/core/server/index.ts | 15 +- src/core/server/internal_types.ts | 3 + src/core/server/legacy/legacy_service.test.ts | 3 + src/core/server/legacy/legacy_service.ts | 2 + src/core/server/mocks.ts | 5 + src/core/server/plugins/plugin_context.ts | 7 + src/core/server/server.api.md | 3493 +++++++++-------- src/core/server/server.ts | 14 +- .../server/test_utils.ts} | 5 +- src/core/types/capabilities.ts | 41 + src/core/types/index.ts | 1 + .../tests_bundle/tests_entry_template.js | 4 +- .../plugin_spec/plugin_spec_options.d.ts | 2 +- src/legacy/plugin_discovery/types.ts | 2 +- .../capabilities/capabilities_mixin.test.ts | 84 +- .../server/capabilities/capabilities_mixin.ts | 51 +- .../capabilities/capabilities_route.test.ts | 137 - .../server/capabilities/capabilities_route.ts | 54 - src/legacy/server/capabilities/index.ts | 2 +- .../capabilities/merge_capabilities.test.ts | 84 - src/legacy/server/kbn_server.d.ts | 9 +- src/legacy/ui/ui_render/ui_render_mixin.js | 2 - x-pack/legacy/plugins/security/index.js | 1 - x-pack/legacy/plugins/spaces/index.ts | 3 - .../plugins/features/server/feature_schema.ts | 2 +- x-pack/plugins/features/server/plugin.ts | 2 +- .../server/ui_capabilities_for_features.ts | 2 +- .../server/authorization/actions/ui.ts | 2 +- x-pack/plugins/security/server/plugin.ts | 8 +- x-pack/plugins/spaces/server/plugin.ts | 24 +- .../api/__fixtures__/create_legacy_api.ts | 1 - .../common/services/ui_capabilities.ts | 56 +- .../security_and_spaces/tests/catalogue.ts | 25 +- .../security_and_spaces/tests/foo.ts | 25 +- .../security_and_spaces/tests/nav_links.ts | 15 +- .../security_only/tests/catalogue.ts | 12 +- .../security_only/tests/foo.ts | 15 +- .../security_only/tests/nav_links.ts | 10 +- 79 files changed, 3357 insertions(+), 2423 deletions(-) create mode 100644 docs/development/core/server/kibana-plugin-server.capabilities.catalogue.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilities.management.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilities.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilities.navlinks.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilitiesprovider.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilitiessetup.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilitiessetup.registerprovider.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilitiessetup.registerswitcher.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilitiesstart.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilitiesstart.resolvecapabilities.md create mode 100644 docs/development/core/server/kibana-plugin-server.capabilitiesswitcher.md create mode 100644 docs/development/core/server/kibana-plugin-server.coresetup.capabilities.md create mode 100644 docs/development/core/server/kibana-plugin-server.corestart.capabilities.md create mode 100644 src/core/server/capabilities/capabilities_service.mock.ts create mode 100644 src/core/server/capabilities/capabilities_service.test.ts create mode 100644 src/core/server/capabilities/capabilities_service.ts create mode 100644 src/core/server/capabilities/index.ts create mode 100644 src/core/server/capabilities/integration_tests/capabilities_service.test.ts create mode 100644 src/core/server/capabilities/merge_capabilities.test.ts rename src/{legacy => core}/server/capabilities/merge_capabilities.ts (60%) create mode 100644 src/core/server/capabilities/resolve_capabilities.test.ts create mode 100644 src/core/server/capabilities/resolve_capabilities.ts create mode 100644 src/core/server/capabilities/routes/index.ts create mode 100644 src/core/server/capabilities/routes/resolve_capabilities.ts rename src/core/{public/application/capabilities/merge_capabilities.ts => server/capabilities/types.ts} (63%) rename src/{legacy/server/capabilities/resolve_capabilities.ts => core/server/http/router/router.mock.ts} (61%) create mode 100644 src/core/server/http/test_utils.ts rename src/{legacy/server/capabilities/capabilities_mixin.test.mocks.ts => core/server/test_utils.ts} (82%) create mode 100644 src/core/types/capabilities.ts delete mode 100644 src/legacy/server/capabilities/capabilities_route.test.ts delete mode 100644 src/legacy/server/capabilities/capabilities_route.ts delete mode 100644 src/legacy/server/capabilities/merge_capabilities.test.ts diff --git a/.eslintrc.js b/.eslintrc.js index 58b74d0bf5c25..0db97356148f4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -337,6 +337,7 @@ module.exports = { '!src/core/server/index.ts', '!src/core/server/mocks.ts', '!src/core/server/types.ts', + '!src/core/server/test_utils.ts', // for absolute imports until fixed in // https://github.com/elastic/kibana/issues/36096 '!src/core/server/types', diff --git a/docs/development/core/server/kibana-plugin-server.capabilities.catalogue.md b/docs/development/core/server/kibana-plugin-server.capabilities.catalogue.md new file mode 100644 index 0000000000000..4eb012c78f0cb --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilities.catalogue.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Capabilities](./kibana-plugin-server.capabilities.md) > [catalogue](./kibana-plugin-server.capabilities.catalogue.md) + +## Capabilities.catalogue property + +Catalogue capabilities. Catalogue entries drive the visibility of the Kibana homepage options. + +Signature: + +```typescript +catalogue: Record; +``` diff --git a/docs/development/core/server/kibana-plugin-server.capabilities.management.md b/docs/development/core/server/kibana-plugin-server.capabilities.management.md new file mode 100644 index 0000000000000..d917c81dc3720 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilities.management.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Capabilities](./kibana-plugin-server.capabilities.md) > [management](./kibana-plugin-server.capabilities.management.md) + +## Capabilities.management property + +Management section capabilities. + +Signature: + +```typescript +management: { + [sectionId: string]: Record; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-server.capabilities.md b/docs/development/core/server/kibana-plugin-server.capabilities.md new file mode 100644 index 0000000000000..031282b9733ac --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilities.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Capabilities](./kibana-plugin-server.capabilities.md) + +## Capabilities interface + +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. + +Signature: + +```typescript +export interface Capabilities +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [catalogue](./kibana-plugin-server.capabilities.catalogue.md) | Record<string, boolean> | Catalogue capabilities. Catalogue entries drive the visibility of the Kibana homepage options. | +| [management](./kibana-plugin-server.capabilities.management.md) | {
[sectionId: string]: Record<string, boolean>;
} | Management section capabilities. | +| [navLinks](./kibana-plugin-server.capabilities.navlinks.md) | Record<string, boolean> | Navigation link capabilities. | + diff --git a/docs/development/core/server/kibana-plugin-server.capabilities.navlinks.md b/docs/development/core/server/kibana-plugin-server.capabilities.navlinks.md new file mode 100644 index 0000000000000..a1612ea840fbf --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilities.navlinks.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Capabilities](./kibana-plugin-server.capabilities.md) > [navLinks](./kibana-plugin-server.capabilities.navlinks.md) + +## Capabilities.navLinks property + +Navigation link capabilities. + +Signature: + +```typescript +navLinks: Record; +``` diff --git a/docs/development/core/server/kibana-plugin-server.capabilitiesprovider.md b/docs/development/core/server/kibana-plugin-server.capabilitiesprovider.md new file mode 100644 index 0000000000000..66e5d256ada66 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilitiesprovider.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CapabilitiesProvider](./kibana-plugin-server.capabilitiesprovider.md) + +## CapabilitiesProvider type + +See [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) + +Signature: + +```typescript +export declare type CapabilitiesProvider = () => Partial; +``` diff --git a/docs/development/core/server/kibana-plugin-server.capabilitiessetup.md b/docs/development/core/server/kibana-plugin-server.capabilitiessetup.md new file mode 100644 index 0000000000000..27c42fe75e751 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilitiessetup.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) + +## CapabilitiesSetup interface + +APIs to manage the [Capabilities](./kibana-plugin-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. + +Signature: + +```typescript +export interface CapabilitiesSetup +``` + +## Methods + +| Method | Description | +| --- | --- | +| [registerProvider(provider)](./kibana-plugin-server.capabilitiessetup.registerprovider.md) | Register a [CapabilitiesProvider](./kibana-plugin-server.capabilitiesprovider.md) to be used to provide [Capabilities](./kibana-plugin-server.capabilities.md) when resolving them. | +| [registerSwitcher(switcher)](./kibana-plugin-server.capabilitiessetup.registerswitcher.md) | Register a [CapabilitiesSwitcher](./kibana-plugin-server.capabilitiesswitcher.md) to be used to change the default state of the [Capabilities](./kibana-plugin-server.capabilities.md) entries when resolving them.A capabilities switcher can only change the state of existing capabilities. Capabilities added or removed when invoking the switcher will be ignored. | + diff --git a/docs/development/core/server/kibana-plugin-server.capabilitiessetup.registerprovider.md b/docs/development/core/server/kibana-plugin-server.capabilitiessetup.registerprovider.md new file mode 100644 index 0000000000000..750913ee35895 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilitiessetup.registerprovider.md @@ -0,0 +1,46 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) > [registerProvider](./kibana-plugin-server.capabilitiessetup.registerprovider.md) + +## CapabilitiesSetup.registerProvider() method + +Register a [CapabilitiesProvider](./kibana-plugin-server.capabilitiesprovider.md) to be used to provide [Capabilities](./kibana-plugin-server.capabilities.md) when resolving them. + +Signature: + +```typescript +registerProvider(provider: CapabilitiesProvider): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| provider | CapabilitiesProvider | | + +Returns: + +`void` + +## Example + +How to register a plugin's capabilities during setup + +```ts +// my-plugin/server/plugin.ts +public setup(core: CoreSetup, deps: {}) { + core.capabilities.registerProvider(() => { + return { + catalogue: { + myPlugin: true, + }, + myPlugin: { + someFeature: true, + featureDisabledByDefault: false, + }, + } + }); +} + +``` + diff --git a/docs/development/core/server/kibana-plugin-server.capabilitiessetup.registerswitcher.md b/docs/development/core/server/kibana-plugin-server.capabilitiessetup.registerswitcher.md new file mode 100644 index 0000000000000..fbaa2959c635c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilitiessetup.registerswitcher.md @@ -0,0 +1,47 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) > [registerSwitcher](./kibana-plugin-server.capabilitiessetup.registerswitcher.md) + +## CapabilitiesSetup.registerSwitcher() method + +Register a [CapabilitiesSwitcher](./kibana-plugin-server.capabilitiesswitcher.md) to be used to change the default state of the [Capabilities](./kibana-plugin-server.capabilities.md) entries when resolving them. + +A capabilities switcher can only change the state of existing capabilities. Capabilities added or removed when invoking the switcher will be ignored. + +Signature: + +```typescript +registerSwitcher(switcher: CapabilitiesSwitcher): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| switcher | CapabilitiesSwitcher | | + +Returns: + +`void` + +## Example + +How to restrict some capabilities + +```ts +// my-plugin/server/plugin.ts +public setup(core: CoreSetup, deps: {}) { + core.capabilities.registerSwitcher((request, capabilities) => { + if(myPluginApi.shouldRestrictSomePluginBecauseOf(request)) { + return { + somePlugin: { + featureEnabledByDefault: false // `featureEnabledByDefault` will be disabled. All other capabilities will remain unchanged. + } + } + } + return {}; // All capabilities will remain unchanged. + }); +} + +``` + diff --git a/docs/development/core/server/kibana-plugin-server.capabilitiesstart.md b/docs/development/core/server/kibana-plugin-server.capabilitiesstart.md new file mode 100644 index 0000000000000..55cc1aed76b5b --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilitiesstart.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CapabilitiesStart](./kibana-plugin-server.capabilitiesstart.md) + +## CapabilitiesStart interface + +APIs to access the application [Capabilities](./kibana-plugin-server.capabilities.md). + +Signature: + +```typescript +export interface CapabilitiesStart +``` + +## Methods + +| Method | Description | +| --- | --- | +| [resolveCapabilities(request)](./kibana-plugin-server.capabilitiesstart.resolvecapabilities.md) | Resolve the [Capabilities](./kibana-plugin-server.capabilities.md) to be used for given request | + diff --git a/docs/development/core/server/kibana-plugin-server.capabilitiesstart.resolvecapabilities.md b/docs/development/core/server/kibana-plugin-server.capabilitiesstart.resolvecapabilities.md new file mode 100644 index 0000000000000..95b751dd4fc95 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilitiesstart.resolvecapabilities.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CapabilitiesStart](./kibana-plugin-server.capabilitiesstart.md) > [resolveCapabilities](./kibana-plugin-server.capabilitiesstart.resolvecapabilities.md) + +## CapabilitiesStart.resolveCapabilities() method + +Resolve the [Capabilities](./kibana-plugin-server.capabilities.md) to be used for given request + +Signature: + +```typescript +resolveCapabilities(request: KibanaRequest): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| request | KibanaRequest | | + +Returns: + +`Promise` + diff --git a/docs/development/core/server/kibana-plugin-server.capabilitiesswitcher.md b/docs/development/core/server/kibana-plugin-server.capabilitiesswitcher.md new file mode 100644 index 0000000000000..dd6af54376896 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.capabilitiesswitcher.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CapabilitiesSwitcher](./kibana-plugin-server.capabilitiesswitcher.md) + +## CapabilitiesSwitcher type + +See [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) + +Signature: + +```typescript +export declare type CapabilitiesSwitcher = (request: KibanaRequest, uiCapabilities: Capabilities) => Partial | Promise>; +``` diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.capabilities.md b/docs/development/core/server/kibana-plugin-server.coresetup.capabilities.md new file mode 100644 index 0000000000000..fe50347d97e3c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.coresetup.capabilities.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreSetup](./kibana-plugin-server.coresetup.md) > [capabilities](./kibana-plugin-server.coresetup.capabilities.md) + +## CoreSetup.capabilities property + +[CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) + +Signature: + +```typescript +capabilities: CapabilitiesSetup; +``` diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.md b/docs/development/core/server/kibana-plugin-server.coresetup.md index 1ad1641beb83b..3886b6e05e657 100644 --- a/docs/development/core/server/kibana-plugin-server.coresetup.md +++ b/docs/development/core/server/kibana-plugin-server.coresetup.md @@ -16,6 +16,7 @@ export interface CoreSetup | Property | Type | Description | | --- | --- | --- | +| [capabilities](./kibana-plugin-server.coresetup.capabilities.md) | CapabilitiesSetup | [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) | | [context](./kibana-plugin-server.coresetup.context.md) | ContextSetup | [ContextSetup](./kibana-plugin-server.contextsetup.md) | | [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | ElasticsearchServiceSetup | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) | | [http](./kibana-plugin-server.coresetup.http.md) | HttpServiceSetup | [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) | diff --git a/docs/development/core/server/kibana-plugin-server.corestart.capabilities.md b/docs/development/core/server/kibana-plugin-server.corestart.capabilities.md new file mode 100644 index 0000000000000..03930d367ee75 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.corestart.capabilities.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CoreStart](./kibana-plugin-server.corestart.md) > [capabilities](./kibana-plugin-server.corestart.capabilities.md) + +## CoreStart.capabilities property + +[CapabilitiesStart](./kibana-plugin-server.capabilitiesstart.md) + +Signature: + +```typescript +capabilities: CapabilitiesStart; +``` diff --git a/docs/development/core/server/kibana-plugin-server.corestart.md b/docs/development/core/server/kibana-plugin-server.corestart.md index a675c45a29820..e523717a37ac8 100644 --- a/docs/development/core/server/kibana-plugin-server.corestart.md +++ b/docs/development/core/server/kibana-plugin-server.corestart.md @@ -16,5 +16,6 @@ export interface CoreStart | Property | Type | Description | | --- | --- | --- | +| [capabilities](./kibana-plugin-server.corestart.capabilities.md) | CapabilitiesStart | [CapabilitiesStart](./kibana-plugin-server.capabilitiesstart.md) | | [savedObjects](./kibana-plugin-server.corestart.savedobjects.md) | SavedObjectsServiceStart | [SavedObjectsServiceStart](./kibana-plugin-server.savedobjectsservicestart.md) | diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md index 17c5136fdc318..9144742c9bb73 100644 --- a/docs/development/core/server/kibana-plugin-server.md +++ b/docs/development/core/server/kibana-plugin-server.md @@ -43,6 +43,9 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AuthResultParams](./kibana-plugin-server.authresultparams.md) | Result of an incoming request authentication. | | [AuthToolkit](./kibana-plugin-server.authtoolkit.md) | A tool set defining an outcome of Auth interceptor for incoming request. | | [CallAPIOptions](./kibana-plugin-server.callapioptions.md) | The set of options that defines how API call should be made and result be processed. | +| [Capabilities](./kibana-plugin-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-server.capabilitiessetup.md) | APIs to manage the [Capabilities](./kibana-plugin-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-server.capabilitiesstart.md) | APIs to access the application [Capabilities](./kibana-plugin-server.capabilities.md). | | [ContextSetup](./kibana-plugin-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. | | [CoreSetup](./kibana-plugin-server.coresetup.md) | Context passed to the plugins setup method. | | [CoreStart](./kibana-plugin-server.corestart.md) | Context passed to the plugins start method. | @@ -144,6 +147,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AuthenticationHandler](./kibana-plugin-server.authenticationhandler.md) | See [AuthToolkit](./kibana-plugin-server.authtoolkit.md). | | [AuthHeaders](./kibana-plugin-server.authheaders.md) | Auth Headers map | | [AuthResult](./kibana-plugin-server.authresult.md) | | +| [CapabilitiesProvider](./kibana-plugin-server.capabilitiesprovider.md) | See [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) | +| [CapabilitiesSwitcher](./kibana-plugin-server.capabilitiesswitcher.md) | See [CapabilitiesSetup](./kibana-plugin-server.capabilitiessetup.md) | | [ConfigPath](./kibana-plugin-server.configpath.md) | | | [ElasticsearchClientConfig](./kibana-plugin-server.elasticsearchclientconfig.md) | | | [GetAuthHeaders](./kibana-plugin-server.getauthheaders.md) | Get headers to authenticate a user against Elasticsearch. | diff --git a/src/core/public/application/application_service.test.tsx b/src/core/public/application/application_service.test.tsx index 5b374218a5932..19a208aeefb37 100644 --- a/src/core/public/application/application_service.test.tsx +++ b/src/core/public/application/application_service.test.tsx @@ -136,7 +136,7 @@ describe('#start()', () => { expect(MockCapabilitiesService.start).toHaveBeenCalledWith({ apps: new Map([['app1', { id: 'app1' }]]), legacyApps: new Map(), - injectedMetadata, + http, }); }); @@ -153,7 +153,7 @@ describe('#start()', () => { expect(MockCapabilitiesService.start).toHaveBeenCalledWith({ apps: new Map(), legacyApps: new Map([['legacyApp1', { id: 'legacyApp1' }]]), - injectedMetadata, + http, }); }); diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index 935844baddf86..45ca7f3fe7f7b 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -111,9 +111,9 @@ export class ApplicationService { const legacyMode = injectedMetadata.getLegacyMode(); const currentAppId$ = new BehaviorSubject(undefined); const { availableApps, availableLegacyApps, capabilities } = await this.capabilities.start({ + http, apps: new Map([...this.apps$.value].map(([id, { app }]) => [id, app])), legacyApps: this.legacyApps$.value, - injectedMetadata, }); // Only setup history if we're not in legacy mode diff --git a/src/core/public/application/capabilities/capabilities_service.test.ts b/src/core/public/application/capabilities/capabilities_service.test.ts index e80e9a7af321a..3245be8dd502d 100644 --- a/src/core/public/application/capabilities/capabilities_service.test.ts +++ b/src/core/public/application/capabilities/capabilities_service.test.ts @@ -17,32 +17,35 @@ * under the License. */ -import { InjectedMetadataService } from '../../injected_metadata'; +import { httpServiceMock, HttpSetupMock } from '../../http/http_service.mock'; import { CapabilitiesService } from './capabilities_service'; import { LegacyApp, App } from '../types'; +const mockedCapabilities = { + catalogue: {}, + management: {}, + navLinks: { + app1: true, + app2: false, + legacyApp1: true, + legacyApp2: false, + }, + foo: { feature: true }, + bar: { feature: true }, +}; + describe('#start', () => { - const injectedMetadata = new InjectedMetadataService({ - injectedMetadata: { - version: 'kibanaVersion', - capabilities: { - catalogue: {}, - management: {}, - navLinks: { - app1: true, - app2: false, - legacyApp1: true, - legacyApp2: false, - }, - foo: { feature: true }, - bar: { feature: true }, - }, - } as any, - }).start(); + let http: HttpSetupMock; + + beforeEach(() => { + http = httpServiceMock.createStartContract(); + http.post.mockReturnValue(Promise.resolve(mockedCapabilities)); + }); const apps = new Map([ ['app1', { id: 'app1' }], ['app2', { id: 'app2', capabilities: { app2: { feature: true } } }], + ['appMissingInCapabilities', { id: 'appMissingInCapabilities' }], ] as Array<[string, App]>); const legacyApps = new Map([ ['legacyApp1', { id: 'legacyApp1' }], @@ -51,8 +54,13 @@ describe('#start', () => { it('filters available apps based on returned navLinks', async () => { const service = new CapabilitiesService(); - const startContract = await service.start({ apps, legacyApps, injectedMetadata }); - expect(startContract.availableApps).toEqual(new Map([['app1', { id: 'app1' }]])); + const startContract = await service.start({ apps, legacyApps, http }); + expect(startContract.availableApps).toEqual( + new Map([ + ['app1', { id: 'app1' }], + ['appMissingInCapabilities', { id: 'appMissingInCapabilities' }], + ]) + ); expect(startContract.availableLegacyApps).toEqual( new Map([['legacyApp1', { id: 'legacyApp1' }]]) ); @@ -63,7 +71,7 @@ describe('#start', () => { const { capabilities } = await service.start({ apps, legacyApps, - injectedMetadata, + http, }); // @ts-ignore TypeScript knows this shouldn't be possible diff --git a/src/core/public/application/capabilities/capabilities_service.tsx b/src/core/public/application/capabilities/capabilities_service.tsx index b080f8c138cf2..e330a4b0326ae 100644 --- a/src/core/public/application/capabilities/capabilities_service.tsx +++ b/src/core/public/application/capabilities/capabilities_service.tsx @@ -17,38 +17,18 @@ * under the License. */ +import { Capabilities } from '../../../types/capabilities'; import { deepFreeze, RecursiveReadonly } from '../../../utils'; import { LegacyApp, App } from '../types'; -import { InjectedMetadataStart } from '../../injected_metadata'; +import { HttpStart } from '../../http'; interface StartDeps { apps: ReadonlyMap; legacyApps: ReadonlyMap; - injectedMetadata: InjectedMetadataStart; + http: HttpStart; } -/** - * 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. - * - * @public - */ -export interface Capabilities { - /** Navigation link capabilities. */ - navLinks: Record; - - /** Management section capabilities. */ - management: { - [sectionId: string]: Record; - }; - - /** Catalogue capabilities. Catalogue entries drive the visibility of the Kibana homepage options. */ - catalogue: Record; - - /** Custom capabilities, registered by plugins. */ - [key: string]: Record>; -} +export { Capabilities }; /** @internal */ export interface CapabilitiesStart { @@ -62,12 +42,9 @@ export interface CapabilitiesStart { * @internal */ export class CapabilitiesService { - public async start({ - apps, - legacyApps, - injectedMetadata, - }: StartDeps): Promise { - const capabilities = deepFreeze(injectedMetadata.getCapabilities()); + public async start({ apps, legacyApps, http }: StartDeps): Promise { + const capabilities = await this.fetchCapabilities(http, [...apps.keys(), ...legacyApps.keys()]); + const availableApps = new Map( [...apps].filter( ([appId]) => @@ -88,4 +65,18 @@ export class CapabilitiesService { capabilities, }; } + + private async fetchCapabilities(http: HttpStart, appIds: string[]): Promise { + const payload = JSON.stringify({ + applications: appIds, + }); + + const url = http.anonymousPaths.isAnonymous(window.location.pathname) + ? '/api/core/capabilities/defaults' + : '/api/core/capabilities'; + const capabilities = await http.post(url, { + body: payload, + }); + return deepFreeze(capabilities); + } } diff --git a/src/core/public/http/http_service.mock.ts b/src/core/public/http/http_service.mock.ts index 52f188c7b20a0..fe7c749091b03 100644 --- a/src/core/public/http/http_service.mock.ts +++ b/src/core/public/http/http_service.mock.ts @@ -23,11 +23,11 @@ import { BehaviorSubject } from 'rxjs'; import { BasePath } from './base_path_service'; import { AnonymousPaths } from './anonymous_paths'; -type ServiceSetupMockType = jest.Mocked & { +export type HttpSetupMock = jest.Mocked & { basePath: BasePath; }; -const createServiceMock = ({ basePath = '' } = {}): ServiceSetupMockType => ({ +const createServiceMock = ({ basePath = '' } = {}): HttpSetupMock => ({ fetch: jest.fn(), get: jest.fn(), head: jest.fn(), 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 9e1d5aeec7ff4..9dfe019116669 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.mock.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.mock.ts @@ -23,7 +23,6 @@ const createSetupContractMock = () => { getBasePath: jest.fn(), getKibanaVersion: jest.fn(), getKibanaBranch: jest.fn(), - getCapabilities: jest.fn(), getCspConfig: jest.fn(), getLegacyMode: jest.fn(), getLegacyMetadata: jest.fn(), @@ -32,7 +31,6 @@ const createSetupContractMock = () => { getInjectedVars: jest.fn(), getKibanaBuildNumber: jest.fn(), }; - setupContract.getCapabilities.mockReturnValue({} as any); setupContract.getCspConfig.mockReturnValue({ warnLegacyBrowsers: true }); setupContract.getKibanaVersion.mockReturnValue('kibanaVersion'); setupContract.getLegacyMode.mockReturnValue(true); diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts index 002f83d9feac4..0bde1b68e1876 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.ts @@ -26,7 +26,6 @@ import { UserProvidedValues, } from '../../server/types'; import { deepFreeze } from '../../utils/'; -import { Capabilities } from '..'; /** @public */ export interface LegacyNavLink { @@ -64,7 +63,6 @@ export interface InjectedMetadataParams { packageInfo: Readonly; }; uiPlugins: InjectedPluginMetadata[]; - capabilities: Capabilities; legacyMode: boolean; legacyMetadata: { app: unknown; @@ -114,10 +112,6 @@ export class InjectedMetadataService { return this.state.version; }, - getCapabilities: () => { - return this.state.capabilities; - }, - getCspConfig: () => { return this.state.csp; }, @@ -163,7 +157,6 @@ export interface InjectedMetadataSetup { getKibanaBuildNumber: () => number; getKibanaBranch: () => string; getKibanaVersion: () => string; - getCapabilities: () => Capabilities; getCspConfig: () => { warnLegacyBrowsers: boolean; }; diff --git a/src/core/server/capabilities/capabilities_service.mock.ts b/src/core/server/capabilities/capabilities_service.mock.ts new file mode 100644 index 0000000000000..3f31eca8339d8 --- /dev/null +++ b/src/core/server/capabilities/capabilities_service.mock.ts @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CapabilitiesService, CapabilitiesSetup, CapabilitiesStart } from './capabilities_service'; + +const createSetupContractMock = () => { + const setupContract: jest.Mocked = { + registerProvider: jest.fn(), + registerSwitcher: jest.fn(), + }; + return setupContract; +}; + +const createStartContractMock = () => { + const setupContract: jest.Mocked = { + resolveCapabilities: jest.fn().mockReturnValue(Promise.resolve({})), + }; + return setupContract; +}; + +type CapabilitiesServiceContract = PublicMethodsOf; +const createMock = () => { + const mocked: jest.Mocked = { + setup: jest.fn().mockReturnValue(createSetupContractMock()), + start: jest.fn().mockReturnValue(createStartContractMock()), + }; + return mocked; +}; + +export const capabilitiesServiceMock = { + create: createMock, + createSetupContract: createSetupContractMock, + createStartContract: createStartContractMock, +}; diff --git a/src/core/server/capabilities/capabilities_service.test.ts b/src/core/server/capabilities/capabilities_service.test.ts new file mode 100644 index 0000000000000..aace0b9debf9c --- /dev/null +++ b/src/core/server/capabilities/capabilities_service.test.ts @@ -0,0 +1,188 @@ +/* + * 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 { httpServiceMock, HttpServiceSetupMock } from '../http/http_service.mock'; +import { mockRouter, RouterMock } from '../http/router/router.mock'; +import { CapabilitiesService, CapabilitiesSetup } from './capabilities_service'; +import { mockCoreContext } from '../core_context.mock'; + +describe('CapabilitiesService', () => { + let http: HttpServiceSetupMock; + let service: CapabilitiesService; + let setup: CapabilitiesSetup; + let router: RouterMock; + + beforeEach(() => { + http = httpServiceMock.createSetupContract(); + router = mockRouter.create(); + http.createRouter.mockReturnValue(router); + service = new CapabilitiesService(mockCoreContext.create()); + }); + + describe('#setup()', () => { + beforeEach(() => { + setup = service.setup({ http }); + }); + + it('registers the capabilities routes', async () => { + expect(http.createRouter).toHaveBeenCalledWith('/api/core/capabilities'); + expect(router.post).toHaveBeenCalledTimes(2); + expect(router.post).toHaveBeenCalledWith(expect.any(Object), expect.any(Function)); + }); + + it('allows to register a capabilities provider', async () => { + setup.registerProvider(() => ({ + navLinks: { myLink: true }, + catalogue: { myPlugin: true }, + })); + const start = service.start(); + expect(await start.resolveCapabilities({} as any)).toMatchInlineSnapshot(` + Object { + "catalogue": Object { + "myPlugin": true, + }, + "management": Object {}, + "navLinks": Object { + "myLink": true, + }, + } + `); + }); + + it('allows to register multiple capabilities providers', async () => { + setup.registerProvider(() => ({ + navLinks: { A: true }, + catalogue: { A: true }, + })); + setup.registerProvider(() => ({ + navLinks: { B: true }, + catalogue: { B: true }, + })); + setup.registerProvider(() => ({ + navLinks: { C: true }, + customSection: { + C: true, + }, + })); + const start = service.start(); + expect(await start.resolveCapabilities({} as any)).toMatchInlineSnapshot(` + Object { + "catalogue": Object { + "A": true, + "B": true, + }, + "customSection": Object { + "C": true, + }, + "management": Object {}, + "navLinks": Object { + "A": true, + "B": true, + "C": true, + }, + } + `); + }); + + it('allows to register capabilities switchers', async () => { + setup.registerProvider(() => ({ + catalogue: { a: true, b: true, c: true }, + })); + setup.registerSwitcher((req, capabilities) => { + return { + ...capabilities, + catalogue: { + ...capabilities.catalogue, + b: false, + }, + }; + }); + const start = service.start(); + expect(await start.resolveCapabilities({} as any)).toMatchInlineSnapshot(` + Object { + "catalogue": Object { + "a": true, + "b": false, + "c": true, + }, + "management": Object {}, + "navLinks": Object {}, + } + `); + }); + + it('allows to register multiple providers and switchers', async () => { + setup.registerProvider(() => ({ + navLinks: { a: true }, + catalogue: { a: true }, + })); + setup.registerProvider(() => ({ + navLinks: { b: true }, + catalogue: { b: true }, + })); + setup.registerProvider(() => ({ + navLinks: { c: true }, + catalogue: { c: true }, + customSection: { + c: true, + }, + })); + setup.registerSwitcher((req, capabilities) => { + return { + catalogue: { + b: false, + }, + }; + }); + + setup.registerSwitcher((req, capabilities) => { + return { + navLinks: { c: false }, + }; + }); + setup.registerSwitcher((req, capabilities) => { + return { + customSection: { + c: false, + }, + }; + }); + + const start = service.start(); + expect(await start.resolveCapabilities({} as any)).toMatchInlineSnapshot(` + Object { + "catalogue": Object { + "a": true, + "b": false, + "c": true, + }, + "customSection": Object { + "c": false, + }, + "management": Object {}, + "navLinks": Object { + "a": true, + "b": true, + "c": false, + }, + } + `); + }); + }); +}); diff --git a/src/core/server/capabilities/capabilities_service.ts b/src/core/server/capabilities/capabilities_service.ts new file mode 100644 index 0000000000000..d3d6b507da523 --- /dev/null +++ b/src/core/server/capabilities/capabilities_service.ts @@ -0,0 +1,156 @@ +/* + * 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 { Capabilities, CapabilitiesProvider, CapabilitiesSwitcher } from './types'; +import { CoreContext } from '../core_context'; +import { Logger } from '../logging'; +import { InternalHttpServiceSetup, KibanaRequest } from '../http'; +import { mergeCapabilities } from './merge_capabilities'; +import { getCapabilitiesResolver, CapabilitiesResolver } from './resolve_capabilities'; +import { registerRoutes } from './routes'; + +/** + * APIs to manage the {@link Capabilities} 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. + * + * @public + */ +export interface CapabilitiesSetup { + /** + * Register a {@link CapabilitiesProvider} to be used to provide {@link Capabilities} + * when resolving them. + * + * @example + * How to register a plugin's capabilities during setup + * ```ts + * // my-plugin/server/plugin.ts + * public setup(core: CoreSetup, deps: {}) { + * core.capabilities.registerProvider(() => { + * return { + * catalogue: { + * myPlugin: true, + * }, + * myPlugin: { + * someFeature: true, + * featureDisabledByDefault: false, + * }, + * } + * }); + * } + * ``` + */ + registerProvider(provider: CapabilitiesProvider): void; + + /** + * Register a {@link CapabilitiesSwitcher} to be used to change the default state + * of the {@link Capabilities} entries when resolving them. + * + * A capabilities switcher can only change the state of existing capabilities. + * Capabilities added or removed when invoking the switcher will be ignored. + * + * @example + * How to restrict some capabilities + * ```ts + * // my-plugin/server/plugin.ts + * public setup(core: CoreSetup, deps: {}) { + * core.capabilities.registerSwitcher((request, capabilities) => { + * if(myPluginApi.shouldRestrictSomePluginBecauseOf(request)) { + * return { + * somePlugin: { + * featureEnabledByDefault: false // `featureEnabledByDefault` will be disabled. All other capabilities will remain unchanged. + * } + * } + * } + * return {}; // All capabilities will remain unchanged. + * }); + * } + * ``` + */ + registerSwitcher(switcher: CapabilitiesSwitcher): void; +} + +/** + * APIs to access the application {@link Capabilities}. + * + * @public + */ +export interface CapabilitiesStart { + /** + * Resolve the {@link Capabilities} to be used for given request + */ + resolveCapabilities(request: KibanaRequest): Promise; +} + +interface SetupDeps { + http: InternalHttpServiceSetup; +} + +const defaultCapabilities: Capabilities = { + navLinks: {}, + management: {}, + catalogue: {}, +}; + +/** @internal */ +export class CapabilitiesService { + private readonly logger: Logger; + private readonly capabilitiesProviders: CapabilitiesProvider[] = []; + private readonly capabilitiesSwitchers: CapabilitiesSwitcher[] = []; + private readonly resolveCapabilities: CapabilitiesResolver; + + constructor(core: CoreContext) { + this.logger = core.logger.get('capabilities-service'); + this.resolveCapabilities = getCapabilitiesResolver( + () => + mergeCapabilities( + defaultCapabilities, + ...this.capabilitiesProviders.map(provider => provider()) + ), + () => this.capabilitiesSwitchers + ); + } + + public setup(setupDeps: SetupDeps): CapabilitiesSetup { + this.logger.debug('Setting up capabilities service'); + + registerRoutes(setupDeps.http, this.resolveCapabilities); + + return { + registerProvider: (provider: CapabilitiesProvider) => { + this.capabilitiesProviders.push(provider); + }, + registerSwitcher: (switcher: CapabilitiesSwitcher) => { + this.capabilitiesSwitchers.push(switcher); + }, + }; + } + + public start(): CapabilitiesStart { + return { + resolveCapabilities: request => this.resolveCapabilities(request, []), + }; + } +} diff --git a/src/core/server/capabilities/index.ts b/src/core/server/capabilities/index.ts new file mode 100644 index 0000000000000..ac9454f01391c --- /dev/null +++ b/src/core/server/capabilities/index.ts @@ -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. + */ + +export { CapabilitiesService, CapabilitiesSetup, CapabilitiesStart } from './capabilities_service'; +export { Capabilities, CapabilitiesSwitcher, CapabilitiesProvider } from './types'; diff --git a/src/core/server/capabilities/integration_tests/capabilities_service.test.ts b/src/core/server/capabilities/integration_tests/capabilities_service.test.ts new file mode 100644 index 0000000000000..6be9846f5a86a --- /dev/null +++ b/src/core/server/capabilities/integration_tests/capabilities_service.test.ts @@ -0,0 +1,95 @@ +/* + * 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 supertest from 'supertest'; +import { HttpService, InternalHttpServiceSetup } from '../../http'; +import { contextServiceMock } from '../../context/context_service.mock'; +import { loggingServiceMock } from '../../logging/logging_service.mock'; +import { Env } from '../../config'; +import { getEnvOptions } from '../../config/__mocks__/env'; +import { CapabilitiesService, CapabilitiesSetup } from '..'; +import { createHttpServer } from '../../http/test_utils'; + +const coreId = Symbol('core'); +const env = Env.createDefault(getEnvOptions()); + +describe('CapabilitiesService', () => { + let server: HttpService; + let httpSetup: InternalHttpServiceSetup; + + let service: CapabilitiesService; + let serviceSetup: CapabilitiesSetup; + + beforeEach(async () => { + server = createHttpServer(); + httpSetup = await server.setup({ + context: contextServiceMock.createSetupContract(), + }); + service = new CapabilitiesService({ + coreId, + env, + logger: loggingServiceMock.create(), + configService: {} as any, + }); + serviceSetup = await service.setup({ http: httpSetup }); + await server.start(); + }); + + afterEach(async () => { + await server.stop(); + }); + + describe('/api/core/capabilities route', () => { + it('is exposed', async () => { + const result = await supertest(httpSetup.server.listener) + .post('/api/core/capabilities') + .send({ applications: [] }) + .expect(200); + expect(result.body).toMatchInlineSnapshot(` + Object { + "catalogue": Object {}, + "management": Object {}, + "navLinks": Object {}, + } + `); + }); + + it('uses the service capabilities providers', async () => { + serviceSetup.registerProvider(() => ({ + catalogue: { + something: true, + }, + })); + + const result = await supertest(httpSetup.server.listener) + .post('/api/core/capabilities') + .send({ applications: [] }) + .expect(200); + expect(result.body).toMatchInlineSnapshot(` + Object { + "catalogue": Object { + "something": true, + }, + "management": Object {}, + "navLinks": Object {}, + } + `); + }); + }); +}); diff --git a/src/core/server/capabilities/merge_capabilities.test.ts b/src/core/server/capabilities/merge_capabilities.test.ts new file mode 100644 index 0000000000000..ca30dc4bf2f81 --- /dev/null +++ b/src/core/server/capabilities/merge_capabilities.test.ts @@ -0,0 +1,76 @@ +/* + * 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 { mergeCapabilities } from './merge_capabilities'; + +describe('mergeCapabilities', () => { + it('merges empty object with non-empty object', () => { + const capabilities = mergeCapabilities({ foo: {} }, { foo: { bar: true } }); + expect(capabilities).toEqual({ foo: { bar: true } }); + }); + + it('merges nested object properties', () => { + const capabilities = mergeCapabilities({ foo: { baz: true } }, { foo: { bar: true } }); + expect(capabilities).toEqual({ foo: { bar: true, baz: true } }); + }); + + it('merges all object properties', () => { + const capabilities = mergeCapabilities({ foo: { bar: true } }, { hello: { dolly: true } }); + expect(capabilities).toEqual({ foo: { bar: true }, hello: { dolly: true } }); + }); + + it('merges boolean as same path if they are equals', () => { + const capabilities = mergeCapabilities( + { foo: { bar: true, dolly: false, a: true } }, + { foo: { bar: true, dolly: false, b: false } } + ); + expect(capabilities).toEqual({ foo: { bar: true, dolly: false, a: true, b: false } }); + }); + + it('throws if boolean at same path are not equals', () => { + expect(() => { + mergeCapabilities({ foo: { bar: false } }, { foo: { bar: true } }); + }).toThrowErrorMatchingInlineSnapshot( + `"conflict trying to merge booleans with different values"` + ); + + expect(() => { + mergeCapabilities({ foo: { bar: true } }, { foo: { bar: false } }); + }).toThrowErrorMatchingInlineSnapshot( + `"conflict trying to merge booleans with different values"` + ); + }); + + it('throws if value as same path is boolean on left and object on right', () => { + expect(() => { + mergeCapabilities({ foo: { bar: false } }, { foo: { bar: {} } }); + }).toThrowErrorMatchingInlineSnapshot(`"conflict trying to merge boolean with object"`); + expect(() => { + mergeCapabilities({ foo: { bar: false } }, { foo: { bar: { baz: false } } }); + }).toThrowErrorMatchingInlineSnapshot(`"conflict trying to merge boolean with object"`); + }); + + it('should not alter the input capabilities', () => { + const left = { foo: { bar: true } }; + const right = { hello: { dolly: true } }; + mergeCapabilities(left, right); + expect(left).toEqual({ foo: { bar: true } }); + expect(right).toEqual({ hello: { dolly: true } }); + }); +}); diff --git a/src/legacy/server/capabilities/merge_capabilities.ts b/src/core/server/capabilities/merge_capabilities.ts similarity index 60% rename from src/legacy/server/capabilities/merge_capabilities.ts rename to src/core/server/capabilities/merge_capabilities.ts index 5fe31775ba32d..95296346ad835 100644 --- a/src/legacy/server/capabilities/merge_capabilities.ts +++ b/src/core/server/capabilities/merge_capabilities.ts @@ -17,28 +17,19 @@ * under the License. */ -import typeDetect from 'type-detect'; import { merge } from 'lodash'; -import { Capabilities } from '../../../core/public'; +import { Capabilities } from './types'; export const mergeCapabilities = (...sources: Array>): Capabilities => - merge( - { - navLinks: {}, - management: {}, - catalogue: {}, - }, - ...sources, - (a: any, b: any) => { - if ( - (typeDetect(a) === 'boolean' && typeDetect(b) === 'Object') || - (typeDetect(b) === 'boolean' && typeDetect(a) === 'Object') - ) { - throw new Error(`a boolean and an object can't be merged`); - } + merge({}, ...sources, (a: any, b: any) => { + if ( + (typeof a === 'boolean' && typeof b === 'object') || + (typeof a === 'object' && typeof b === 'boolean') + ) { + throw new Error(`conflict trying to merge boolean with object`); + } - if (typeDetect(a) === 'boolean' && typeDetect(b) === 'boolean' && a !== b) { - throw new Error(`"true" and "false" can't be merged`); - } + if (typeof a === 'boolean' && typeof b === 'boolean' && a !== b) { + throw new Error(`conflict trying to merge booleans with different values`); } - ); + }); diff --git a/src/core/server/capabilities/resolve_capabilities.test.ts b/src/core/server/capabilities/resolve_capabilities.test.ts new file mode 100644 index 0000000000000..372efeff21ae2 --- /dev/null +++ b/src/core/server/capabilities/resolve_capabilities.test.ts @@ -0,0 +1,164 @@ +/* + * 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 { Capabilities } from './types'; +import { resolveCapabilities } from './resolve_capabilities'; +import { KibanaRequest } from '../http'; +import { httpServerMock } from '../http/http_server.mocks'; + +describe('resolveCapabilities', () => { + let defaultCaps: Capabilities; + let request: KibanaRequest; + + beforeEach(() => { + defaultCaps = { + navLinks: {}, + catalogue: {}, + management: {}, + }; + request = httpServerMock.createKibanaRequest(); + }); + + it('returns the initial capabilities if no switcher are used', async () => { + const result = await resolveCapabilities(defaultCaps, [], request, []); + expect(result).toEqual(defaultCaps); + }); + + it('applies the switcher to the capabilities ', async () => { + const caps = { + ...defaultCaps, + catalogue: { + A: true, + B: true, + }, + }; + const switcher = (req: KibanaRequest, capabilities: Capabilities) => ({ + ...capabilities, + catalogue: { + ...capabilities.catalogue, + A: false, + }, + }); + const result = await resolveCapabilities(caps, [switcher], request, []); + expect(result).toMatchInlineSnapshot(` + Object { + "catalogue": Object { + "A": false, + "B": true, + }, + "management": Object {}, + "navLinks": Object {}, + } + `); + }); + + it('does not mutate the input capabilities', async () => { + const caps = { + ...defaultCaps, + catalogue: { + A: true, + B: true, + }, + }; + const switcher = (req: KibanaRequest, capabilities: Capabilities) => ({ + ...capabilities, + catalogue: { + ...capabilities.catalogue, + A: false, + }, + }); + await resolveCapabilities(caps, [switcher], request, []); + expect(caps.catalogue).toEqual({ + A: true, + B: true, + }); + }); + + it('ignores any added capability from the switcher', async () => { + const caps = { + ...defaultCaps, + catalogue: { + A: true, + B: true, + }, + }; + const switcher = (req: KibanaRequest, capabilities: Capabilities) => ({ + ...capabilities, + catalogue: { + ...capabilities.catalogue, + C: false, + }, + }); + const result = await resolveCapabilities(caps, [switcher], request, []); + expect(result.catalogue).toEqual({ + A: true, + B: true, + }); + }); + + it('ignores any removed capability from the switcher', async () => { + const caps = { + ...defaultCaps, + catalogue: { + A: true, + B: true, + C: true, + }, + }; + const switcher = (req: KibanaRequest, capabilities: Capabilities) => ({ + ...capabilities, + catalogue: Object.entries(capabilities.catalogue) + .filter(([key]) => key !== 'B') + .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), + }); + const result = await resolveCapabilities(caps, [switcher], request, []); + expect(result.catalogue).toEqual({ + A: true, + B: true, + C: true, + }); + }); + + it('ignores any capability type mutation from the switcher', async () => { + const caps = { + ...defaultCaps, + section: { + boolean: true, + record: { + entry: true, + }, + }, + }; + const switcher = (req: KibanaRequest, capabilities: Capabilities) => ({ + section: { + boolean: { + entry: false, + }, + record: false, + }, + }); + const result = await resolveCapabilities(caps, [switcher], request, []); + expect(result.section).toEqual({ + boolean: true, + record: { + entry: true, + }, + }); + }); +}); diff --git a/src/core/server/capabilities/resolve_capabilities.ts b/src/core/server/capabilities/resolve_capabilities.ts new file mode 100644 index 0000000000000..dcb93bdca5f16 --- /dev/null +++ b/src/core/server/capabilities/resolve_capabilities.ts @@ -0,0 +1,85 @@ +/* + * 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 { cloneDeep } from 'lodash'; +import { Capabilities, CapabilitiesSwitcher } from './types'; +import { KibanaRequest } from '../http'; + +export type CapabilitiesResolver = ( + request: KibanaRequest, + applications: string[] +) => Promise; + +export const getCapabilitiesResolver = ( + capabilities: () => Capabilities, + switchers: () => CapabilitiesSwitcher[] +): CapabilitiesResolver => async ( + request: KibanaRequest, + applications: string[] +): Promise => { + return resolveCapabilities(capabilities(), switchers(), request, applications); +}; + +export const resolveCapabilities = async ( + capabilities: Capabilities, + switchers: CapabilitiesSwitcher[], + request: KibanaRequest, + applications: string[] +): Promise => { + const mergedCaps = cloneDeep({ + ...capabilities, + navLinks: applications.reduce( + (acc, app) => ({ + ...acc, + [app]: true, + }), + capabilities.navLinks + ), + }); + return switchers.reduce(async (caps, switcher) => { + const resolvedCaps = await caps; + const changes = await switcher(request, resolvedCaps); + return recursiveApplyChanges(resolvedCaps, changes); + }, Promise.resolve(mergedCaps)); +}; + +function recursiveApplyChanges< + TDestination extends Record, + TSource extends Record +>(destination: TDestination, source: TSource): TDestination { + return Object.keys(destination) + .map(key => { + const orig = destination[key]; + const changed = source[key]; + if (changed == null) { + return [key, orig]; + } + if (typeof orig === 'object' && typeof changed === 'object') { + return [key, recursiveApplyChanges(orig, changed)]; + } + return [key, typeof orig === typeof changed ? changed : orig]; + }) + .reduce( + (acc, [key, value]) => ({ + ...acc, + [key]: value, + }), + {} as TDestination + ); +} diff --git a/src/core/server/capabilities/routes/index.ts b/src/core/server/capabilities/routes/index.ts new file mode 100644 index 0000000000000..ccaa4621d7003 --- /dev/null +++ b/src/core/server/capabilities/routes/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 { CapabilitiesResolver } from '../resolve_capabilities'; +import { InternalHttpServiceSetup } from '../../http'; +import { registerCapabilitiesRoutes } from './resolve_capabilities'; + +export function registerRoutes(http: InternalHttpServiceSetup, resolver: CapabilitiesResolver) { + const router = http.createRouter('/api/core/capabilities'); + registerCapabilitiesRoutes(router, resolver); +} diff --git a/src/core/server/capabilities/routes/resolve_capabilities.ts b/src/core/server/capabilities/routes/resolve_capabilities.ts new file mode 100644 index 0000000000000..5e1d49b4b1b7e --- /dev/null +++ b/src/core/server/capabilities/routes/resolve_capabilities.ts @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { schema } from '@kbn/config-schema'; +import { IRouter } from '../../http'; +import { CapabilitiesResolver } from '../resolve_capabilities'; + +export function registerCapabilitiesRoutes(router: IRouter, resolver: CapabilitiesResolver) { + // Capabilities are fetched on both authenticated and anonymous routes. + // However when `authRequired` is false, authentication is not performed + // and only default capabilities are returned (all disabled), even for authenticated users. + // So we need two endpoints to handle both scenarios. + [true, false].forEach(authRequired => { + router.post( + { + path: authRequired ? '' : '/defaults', + options: { + authRequired, + }, + validate: { + body: schema.object({ + applications: schema.arrayOf(schema.string()), + }), + }, + }, + async (ctx, req, res) => { + const { applications } = req.body; + const capabilities = await resolver(req, applications); + return res.ok({ + body: capabilities, + }); + } + ); + }); +} diff --git a/src/core/public/application/capabilities/merge_capabilities.ts b/src/core/server/capabilities/types.ts similarity index 63% rename from src/core/public/application/capabilities/merge_capabilities.ts rename to src/core/server/capabilities/types.ts index 55841717985b7..105233761a437 100644 --- a/src/core/public/application/capabilities/merge_capabilities.ts +++ b/src/core/server/capabilities/types.ts @@ -17,23 +17,22 @@ * under the License. */ -import { Capabilities } from './capabilities_service'; +import { Capabilities } from '../../types/capabilities'; +import { KibanaRequest } from '../http'; -export const mergeCapabilities = (...sources: Array>) => - sources.reduce( - (capabilities, source) => { - Object.entries(source).forEach(([key, value]) => { - capabilities[key] = { - ...value, - ...capabilities[key], - }; - }); +export { Capabilities }; - return capabilities; - }, - { - navLinks: {}, - management: {}, - catalogue: {}, - } - ); +/** + * See {@link CapabilitiesSetup} + * @public + */ +export type CapabilitiesProvider = () => Partial; + +/** + * See {@link CapabilitiesSetup} + * @public + */ +export type CapabilitiesSwitcher = ( + request: KibanaRequest, + uiCapabilities: Capabilities +) => Partial | Promise>; diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 6dab120b20e50..c7f6cdb2bb422 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -18,15 +18,15 @@ */ import { Server } from 'hapi'; +import { mockRouter } from './router/router.mock'; import { InternalHttpServiceSetup } from './types'; import { HttpService } from './http_service'; import { OnPreAuthToolkit } from './lifecycle/on_pre_auth'; import { AuthToolkit } from './lifecycle/auth'; import { sessionStorageMock } from './cookie_session_storage.mocks'; -import { IRouter } from './router'; import { OnPostAuthToolkit } from './lifecycle/on_post_auth'; -type ServiceSetupMockType = jest.Mocked & { +export type HttpServiceSetupMock = jest.Mocked & { basePath: jest.Mocked; }; @@ -38,19 +38,8 @@ const createBasePathMock = (): jest.Mocked remove: jest.fn(), }); -const createRouterMock = (): jest.Mocked => ({ - routerPath: '/', - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - patch: jest.fn(), - delete: jest.fn(), - getRoutes: jest.fn(), - handleLegacyErrors: jest.fn().mockImplementation(handler => handler), -}); - const createSetupContractMock = () => { - const setupContract: ServiceSetupMockType = { + const setupContract: HttpServiceSetupMock = { // we can mock other hapi server methods when we need it server: ({ route: jest.fn(), @@ -62,7 +51,7 @@ const createSetupContractMock = () => { registerAuth: jest.fn(), registerOnPostAuth: jest.fn(), registerRouteHandlerContext: jest.fn(), - createRouter: jest.fn(), + createRouter: jest.fn().mockImplementation(() => mockRouter.create({})), basePath: createBasePathMock(), auth: { get: jest.fn(), @@ -75,7 +64,7 @@ const createSetupContractMock = () => { setupContract.createCookieSessionStorageFactory.mockResolvedValue( sessionStorageMock.createFactory() ); - setupContract.createRouter.mockImplementation(createRouterMock); + setupContract.createRouter.mockImplementation(() => mockRouter.create()); return setupContract; }; @@ -110,5 +99,5 @@ export const httpServiceMock = { createOnPreAuthToolkit: createOnPreAuthToolkitMock, createOnPostAuthToolkit: createOnPostAuthToolkitMock, createAuthToolkit: createAuthToolkitMock, - createRouter: createRouterMock, + createRouter: mockRouter.create, }; diff --git a/src/core/server/http/integration_tests/lifecycle.test.ts b/src/core/server/http/integration_tests/lifecycle.test.ts index d5f9343f3e981..2a32db77377a4 100644 --- a/src/core/server/http/integration_tests/lifecycle.test.ts +++ b/src/core/server/http/integration_tests/lifecycle.test.ts @@ -18,49 +18,28 @@ */ import supertest from 'supertest'; -import { ByteSizeValue } from '@kbn/config-schema'; import request from 'request'; -import { BehaviorSubject } from 'rxjs'; import { ensureRawRequest } from '../router'; import { HttpService } from '../http_service'; -import { CoreContext } from '../../core_context'; -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 { loggingServiceMock } from '../../logging/logging_service.mock'; +import { createHttpServer } from '../test_utils'; let server: HttpService; let logger: ReturnType; -let env: Env; -let coreContext: CoreContext; -const configService = configServiceMock.create(); + const contextSetup = contextServiceMock.createSetupContract(); const setupDeps = { context: contextSetup, }; -configService.atPath.mockReturnValue( - new BehaviorSubject({ - hosts: ['localhost'], - maxPayload: new ByteSizeValue(1024), - autoListen: true, - ssl: { - enabled: false, - }, - compression: { enabled: true }, - } as any) -); beforeEach(() => { logger = loggingServiceMock.create(); - env = Env.createDefault(getEnvOptions()); - - coreContext = { coreId: Symbol('core'), env, logger, configService: configService as any }; - server = new HttpService(coreContext); + server = createHttpServer({ logger }); }); afterEach(async () => { diff --git a/src/core/server/http/integration_tests/router.test.ts b/src/core/server/http/integration_tests/router.test.ts index 463f941ba84b4..7d95110b98a12 100644 --- a/src/core/server/http/integration_tests/router.test.ts +++ b/src/core/server/http/integration_tests/router.test.ts @@ -18,49 +18,28 @@ */ import { Stream } from 'stream'; import Boom from 'boom'; - import supertest from 'supertest'; -import { BehaviorSubject } from 'rxjs'; -import { ByteSizeValue, schema } from '@kbn/config-schema'; +import { schema } from '@kbn/config-schema'; import { HttpService } from '../http_service'; -import { CoreContext } from '../../core_context'; -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 { loggingServiceMock } from '../../logging/logging_service.mock'; +import { createHttpServer } from '../test_utils'; let server: HttpService; let logger: ReturnType; -let env: Env; -let coreContext: CoreContext; -const configService = configServiceMock.create(); const contextSetup = contextServiceMock.createSetupContract(); const setupDeps = { context: contextSetup, }; -configService.atPath.mockReturnValue( - new BehaviorSubject({ - hosts: ['localhost'], - maxPayload: new ByteSizeValue(1024), - autoListen: true, - ssl: { - enabled: false, - }, - compression: { enabled: true }, - } as any) -); beforeEach(() => { logger = loggingServiceMock.create(); - env = Env.createDefault(getEnvOptions()); - coreContext = { coreId: Symbol('core'), env, logger, configService: configService as any }; - server = new HttpService(coreContext); + server = createHttpServer({ logger }); }); afterEach(async () => { diff --git a/src/legacy/server/capabilities/resolve_capabilities.ts b/src/core/server/http/router/router.mock.ts similarity index 61% rename from src/legacy/server/capabilities/resolve_capabilities.ts rename to src/core/server/http/router/router.mock.ts index 0df4932099b54..b43db0ca7ed5a 100644 --- a/src/legacy/server/capabilities/resolve_capabilities.ts +++ b/src/core/server/http/router/router.mock.ts @@ -17,18 +17,23 @@ * under the License. */ -import { Request } from 'hapi'; +import { IRouter } from './router'; -import { Capabilities } from '../../../core/public'; -import { mergeCapabilities } from './merge_capabilities'; -import { CapabilitiesModifier } from './capabilities_mixin'; +export type RouterMock = DeeplyMockedKeys; -export const resolveCapabilities = ( - request: Request, - modifiers: CapabilitiesModifier[], - ...capabilities: Array> -) => - modifiers.reduce( - async (resolvedCaps, modifier) => modifier(request, await resolvedCaps), - Promise.resolve(mergeCapabilities(...capabilities)) - ); +function create({ routerPath = '' }: { routerPath?: string } = {}): RouterMock { + return { + routerPath, + get: jest.fn(), + post: jest.fn(), + delete: jest.fn(), + put: jest.fn(), + patch: jest.fn(), + getRoutes: jest.fn(), + handleLegacyErrors: jest.fn().mockImplementation(handler => handler), + }; +} + +export const mockRouter = { + create, +}; diff --git a/src/core/server/http/test_utils.ts b/src/core/server/http/test_utils.ts new file mode 100644 index 0000000000000..e0a15cdc6e839 --- /dev/null +++ b/src/core/server/http/test_utils.ts @@ -0,0 +1,63 @@ +/* + * 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 { 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 { loggingServiceMock } from '../logging/logging_service.mock'; + +const coreId = Symbol('core'); +const env = Env.createDefault(getEnvOptions()); + +const logger = loggingServiceMock.create(); + +const configService = configServiceMock.create(); +configService.atPath.mockReturnValue( + new BehaviorSubject({ + hosts: ['localhost'], + maxPayload: new ByteSizeValue(1024), + autoListen: true, + ssl: { + enabled: false, + }, + compression: { enabled: true }, + } as any) +); + +const defaultContext: CoreContext = { + coreId, + env, + logger, + configService, +}; + +/** + * Creates a concrete HttpServer with a mocked context. + */ +export const createHttpServer = (overrides: Partial = {}): HttpService => { + const context = { + ...defaultContext, + ...overrides, + }; + return new HttpService(context); +}; diff --git a/src/core/server/index.ts b/src/core/server/index.ts index a54ada233bbc9..efff85142c3e4 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -46,8 +46,10 @@ import { ContextSetup } from './context'; import { IUiSettingsClient, UiSettingsServiceSetup } from './ui_settings'; import { SavedObjectsClientContract } from './saved_objects/types'; import { SavedObjectsServiceSetup, SavedObjectsServiceStart } from './saved_objects'; +import { CapabilitiesSetup, CapabilitiesStart } from './capabilities'; export { bootstrap } from './bootstrap'; +export { Capabilities, CapabilitiesProvider, CapabilitiesSwitcher } from './capabilities'; export { ConfigPath, ConfigService, EnvironmentMode, PackageInfo } from './config'; export { IContextContainer, @@ -240,6 +242,8 @@ export interface RequestHandlerContext { * @public */ export interface CoreSetup { + /** {@link CapabilitiesSetup} */ + capabilities: CapabilitiesSetup; /** {@link ContextSetup} */ context: ContextSetup; /** {@link ElasticsearchServiceSetup} */ @@ -258,8 +262,17 @@ export interface CoreSetup { * @public */ export interface CoreStart { + /** {@link CapabilitiesStart} */ + capabilities: CapabilitiesStart; /** {@link SavedObjectsServiceStart} */ savedObjects: SavedObjectsServiceStart; } -export { ContextSetup, PluginsServiceSetup, PluginsServiceStart, PluginOpaqueId }; +export { + CapabilitiesSetup, + CapabilitiesStart, + ContextSetup, + PluginsServiceSetup, + PluginsServiceStart, + PluginOpaqueId, +}; diff --git a/src/core/server/internal_types.ts b/src/core/server/internal_types.ts index d1a65c6f3437e..07fd77f83d774 100644 --- a/src/core/server/internal_types.ts +++ b/src/core/server/internal_types.ts @@ -25,9 +25,11 @@ import { InternalSavedObjectsServiceStart, InternalSavedObjectsServiceSetup, } from './saved_objects'; +import { CapabilitiesSetup, CapabilitiesStart } from './capabilities'; /** @internal */ export interface InternalCoreSetup { + capabilities: CapabilitiesSetup; context: ContextSetup; http: InternalHttpServiceSetup; elasticsearch: InternalElasticsearchServiceSetup; @@ -39,5 +41,6 @@ export interface InternalCoreSetup { * @internal */ export interface InternalCoreStart { + capabilities: CapabilitiesStart; savedObjects: InternalSavedObjectsServiceStart; } diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index 286e1a0612c94..0bd4eafe78ce1 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -46,6 +46,7 @@ import { DiscoveredPlugin } from '../plugins'; import { httpServiceMock } from '../http/http_service.mock'; import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock'; import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock'; +import { capabilitiesServiceMock } from '../capabilities/capabilities_service.mock'; const MockKbnServer: jest.Mock = KbnServer as any; @@ -69,6 +70,7 @@ beforeEach(() => { setupDeps = { core: { + capabilities: capabilitiesServiceMock.createSetupContract(), context: contextServiceMock.createSetupContract(), elasticsearch: { legacy: {} } as any, uiSettings: uiSettingsServiceMock.createSetupContract(), @@ -93,6 +95,7 @@ beforeEach(() => { startDeps = { core: { + capabilities: capabilitiesServiceMock.createStartContract(), savedObjects: savedObjectsServiceMock.createStartContract(), plugins: { contracts: new Map() }, }, diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index fd081b23a0ef2..1c9467e4d60de 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -241,6 +241,7 @@ export class LegacyService implements CoreService { } ) { const coreSetup: CoreSetup = { + capabilities: setupDeps.core.capabilities, context: setupDeps.core.context, elasticsearch: { adminClient$: setupDeps.core.elasticsearch.adminClient$, @@ -271,6 +272,7 @@ export class LegacyService implements CoreService { }, }; const coreStart: CoreStart = { + capabilities: startDeps.core.capabilities, savedObjects: { getScopedClient: startDeps.core.savedObjects.getScopedClient }, }; diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index a811efdf4b1b9..025ebce22e2c4 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -25,6 +25,7 @@ import { contextServiceMock } from './context/context_service.mock'; import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; import { InternalCoreSetup, InternalCoreStart } from './internal_types'; +import { capabilitiesServiceMock } from './capabilities/capabilities_service.mock'; export { httpServerMock } from './http/http_server.mocks'; export { sessionStorageMock } from './http/cookie_session_storage.mocks'; @@ -86,6 +87,7 @@ function createCoreSetupMock() { register: uiSettingsServiceMock.createSetupContract().register, }; const mock: MockedKeys = { + capabilities: capabilitiesServiceMock.createSetupContract(), context: contextServiceMock.createSetupContract(), elasticsearch: elasticsearchServiceMock.createSetupContract(), http: httpMock, @@ -98,6 +100,7 @@ function createCoreSetupMock() { function createCoreStartMock() { const mock: MockedKeys = { + capabilities: capabilitiesServiceMock.createStartContract(), savedObjects: savedObjectsServiceMock.createStartContract(), }; @@ -106,6 +109,7 @@ function createCoreStartMock() { function createInternalCoreSetupMock() { const setupDeps: InternalCoreSetup = { + capabilities: capabilitiesServiceMock.createSetupContract(), context: contextServiceMock.createSetupContract(), elasticsearch: elasticsearchServiceMock.createSetupContract(), http: httpServiceMock.createSetupContract(), @@ -117,6 +121,7 @@ function createInternalCoreSetupMock() { function createInternalCoreStartMock() { const startDeps: InternalCoreStart = { + capabilities: capabilitiesServiceMock.createStartContract(), savedObjects: savedObjectsServiceMock.createStartContract(), }; return startDeps; diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 6edce1b2533cb..773b6e121ce5c 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -102,6 +102,10 @@ export function createPluginSetupContext( plugin: PluginWrapper ): CoreSetup { return { + capabilities: { + registerProvider: deps.capabilities.registerProvider, + registerSwitcher: deps.capabilities.registerSwitcher, + }, context: { createContextContainer: deps.context.createContextContainer, }, @@ -153,6 +157,9 @@ export function createPluginStartContext( plugin: PluginWrapper ): CoreStart { return { + capabilities: { + resolveCapabilities: deps.capabilities.resolveCapabilities, + }, savedObjects: { getScopedClient: deps.savedObjects.getScopedClient }, }; } diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index e8cafe1e1334e..26d1921e06446 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1,1731 +1,1762 @@ -## API Report File for "kibana" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import Boom from 'boom'; -import { BulkIndexDocumentsParams } from 'elasticsearch'; -import { CatAliasesParams } from 'elasticsearch'; -import { CatAllocationParams } from 'elasticsearch'; -import { CatCommonParams } from 'elasticsearch'; -import { CatFielddataParams } from 'elasticsearch'; -import { CatHealthParams } from 'elasticsearch'; -import { CatHelpParams } from 'elasticsearch'; -import { CatIndicesParams } from 'elasticsearch'; -import { CatRecoveryParams } from 'elasticsearch'; -import { CatSegmentsParams } from 'elasticsearch'; -import { CatShardsParams } from 'elasticsearch'; -import { CatSnapshotsParams } from 'elasticsearch'; -import { CatTasksParams } from 'elasticsearch'; -import { CatThreadPoolParams } from 'elasticsearch'; -import { ClearScrollParams } from 'elasticsearch'; -import { Client } from 'elasticsearch'; -import { ClusterAllocationExplainParams } from 'elasticsearch'; -import { ClusterGetSettingsParams } from 'elasticsearch'; -import { ClusterHealthParams } from 'elasticsearch'; -import { ClusterPendingTasksParams } from 'elasticsearch'; -import { ClusterPutSettingsParams } from 'elasticsearch'; -import { ClusterRerouteParams } from 'elasticsearch'; -import { ClusterStateParams } from 'elasticsearch'; -import { ClusterStatsParams } from 'elasticsearch'; -import { ConfigOptions } from 'elasticsearch'; -import { CountParams } from 'elasticsearch'; -import { CreateDocumentParams } from 'elasticsearch'; -import { DeleteDocumentByQueryParams } from 'elasticsearch'; -import { DeleteDocumentParams } from 'elasticsearch'; -import { DeleteScriptParams } from 'elasticsearch'; -import { DeleteTemplateParams } from 'elasticsearch'; -import { DetailedPeerCertificate } from 'tls'; -import { Duration } from 'moment'; -import { ExistsParams } from 'elasticsearch'; -import { ExplainParams } from 'elasticsearch'; -import { FieldStatsParams } from 'elasticsearch'; -import { GenericParams } from 'elasticsearch'; -import { GetParams } from 'elasticsearch'; -import { GetResponse } from 'elasticsearch'; -import { GetScriptParams } from 'elasticsearch'; -import { GetSourceParams } from 'elasticsearch'; -import { GetTemplateParams } from 'elasticsearch'; -import { IncomingHttpHeaders } from 'http'; -import { IndexDocumentParams } from 'elasticsearch'; -import { IndicesAnalyzeParams } from 'elasticsearch'; -import { IndicesClearCacheParams } from 'elasticsearch'; -import { IndicesCloseParams } from 'elasticsearch'; -import { IndicesCreateParams } from 'elasticsearch'; -import { IndicesDeleteAliasParams } from 'elasticsearch'; -import { IndicesDeleteParams } from 'elasticsearch'; -import { IndicesDeleteTemplateParams } from 'elasticsearch'; -import { IndicesExistsAliasParams } from 'elasticsearch'; -import { IndicesExistsParams } from 'elasticsearch'; -import { IndicesExistsTemplateParams } from 'elasticsearch'; -import { IndicesExistsTypeParams } from 'elasticsearch'; -import { IndicesFlushParams } from 'elasticsearch'; -import { IndicesFlushSyncedParams } from 'elasticsearch'; -import { IndicesForcemergeParams } from 'elasticsearch'; -import { IndicesGetAliasParams } from 'elasticsearch'; -import { IndicesGetFieldMappingParams } from 'elasticsearch'; -import { IndicesGetMappingParams } from 'elasticsearch'; -import { IndicesGetParams } from 'elasticsearch'; -import { IndicesGetSettingsParams } from 'elasticsearch'; -import { IndicesGetTemplateParams } from 'elasticsearch'; -import { IndicesGetUpgradeParams } from 'elasticsearch'; -import { IndicesOpenParams } from 'elasticsearch'; -import { IndicesPutAliasParams } from 'elasticsearch'; -import { IndicesPutMappingParams } from 'elasticsearch'; -import { IndicesPutSettingsParams } from 'elasticsearch'; -import { IndicesPutTemplateParams } from 'elasticsearch'; -import { IndicesRecoveryParams } from 'elasticsearch'; -import { IndicesRefreshParams } from 'elasticsearch'; -import { IndicesRolloverParams } from 'elasticsearch'; -import { IndicesSegmentsParams } from 'elasticsearch'; -import { IndicesShardStoresParams } from 'elasticsearch'; -import { IndicesShrinkParams } from 'elasticsearch'; -import { IndicesStatsParams } from 'elasticsearch'; -import { IndicesUpdateAliasesParams } from 'elasticsearch'; -import { IndicesUpgradeParams } from 'elasticsearch'; -import { IndicesValidateQueryParams } from 'elasticsearch'; -import { InfoParams } from 'elasticsearch'; -import { IngestDeletePipelineParams } from 'elasticsearch'; -import { IngestGetPipelineParams } from 'elasticsearch'; -import { IngestPutPipelineParams } from 'elasticsearch'; -import { IngestSimulateParams } from 'elasticsearch'; -import { KibanaConfigType } from 'src/core/server/kibana_config'; -import { Logger as Logger_2 } from 'src/core/server/logging'; -import { MGetParams } from 'elasticsearch'; -import { MGetResponse } from 'elasticsearch'; -import { MSearchParams } from 'elasticsearch'; -import { MSearchResponse } from 'elasticsearch'; -import { MSearchTemplateParams } from 'elasticsearch'; -import { MTermVectorsParams } from 'elasticsearch'; -import { NodesHotThreadsParams } from 'elasticsearch'; -import { NodesInfoParams } from 'elasticsearch'; -import { NodesStatsParams } from 'elasticsearch'; -import { ObjectType } from '@kbn/config-schema'; -import { Observable } from 'rxjs'; -import { PeerCertificate } from 'tls'; -import { PingParams } from 'elasticsearch'; -import { PutScriptParams } from 'elasticsearch'; -import { PutTemplateParams } from 'elasticsearch'; -import { Readable } from 'stream'; -import { ReindexParams } from 'elasticsearch'; -import { ReindexRethrottleParams } from 'elasticsearch'; -import { RenderSearchTemplateParams } from 'elasticsearch'; -import { Request } from 'hapi'; -import { ResponseObject } from 'hapi'; -import { ResponseToolkit } from 'hapi'; -import { ScrollParams } from 'elasticsearch'; -import { SearchParams } from 'elasticsearch'; -import { SearchResponse } from 'elasticsearch'; -import { SearchShardsParams } from 'elasticsearch'; -import { SearchTemplateParams } from 'elasticsearch'; -import { Server } from 'hapi'; -import { ShallowPromise } from '@kbn/utility-types'; -import { SnapshotCreateParams } from 'elasticsearch'; -import { SnapshotCreateRepositoryParams } from 'elasticsearch'; -import { SnapshotDeleteParams } from 'elasticsearch'; -import { SnapshotDeleteRepositoryParams } from 'elasticsearch'; -import { SnapshotGetParams } from 'elasticsearch'; -import { SnapshotGetRepositoryParams } from 'elasticsearch'; -import { SnapshotRestoreParams } from 'elasticsearch'; -import { SnapshotStatusParams } from 'elasticsearch'; -import { SnapshotVerifyRepositoryParams } from 'elasticsearch'; -import { Stream } from 'stream'; -import { SuggestParams } from 'elasticsearch'; -import { TasksCancelParams } from 'elasticsearch'; -import { TasksGetParams } from 'elasticsearch'; -import { TasksListParams } from 'elasticsearch'; -import { TermvectorsParams } from 'elasticsearch'; -import { Type } from '@kbn/config-schema'; -import { TypeOf } from '@kbn/config-schema'; -import { UpdateDocumentByQueryParams } from 'elasticsearch'; -import { UpdateDocumentParams } from 'elasticsearch'; -import { Url } from 'url'; - -// @public (undocumented) -export interface APICaller { - // (undocumented) - (endpoint: 'cluster.state', params: ClusterStateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'bulk', params: BulkIndexDocumentsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'count', params: CountParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'create', params: CreateDocumentParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'delete', params: DeleteDocumentParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'deleteByQuery', params: DeleteDocumentByQueryParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'deleteScript', params: DeleteScriptParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'deleteTemplate', params: DeleteTemplateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'exists', params: ExistsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'explain', params: ExplainParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'fieldStats', params: FieldStatsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'get', params: GetParams, options?: CallAPIOptions): Promise>; - // (undocumented) - (endpoint: 'getScript', params: GetScriptParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'getSource', params: GetSourceParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'getTemplate', params: GetTemplateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'index', params: IndexDocumentParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'info', params: InfoParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'mget', params: MGetParams, options?: CallAPIOptions): Promise>; - // (undocumented) - (endpoint: 'msearch', params: MSearchParams, options?: CallAPIOptions): Promise>; - // (undocumented) - (endpoint: 'msearchTemplate', params: MSearchTemplateParams, options?: CallAPIOptions): Promise>; - // (undocumented) - (endpoint: 'mtermvectors', params: MTermVectorsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'ping', params: PingParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'putScript', params: PutScriptParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'putTemplate', params: PutTemplateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'reindex', params: ReindexParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'reindexRethrottle', params: ReindexRethrottleParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'renderSearchTemplate', params: RenderSearchTemplateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'scroll', params: ScrollParams, options?: CallAPIOptions): Promise>; - // (undocumented) - (endpoint: 'search', params: SearchParams, options?: CallAPIOptions): Promise>; - // (undocumented) - (endpoint: 'searchShards', params: SearchShardsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'searchTemplate', params: SearchTemplateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'suggest', params: SuggestParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'termvectors', params: TermvectorsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'update', params: UpdateDocumentParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'updateByQuery', params: UpdateDocumentByQueryParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.aliases', params: CatAliasesParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.allocation', params: CatAllocationParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.count', params: CatAllocationParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.fielddata', params: CatFielddataParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.health', params: CatHealthParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.help', params: CatHelpParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.indices', params: CatIndicesParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.master', params: CatCommonParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.nodeattrs', params: CatCommonParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.nodes', params: CatCommonParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.pendingTasks', params: CatCommonParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.plugins', params: CatCommonParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.recovery', params: CatRecoveryParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.repositories', params: CatCommonParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.segments', params: CatSegmentsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.shards', params: CatShardsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.snapshots', params: CatSnapshotsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.tasks', params: CatTasksParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cat.threadPool', params: CatThreadPoolParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cluster.allocationExplain', params: ClusterAllocationExplainParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cluster.getSettings', params: ClusterGetSettingsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cluster.health', params: ClusterHealthParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cluster.pendingTasks', params: ClusterPendingTasksParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cluster.putSettings', params: ClusterPutSettingsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cluster.reroute', params: ClusterRerouteParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'clearScroll', params: ClearScrollParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'cluster.stats', params: ClusterStatsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.analyze', params: IndicesAnalyzeParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.clearCache', params: IndicesClearCacheParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.close', params: IndicesCloseParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.create', params: IndicesCreateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.delete', params: IndicesDeleteParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.deleteAlias', params: IndicesDeleteAliasParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.deleteTemplate', params: IndicesDeleteTemplateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.exists', params: IndicesExistsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.existsAlias', params: IndicesExistsAliasParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.existsTemplate', params: IndicesExistsTemplateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.existsType', params: IndicesExistsTypeParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.flush', params: IndicesFlushParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.flushSynced', params: IndicesFlushSyncedParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.forcemerge', params: IndicesForcemergeParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.get', params: IndicesGetParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.getAlias', params: IndicesGetAliasParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.getFieldMapping', params: IndicesGetFieldMappingParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.getMapping', params: IndicesGetMappingParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.getSettings', params: IndicesGetSettingsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.getTemplate', params: IndicesGetTemplateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.getUpgrade', params: IndicesGetUpgradeParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.open', params: IndicesOpenParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.putAlias', params: IndicesPutAliasParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.putMapping', params: IndicesPutMappingParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.putSettings', params: IndicesPutSettingsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.putTemplate', params: IndicesPutTemplateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.recovery', params: IndicesRecoveryParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.refresh', params: IndicesRefreshParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.rollover', params: IndicesRolloverParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.segments', params: IndicesSegmentsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.shardStores', params: IndicesShardStoresParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.shrink', params: IndicesShrinkParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.stats', params: IndicesStatsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.updateAliases', params: IndicesUpdateAliasesParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.upgrade', params: IndicesUpgradeParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'indices.validateQuery', params: IndicesValidateQueryParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'ingest.deletePipeline', params: IngestDeletePipelineParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'ingest.getPipeline', params: IngestGetPipelineParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'ingest.putPipeline', params: IngestPutPipelineParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'ingest.simulate', params: IngestSimulateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'nodes.hotThreads', params: NodesHotThreadsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'nodes.info', params: NodesInfoParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'nodes.stats', params: NodesStatsParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'snapshot.create', params: SnapshotCreateParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'snapshot.createRepository', params: SnapshotCreateRepositoryParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'snapshot.delete', params: SnapshotDeleteParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'snapshot.deleteRepository', params: SnapshotDeleteRepositoryParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'snapshot.get', params: SnapshotGetParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'snapshot.getRepository', params: SnapshotGetRepositoryParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'snapshot.restore', params: SnapshotRestoreParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'snapshot.status', params: SnapshotStatusParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'snapshot.verifyRepository', params: SnapshotVerifyRepositoryParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'tasks.cancel', params: TasksCancelParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'tasks.get', params: TasksGetParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'tasks.list', params: TasksListParams, options?: CallAPIOptions): ReturnType; - // (undocumented) - (endpoint: 'transport.request', clientParams: AssistantAPIClientParams, options?: CallAPIOptions): Promise; - // (undocumented) - (endpoint: 'transport.request', clientParams: DeprecationAPIClientParams, options?: CallAPIOptions): Promise; - // (undocumented) - (endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; -} - -// @public (undocumented) -export interface AssistanceAPIResponse { - // (undocumented) - indices: { - [indexName: string]: { - action_required: MIGRATION_ASSISTANCE_INDEX_ACTION; - }; - }; -} - -// @public (undocumented) -export interface AssistantAPIClientParams extends GenericParams { - // (undocumented) - method: 'GET'; - // (undocumented) - path: '/_migration/assistance'; -} - -// @public (undocumented) -export interface Authenticated extends AuthResultParams { - // (undocumented) - type: AuthResultType.authenticated; -} - -// @public -export type AuthenticationHandler = (request: KibanaRequest, response: LifecycleResponseFactory, toolkit: AuthToolkit) => AuthResult | IKibanaResponse | Promise; - -// @public -export type AuthHeaders = Record; - -// @public (undocumented) -export type AuthResult = Authenticated; - -// @public -export interface AuthResultParams { - requestHeaders?: AuthHeaders; - responseHeaders?: AuthHeaders; - state?: Record; -} - -// @public (undocumented) -export enum AuthResultType { - // (undocumented) - authenticated = "authenticated" -} - -// @public -export enum AuthStatus { - authenticated = "authenticated", - unauthenticated = "unauthenticated", - unknown = "unknown" -} - -// @public -export interface AuthToolkit { - authenticated: (data?: AuthResultParams) => AuthResult; -} - -// @public -export class BasePath { - // @internal - constructor(serverBasePath?: string); - get: (request: KibanaRequest | LegacyRequest) => string; - prepend: (path: string) => string; - remove: (path: string) => string; - readonly serverBasePath: string; - set: (request: KibanaRequest | LegacyRequest, requestSpecificBasePath: string) => void; -} - -// Warning: (ae-forgotten-export) The symbol "BootstrapArgs" needs to be exported by the entry point index.d.ts -// -// @internal (undocumented) -export function bootstrap({ configs, cliArgs, applyConfigOverrides, features, }: BootstrapArgs): Promise; - -// @public -export interface CallAPIOptions { - signal?: AbortSignal; - wrap401Errors?: boolean; -} - -// @public -export class ClusterClient implements IClusterClient { - constructor(config: ElasticsearchClientConfig, log: Logger, getAuthHeaders?: GetAuthHeaders); - asScoped(request?: KibanaRequest | LegacyRequest | FakeRequest): IScopedClusterClient; - callAsInternalUser: APICaller; - close(): void; - } - -// @public (undocumented) -export type ConfigPath = string | string[]; - -// @internal (undocumented) -export class ConfigService { - // Warning: (ae-forgotten-export) The symbol "Config" 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(config$: Observable, env: Env, logger: LoggerFactory); - atPath(path: ConfigPath): Observable; - getConfig$(): Observable; - // (undocumented) - getUnusedPaths(): Promise; - // (undocumented) - getUsedPaths(): Promise; - // (undocumented) - isEnabledAtPath(path: ConfigPath): Promise; - optionalAtPath(path: ConfigPath): Observable; - setSchema(path: ConfigPath, schema: Type): Promise; - } - -// @public -export interface ContextSetup { - createContextContainer>(): IContextContainer; -} - -// @internal (undocumented) -export type CoreId = symbol; - -// @public -export interface CoreSetup { - // (undocumented) - context: ContextSetup; - // (undocumented) - elasticsearch: ElasticsearchServiceSetup; - // (undocumented) - http: HttpServiceSetup; - // (undocumented) - savedObjects: SavedObjectsServiceSetup; - // (undocumented) - uiSettings: UiSettingsServiceSetup; -} - -// @public -export interface CoreStart { - // (undocumented) - savedObjects: SavedObjectsServiceStart; -} - -// @public -export interface CustomHttpResponseOptions { - body?: T; - headers?: ResponseHeaders; - // (undocumented) - statusCode: number; -} - -// @public (undocumented) -export interface DeprecationAPIClientParams extends GenericParams { - // (undocumented) - method: 'GET'; - // (undocumented) - path: '/_migration/deprecations'; -} - -// @public (undocumented) -export interface DeprecationAPIResponse { - // (undocumented) - cluster_settings: DeprecationInfo[]; - // (undocumented) - index_settings: IndexSettingsDeprecationInfo; - // (undocumented) - ml_settings: DeprecationInfo[]; - // (undocumented) - node_settings: DeprecationInfo[]; -} - -// @public (undocumented) -export interface DeprecationInfo { - // (undocumented) - details?: string; - // (undocumented) - level: MIGRATION_DEPRECATION_LEVEL; - // (undocumented) - message: string; - // (undocumented) - url: string; -} - -// @public -export interface DiscoveredPlugin { - readonly configPath: ConfigPath; - readonly id: PluginName; - readonly optionalPlugins: readonly PluginName[]; - readonly requiredPlugins: readonly PluginName[]; -} - -// Warning: (ae-forgotten-export) The symbol "ElasticsearchConfig" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -export type ElasticsearchClientConfig = Pick & Pick & { - pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout']; - requestTimeout?: ElasticsearchConfig['requestTimeout'] | ConfigOptions['requestTimeout']; - sniffInterval?: ElasticsearchConfig['sniffInterval'] | ConfigOptions['sniffInterval']; - ssl?: Partial; -}; - -// @public (undocumented) -export interface ElasticsearchError extends Boom { - // (undocumented) - [code]?: string; -} - -// @public -export class ElasticsearchErrorHelpers { - // (undocumented) - static decorateNotAuthorizedError(error: Error, reason?: string): ElasticsearchError; - // (undocumented) - static isNotAuthorizedError(error: any): error is ElasticsearchError; -} - -// @public (undocumented) -export interface ElasticsearchServiceSetup { - readonly adminClient$: Observable; - readonly createClient: (type: string, clientConfig?: Partial) => IClusterClient; - readonly dataClient$: Observable; -} - -// @public (undocumented) -export interface EnvironmentMode { - // (undocumented) - dev: boolean; - // (undocumented) - name: 'development' | 'production'; - // (undocumented) - prod: boolean; -} - -// @public -export interface ErrorHttpResponseOptions { - body?: ResponseError; - headers?: ResponseHeaders; -} - -// @public -export interface FakeRequest { - headers: Headers; -} - -// @public -export type GetAuthHeaders = (request: KibanaRequest | LegacyRequest) => AuthHeaders | undefined; - -// @public -export type GetAuthState = (request: KibanaRequest | LegacyRequest) => { - status: AuthStatus; - state: unknown; -}; - -// @public -export type HandlerContextType> = T extends HandlerFunction ? U : never; - -// @public -export type HandlerFunction = (context: T, ...args: any[]) => any; - -// @public -export type HandlerParameters> = T extends (context: any, ...args: infer U) => any ? U : never; - -// @public -export type Headers = { - [header in KnownHeaders]?: string | string[] | undefined; -} & { - [header: string]: string | string[] | undefined; -}; - -// @public -export interface HttpResponseOptions { - body?: HttpResponsePayload; - headers?: ResponseHeaders; -} - -// @public -export type HttpResponsePayload = undefined | string | Record | Buffer | Stream; - -// @public -export interface HttpServiceSetup { - basePath: IBasePath; - createCookieSessionStorageFactory: (cookieOptions: SessionStorageCookieOptions) => Promise>; - createRouter: () => IRouter; - isTlsEnabled: boolean; - registerAuth: (handler: AuthenticationHandler) => void; - registerOnPostAuth: (handler: OnPostAuthHandler) => void; - registerOnPreAuth: (handler: OnPreAuthHandler) => void; - registerRouteHandlerContext: (contextName: T, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; -} - -// @public (undocumented) -export interface HttpServiceStart { - isListening: (port: number) => boolean; -} - -// @public -export type IBasePath = Pick; - -// @public -export type IClusterClient = Pick; - -// @public -export interface IContextContainer> { - createHandler(pluginOpaqueId: PluginOpaqueId, handler: THandler): (...rest: HandlerParameters) => ShallowPromise>; - registerContext>(pluginOpaqueId: PluginOpaqueId, contextName: TContextName, provider: IContextProvider): this; -} - -// @public -export type IContextProvider, TContextName extends keyof HandlerContextType> = (context: Partial>, ...rest: HandlerParameters) => Promise[TContextName]> | HandlerContextType[TContextName]; - -// @public -export interface IKibanaResponse { - // (undocumented) - readonly options: HttpResponseOptions; - // (undocumented) - readonly payload?: T; - // (undocumented) - readonly status: number; -} - -// @public -export interface IKibanaSocket { - readonly authorizationError?: Error; - readonly authorized?: boolean; - // (undocumented) - getPeerCertificate(detailed: true): DetailedPeerCertificate | null; - // (undocumented) - getPeerCertificate(detailed: false): PeerCertificate | null; - getPeerCertificate(detailed?: boolean): PeerCertificate | DetailedPeerCertificate | null; -} - -// @public (undocumented) -export interface IndexSettingsDeprecationInfo { - // (undocumented) - [indexName: string]: DeprecationInfo[]; -} - -// @public -export interface IRouter { - delete: RouteRegistrar<'delete'>; - get: RouteRegistrar<'get'>; - // Warning: (ae-forgotten-export) The symbol "RouterRoute" needs to be exported by the entry point index.d.ts - // - // @internal - getRoutes: () => RouterRoute[]; - handleLegacyErrors:

(handler: RequestHandler) => RequestHandler; - patch: RouteRegistrar<'patch'>; - post: RouteRegistrar<'post'>; - put: RouteRegistrar<'put'>; - routerPath: string; -} - -// @public -export type IsAuthenticated = (request: KibanaRequest | LegacyRequest) => boolean; - -// @public -export type ISavedObjectsRepository = Pick; - -// @public -export type IScopedClusterClient = Pick; - -// @public -export interface IUiSettingsClient { - get: (key: string) => Promise; - getAll: () => Promise>; - getRegistered: () => Readonly>; - getUserProvided: () => Promise>>; - isOverridden: (key: string) => boolean; - remove: (key: string) => Promise; - removeMany: (keys: string[]) => Promise; - set: (key: string, value: any) => Promise; - setMany: (changes: Record) => Promise; -} - -// @public -export class KibanaRequest { - // @internal (undocumented) - protected readonly [requestSymbol]: Request; - constructor(request: Request, params: Params, query: Query, body: Body, withoutSecretHeaders: boolean); - // (undocumented) - readonly body: Body; - // @internal - static from

| Type>(req: Request, routeSchemas?: RouteSchemas, withoutSecretHeaders?: boolean): KibanaRequest; - readonly headers: Headers; - // (undocumented) - readonly params: Params; - // (undocumented) - readonly query: Query; - readonly route: RecursiveReadonly>; - // (undocumented) - readonly socket: IKibanaSocket; - readonly url: Url; - } - -// @public -export interface KibanaRequestRoute { - // (undocumented) - method: Method; - // (undocumented) - options: KibanaRequestRouteOptions; - // (undocumented) - path: string; -} - -// @public -export type KibanaRequestRouteOptions = Method extends 'get' | 'options' ? Required, 'body'>> : Required>; - -// @public -export type KibanaResponseFactory = typeof kibanaResponseFactory; - -// @public -export const kibanaResponseFactory: { - custom: | Buffer | Stream | { - message: string | Error; - attributes?: Record | undefined; - } | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; - badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse; - unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse; - forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse; - notFound: (options?: ErrorHttpResponseOptions) => KibanaResponse; - conflict: (options?: ErrorHttpResponseOptions) => KibanaResponse; - internalError: (options?: ErrorHttpResponseOptions) => KibanaResponse; - customError: (options: CustomHttpResponseOptions) => KibanaResponse; - redirected: (options: RedirectResponseOptions) => KibanaResponse | Buffer | Stream>; - ok: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; - accepted: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; - noContent: (options?: HttpResponseOptions) => KibanaResponse; -}; - -// Warning: (ae-forgotten-export) The symbol "KnownKeys" needs to be exported by the entry point index.d.ts -// -// @public -export type KnownHeaders = KnownKeys; - -// @public @deprecated (undocumented) -export interface LegacyRequest extends Request { -} - -// @public @deprecated (undocumented) -export interface LegacyServiceSetupDeps { - // Warning: (ae-forgotten-export) The symbol "InternalCoreSetup" needs to be exported by the entry point index.d.ts - // - // (undocumented) - core: InternalCoreSetup & { - plugins: PluginsServiceSetup; - }; - // (undocumented) - plugins: Record; -} - -// @public @deprecated (undocumented) -export interface LegacyServiceStartDeps { - // Warning: (ae-forgotten-export) The symbol "InternalCoreStart" needs to be exported by the entry point index.d.ts - // - // (undocumented) - core: InternalCoreStart & { - plugins: PluginsServiceStart; - }; - // (undocumented) - plugins: Record; -} - -// Warning: (ae-forgotten-export) The symbol "lifecycleResponseFactory" needs to be exported by the entry point index.d.ts -// -// @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; - 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; -} - -// @public -export interface LoggerFactory { - get(...contextParts: string[]): Logger; -} - -// @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; -} - -// @public -export interface LogMeta { - // (undocumented) - [key: string]: any; -} - -// @internal -export interface LogRecord { - // (undocumented) - context: string; - // (undocumented) - error?: Error; - // (undocumented) - level: LogLevel; - // (undocumented) - message: string; - // (undocumented) - meta?: { - [name: string]: any; - }; - // (undocumented) - timestamp: Date; -} - -// @public (undocumented) -export type MIGRATION_ASSISTANCE_INDEX_ACTION = 'upgrade' | 'reindex'; - -// @public (undocumented) -export type MIGRATION_DEPRECATION_LEVEL = 'none' | 'info' | 'warning' | 'critical'; - -// @public -export type MutatingOperationRefreshSetting = boolean | 'wait_for'; - -// Warning: (ae-forgotten-export) The symbol "OnPostAuthResult" needs to be exported by the entry point index.d.ts -// -// @public -export type OnPostAuthHandler = (request: KibanaRequest, response: LifecycleResponseFactory, toolkit: OnPostAuthToolkit) => OnPostAuthResult | KibanaResponse | Promise; - -// @public -export interface OnPostAuthToolkit { - next: () => OnPostAuthResult; -} - -// Warning: (ae-forgotten-export) The symbol "OnPreAuthResult" needs to be exported by the entry point index.d.ts -// -// @public -export type OnPreAuthHandler = (request: KibanaRequest, response: LifecycleResponseFactory, toolkit: OnPreAuthToolkit) => OnPreAuthResult | KibanaResponse | Promise; - -// @public -export interface OnPreAuthToolkit { - next: () => OnPreAuthResult; - rewriteUrl: (url: string) => OnPreAuthResult; -} - -// @public (undocumented) -export interface PackageInfo { - // (undocumented) - branch: string; - // (undocumented) - buildNum: number; - // (undocumented) - buildSha: string; - // (undocumented) - dist: boolean; - // (undocumented) - version: string; -} - -// @public -export interface Plugin { - // (undocumented) - setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; - // (undocumented) - start(core: CoreStart, plugins: TPluginsStart): TStart | Promise; - // (undocumented) - stop?(): void; -} - -// @public -export interface PluginConfigDescriptor { - exposeToBrowser?: { - [P in keyof T]?: boolean; - }; - schema: PluginConfigSchema; -} - -// @public -export type PluginConfigSchema = Type; - -// @public -export type PluginInitializer = (core: PluginInitializerContext) => Plugin; - -// @public -export interface PluginInitializerContext { - // (undocumented) - config: { - create: () => Observable; - createIfExists: () => Observable; - }; - // (undocumented) - env: { - mode: EnvironmentMode; - packageInfo: Readonly; - }; - // (undocumented) - logger: LoggerFactory; - // (undocumented) - opaqueId: PluginOpaqueId; -} - -// @public -export interface PluginManifest { - readonly configPath: ConfigPath; - readonly id: PluginName; - readonly kibanaVersion: string; - readonly optionalPlugins: readonly PluginName[]; - readonly requiredPlugins: readonly PluginName[]; - readonly server: boolean; - readonly ui: boolean; - readonly version: string; -} - -// @public -export type PluginName = string; - -// @public (undocumented) -export type PluginOpaqueId = symbol; - -// @public (undocumented) -export interface PluginsServiceSetup { - // (undocumented) - contracts: Map; - // (undocumented) - uiPlugins: { - internal: Map; - public: Map; - browserConfigs: Map>; - }; -} - -// @public (undocumented) -export interface PluginsServiceStart { - // (undocumented) - contracts: Map; -} - -// Warning: (ae-forgotten-export) The symbol "RecursiveReadonlyArray" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -export type RecursiveReadonly = T extends (...args: any[]) => any ? T : T extends any[] ? RecursiveReadonlyArray : T extends object ? Readonly<{ - [K in keyof T]: RecursiveReadonly; -}> : T; - -// @public -export type RedirectResponseOptions = HttpResponseOptions & { - headers: { - location: string; - }; -}; - -// @public -export type RequestHandler

| Type, Method extends RouteMethod = any> = (context: RequestHandlerContext, request: KibanaRequest, TypeOf, TypeOf, Method>, response: KibanaResponseFactory) => IKibanaResponse | Promise>; - -// @public -export interface RequestHandlerContext { - // (undocumented) - core: { - savedObjects: { - client: SavedObjectsClientContract; - }; - elasticsearch: { - dataClient: IScopedClusterClient; - adminClient: IScopedClusterClient; - }; - uiSettings: { - client: IUiSettingsClient; - }; - }; -} - -// @public -export type RequestHandlerContextContainer = IContextContainer>; - -// @public -export type RequestHandlerContextProvider = IContextProvider, TContextName>; - -// @public -export type ResponseError = string | Error | { - message: string | Error; - attributes?: ResponseErrorAttributes; -}; - -// @public -export type ResponseErrorAttributes = Record; - -// @public -export type ResponseHeaders = { - [header in KnownHeaders]?: string | string[]; -} & { - [header: string]: string | string[]; -}; - -// @public -export interface RouteConfig

| Type, Method extends RouteMethod> { - options?: RouteConfigOptions; - path: string; - validate: RouteSchemas | false; -} - -// @public -export interface RouteConfigOptions { - authRequired?: boolean; - body?: Method extends 'get' | 'options' ? undefined : RouteConfigOptionsBody; - tags?: readonly string[]; -} - -// @public -export interface RouteConfigOptionsBody { - accepts?: RouteContentType | RouteContentType[] | string | string[]; - maxBytes?: number; - output?: typeof validBodyOutput[number]; - parse?: boolean | 'gunzip'; -} - -// @public -export type RouteContentType = 'application/json' | 'application/*+json' | 'application/octet-stream' | 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/*'; - -// @public -export type RouteMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options'; - -// @public -export type RouteRegistrar =

| Type>(route: RouteConfig, handler: RequestHandler) => void; - -// @public -export interface RouteSchemas

| Type> { - // (undocumented) - body?: B; - // (undocumented) - params?: P; - // (undocumented) - query?: Q; -} - -// @public (undocumented) -export interface SavedObject { - attributes: T; - // (undocumented) - error?: { - message: string; - statusCode: number; - }; - id: string; - migrationVersion?: SavedObjectsMigrationVersion; - references: SavedObjectReference[]; - type: string; - updated_at?: string; - version?: string; -} - -// @public -export type SavedObjectAttribute = SavedObjectAttributeSingle | SavedObjectAttributeSingle[]; - -// @public -export interface SavedObjectAttributes { - // (undocumented) - [key: string]: SavedObjectAttribute; -} - -// @public -export type SavedObjectAttributeSingle = string | number | boolean | null | undefined | SavedObjectAttributes; - -// @public -export interface SavedObjectReference { - // (undocumented) - id: string; - // (undocumented) - name: string; - // (undocumented) - type: string; -} - -// @public (undocumented) -export interface SavedObjectsBaseOptions { - namespace?: string; -} - -// @public (undocumented) -export interface SavedObjectsBulkCreateObject { - // (undocumented) - attributes: T; - // (undocumented) - id?: string; - migrationVersion?: SavedObjectsMigrationVersion; - // (undocumented) - references?: SavedObjectReference[]; - // (undocumented) - type: string; -} - -// @public (undocumented) -export interface SavedObjectsBulkGetObject { - fields?: string[]; - // (undocumented) - id: string; - // (undocumented) - type: string; -} - -// @public (undocumented) -export interface SavedObjectsBulkResponse { - // (undocumented) - saved_objects: Array>; -} - -// @public (undocumented) -export interface SavedObjectsBulkResponse { - // (undocumented) - saved_objects: Array>; -} - -// @public (undocumented) -export interface SavedObjectsBulkUpdateObject extends Pick { - attributes: Partial; - id: string; - type: string; -} - -// @public (undocumented) -export interface SavedObjectsBulkUpdateOptions extends SavedObjectsBaseOptions { - refresh?: MutatingOperationRefreshSetting; -} - -// @public (undocumented) -export interface SavedObjectsBulkUpdateResponse { - // (undocumented) - saved_objects: Array>; -} - -// @public (undocumented) -export class SavedObjectsClient { - // @internal - constructor(repository: ISavedObjectsRepository); - bulkCreate(objects: Array>, options?: SavedObjectsCreateOptions): Promise>; - bulkGet(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise>; - bulkUpdate(objects: Array>, options?: SavedObjectsBulkUpdateOptions): Promise>; - create(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise>; - delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>; - // (undocumented) - errors: typeof SavedObjectsErrorHelpers; - // (undocumented) - static errors: typeof SavedObjectsErrorHelpers; - find(options: SavedObjectsFindOptions): Promise>; - get(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; - update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>; -} - -// @public -export type SavedObjectsClientContract = Pick; - -// @public -export type SavedObjectsClientFactory = ({ request, }: { - request: Request; -}) => SavedObjectsClientContract; - -// @public -export interface SavedObjectsClientProviderOptions { - // (undocumented) - excludedWrappers?: string[]; -} - -// @public -export type SavedObjectsClientWrapperFactory = (options: SavedObjectsClientWrapperOptions) => SavedObjectsClientContract; - -// @public -export interface SavedObjectsClientWrapperOptions { - // (undocumented) - client: SavedObjectsClientContract; - // (undocumented) - request: Request; -} - -// @public (undocumented) -export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions { - id?: string; - migrationVersion?: SavedObjectsMigrationVersion; - overwrite?: boolean; - // (undocumented) - references?: SavedObjectReference[]; - refresh?: MutatingOperationRefreshSetting; -} - -// @public (undocumented) -export interface SavedObjectsDeleteByNamespaceOptions extends SavedObjectsBaseOptions { - refresh?: MutatingOperationRefreshSetting; -} - -// @public (undocumented) -export interface SavedObjectsDeleteOptions extends SavedObjectsBaseOptions { - refresh?: MutatingOperationRefreshSetting; -} - -// @public (undocumented) -export class SavedObjectsErrorHelpers { - // (undocumented) - static createBadRequestError(reason?: string): DecoratedError; - // (undocumented) - static createEsAutoCreateIndexError(): DecoratedError; - // (undocumented) - static createGenericNotFoundError(type?: string | null, id?: string | null): DecoratedError; - // (undocumented) - static createInvalidVersionError(versionInput?: string): DecoratedError; - // (undocumented) - static createUnsupportedTypeError(type: string): DecoratedError; - // (undocumented) - static decorateBadRequestError(error: Error, reason?: string): DecoratedError; - // (undocumented) - static decorateConflictError(error: Error, reason?: string): DecoratedError; - // (undocumented) - static decorateEsUnavailableError(error: Error, reason?: string): DecoratedError; - // (undocumented) - static decorateForbiddenError(error: Error, reason?: string): DecoratedError; - // (undocumented) - static decorateGeneralError(error: Error, reason?: string): DecoratedError; - // (undocumented) - static decorateNotAuthorizedError(error: Error, reason?: string): DecoratedError; - // (undocumented) - static decorateRequestEntityTooLargeError(error: Error, reason?: string): DecoratedError; - // (undocumented) - static isBadRequestError(error: Error | DecoratedError): boolean; - // (undocumented) - static isConflictError(error: Error | DecoratedError): boolean; - // (undocumented) - static isEsAutoCreateIndexError(error: Error | DecoratedError): boolean; - // (undocumented) - static isEsUnavailableError(error: Error | DecoratedError): boolean; - // (undocumented) - static isForbiddenError(error: Error | DecoratedError): boolean; - // (undocumented) - static isInvalidVersionError(error: Error | DecoratedError): boolean; - // (undocumented) - static isNotAuthorizedError(error: Error | DecoratedError): boolean; - // (undocumented) - static isNotFoundError(error: Error | DecoratedError): boolean; - // (undocumented) - static isRequestEntityTooLargeError(error: Error | DecoratedError): boolean; - // Warning: (ae-forgotten-export) The symbol "DecoratedError" needs to be exported by the entry point index.d.ts - // - // (undocumented) - static isSavedObjectsClientError(error: any): error is DecoratedError; -} - -// @public -export interface SavedObjectsExportOptions { - excludeExportDetails?: boolean; - exportSizeLimit: number; - includeReferencesDeep?: boolean; - namespace?: string; - objects?: Array<{ - id: string; - type: string; - }>; - savedObjectsClient: SavedObjectsClientContract; - search?: string; - types?: string[]; -} - -// @public -export interface SavedObjectsExportResultDetails { - exportedCount: number; - missingRefCount: number; - missingReferences: Array<{ - id: string; - type: string; - }>; -} - -// @public (undocumented) -export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions { - // (undocumented) - defaultSearchOperator?: 'AND' | 'OR'; - fields?: string[]; - // (undocumented) - filter?: string; - // (undocumented) - hasReference?: { - type: string; - id: string; - }; - // (undocumented) - page?: number; - // (undocumented) - perPage?: number; - search?: string; - searchFields?: string[]; - // (undocumented) - sortField?: string; - // (undocumented) - sortOrder?: string; - // (undocumented) - type: string | string[]; -} - -// @public -export interface SavedObjectsFindResponse { - // (undocumented) - page: number; - // (undocumented) - per_page: number; - // (undocumented) - saved_objects: Array>; - // (undocumented) - total: number; -} - -// @public -export interface SavedObjectsImportConflictError { - // (undocumented) - type: 'conflict'; -} - -// @public -export interface SavedObjectsImportError { - // (undocumented) - error: SavedObjectsImportConflictError | SavedObjectsImportUnsupportedTypeError | SavedObjectsImportMissingReferencesError | SavedObjectsImportUnknownError; - // (undocumented) - id: string; - // (undocumented) - title?: string; - // (undocumented) - type: string; -} - -// @public -export interface SavedObjectsImportMissingReferencesError { - // (undocumented) - blocking: Array<{ - type: string; - id: string; - }>; - // (undocumented) - references: Array<{ - type: string; - id: string; - }>; - // (undocumented) - type: 'missing_references'; -} - -// @public -export interface SavedObjectsImportOptions { - // (undocumented) - namespace?: string; - // (undocumented) - objectLimit: number; - // (undocumented) - overwrite: boolean; - // (undocumented) - readStream: Readable; - // (undocumented) - savedObjectsClient: SavedObjectsClientContract; - // (undocumented) - supportedTypes: string[]; -} - -// @public -export interface SavedObjectsImportResponse { - // (undocumented) - errors?: SavedObjectsImportError[]; - // (undocumented) - success: boolean; - // (undocumented) - successCount: number; -} - -// @public -export interface SavedObjectsImportRetry { - // (undocumented) - id: string; - // (undocumented) - overwrite: boolean; - // (undocumented) - replaceReferences: Array<{ - type: string; - from: string; - to: string; - }>; - // (undocumented) - type: string; -} - -// @public -export interface SavedObjectsImportUnknownError { - // (undocumented) - message: string; - // (undocumented) - statusCode: number; - // (undocumented) - type: 'unknown'; -} - -// @public -export interface SavedObjectsImportUnsupportedTypeError { - // (undocumented) - type: 'unsupported_type'; -} - -// @public (undocumented) -export interface SavedObjectsIncrementCounterOptions extends SavedObjectsBaseOptions { - // (undocumented) - migrationVersion?: SavedObjectsMigrationVersion; - refresh?: MutatingOperationRefreshSetting; -} - -// @internal @deprecated (undocumented) -export interface SavedObjectsLegacyService { - // Warning: (ae-forgotten-export) The symbol "SavedObjectsClientProvider" needs to be exported by the entry point index.d.ts - // - // (undocumented) - addScopedSavedObjectsClientWrapperFactory: SavedObjectsClientProvider['addClientWrapperFactory']; - // (undocumented) - getSavedObjectsRepository(...rest: any[]): any; - // (undocumented) - getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient']; - // (undocumented) - importExport: { - objectLimit: number; - importSavedObjects(options: SavedObjectsImportOptions): Promise; - resolveImportErrors(options: SavedObjectsResolveImportErrorsOptions): Promise; - getSortedObjectsForExport(options: SavedObjectsExportOptions): Promise; - }; - // (undocumented) - SavedObjectsClient: typeof SavedObjectsClient; - // (undocumented) - schema: SavedObjectsSchema; - // (undocumented) - setScopedSavedObjectsClientFactory: SavedObjectsClientProvider['setClientFactory']; - // (undocumented) - types: string[]; -} - -// @public (undocumented) -export interface SavedObjectsMigrationLogger { - // (undocumented) - debug: (msg: string) => void; - // (undocumented) - info: (msg: string) => void; - // (undocumented) - warning: (msg: string) => void; -} - -// @public -export interface SavedObjectsMigrationVersion { - // (undocumented) - [pluginName: string]: string; -} - -// Warning: (ae-missing-release-tag) "RawDoc" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public -export interface SavedObjectsRawDoc { - // (undocumented) - _id: string; - // (undocumented) - _primary_term?: number; - // (undocumented) - _seq_no?: number; - // (undocumented) - _source: any; - // (undocumented) - _type?: string; -} - -// @public (undocumented) -export class SavedObjectsRepository { - // Warning: (ae-forgotten-export) The symbol "SavedObjectsRepositoryOptions" needs to be exported by the entry point index.d.ts - // - // @internal - constructor(options: SavedObjectsRepositoryOptions); - bulkCreate(objects: Array>, options?: SavedObjectsCreateOptions): Promise>; - bulkGet(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise>; - bulkUpdate(objects: Array>, options?: SavedObjectsBulkUpdateOptions): Promise>; - create(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise>; - delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>; - deleteByNamespace(namespace: string, options?: SavedObjectsDeleteByNamespaceOptions): Promise; - // (undocumented) - find({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, }: SavedObjectsFindOptions): Promise>; - get(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; - incrementCounter(type: string, id: string, counterFieldName: string, options?: SavedObjectsIncrementCounterOptions): Promise<{ - id: string; - type: string; - updated_at: string; - references: any; - version: string; - attributes: any; - }>; - update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>; - } - -// @public -export interface SavedObjectsResolveImportErrorsOptions { - // (undocumented) - namespace?: string; - // (undocumented) - objectLimit: number; - // (undocumented) - readStream: Readable; - // (undocumented) - retries: SavedObjectsImportRetry[]; - // (undocumented) - savedObjectsClient: SavedObjectsClientContract; - // (undocumented) - supportedTypes: string[]; -} - -// @internal (undocumented) -export class SavedObjectsSchema { - // Warning: (ae-forgotten-export) The symbol "SavedObjectsSchemaDefinition" needs to be exported by the entry point index.d.ts - constructor(schemaDefinition?: SavedObjectsSchemaDefinition); - // (undocumented) - getConvertToAliasScript(type: string): string | undefined; - // (undocumented) - getIndexForType(config: Config, type: string): string | undefined; - // (undocumented) - isHiddenType(type: string): boolean; - // (undocumented) - isNamespaceAgnostic(type: string): boolean; -} - -// @internal (undocumented) -export class SavedObjectsSerializer { - constructor(schema: SavedObjectsSchema); - generateRawId(namespace: string | undefined, type: string, id?: string): string; - isRawSavedObject(rawDoc: SavedObjectsRawDoc): any; - // Warning: (ae-forgotten-export) The symbol "SanitizedSavedObjectDoc" needs to be exported by the entry point index.d.ts - rawToSavedObject(doc: SavedObjectsRawDoc): SanitizedSavedObjectDoc; - savedObjectToRaw(savedObj: SanitizedSavedObjectDoc): SavedObjectsRawDoc; - } - -// @public -export interface SavedObjectsServiceSetup { - addClientWrapper: (priority: number, id: string, factory: SavedObjectsClientWrapperFactory) => void; - createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository; - createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository; - setClientFactory: (customClientFactory: SavedObjectsClientFactory) => void; -} - -// @public -export interface SavedObjectsServiceStart { - getScopedClient: (req: KibanaRequest, options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; -} - -// @public (undocumented) -export interface SavedObjectsUpdateOptions extends SavedObjectsBaseOptions { - references?: SavedObjectReference[]; - refresh?: MutatingOperationRefreshSetting; - version?: string; -} - -// @public (undocumented) -export interface SavedObjectsUpdateResponse extends Omit, 'attributes' | 'references'> { - // (undocumented) - attributes: Partial; - // (undocumented) - references: SavedObjectReference[] | undefined; -} - -// @public -export class ScopedClusterClient implements IScopedClusterClient { - constructor(internalAPICaller: APICaller, scopedAPICaller: APICaller, headers?: Headers | undefined); - callAsCurrentUser(endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; - callAsInternalUser(endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; - } - -// @public -export interface SessionCookieValidationResult { - isValid: boolean; - path?: string; -} - -// @public -export interface SessionStorage { - clear(): void; - get(): Promise; - set(sessionValue: T): void; -} - -// @public -export interface SessionStorageCookieOptions { - encryptionKey: string; - isSecure: boolean; - name: string; - validate: (sessionValue: T | T[]) => SessionCookieValidationResult; -} - -// @public -export interface SessionStorageFactory { - // (undocumented) - asScoped: (request: KibanaRequest) => SessionStorage; -} - -// @public -export interface UiSettingsParams { - category?: string[]; - description?: string; - name?: string; - optionLabels?: Record; - options?: string[]; - readonly?: boolean; - requiresPageReload?: boolean; - type?: UiSettingsType; - value?: SavedObjectAttribute; -} - -// @public (undocumented) -export interface UiSettingsServiceSetup { - register(settings: Record): void; -} - -// @public -export type UiSettingsType = 'json' | 'markdown' | 'number' | 'select' | 'boolean' | 'string'; - -// @public -export interface UserProvidedValues { - // (undocumented) - isOverridden?: boolean; - // (undocumented) - userValue?: T; -} - -// @public -export const validBodyOutput: readonly ["data", "stream"]; - - -// Warnings were encountered during analysis: -// -// src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/plugins_service.ts:43:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts - -``` +## API Report File for "kibana" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import Boom from 'boom'; +import { BulkIndexDocumentsParams } from 'elasticsearch'; +import { CatAliasesParams } from 'elasticsearch'; +import { CatAllocationParams } from 'elasticsearch'; +import { CatCommonParams } from 'elasticsearch'; +import { CatFielddataParams } from 'elasticsearch'; +import { CatHealthParams } from 'elasticsearch'; +import { CatHelpParams } from 'elasticsearch'; +import { CatIndicesParams } from 'elasticsearch'; +import { CatRecoveryParams } from 'elasticsearch'; +import { CatSegmentsParams } from 'elasticsearch'; +import { CatShardsParams } from 'elasticsearch'; +import { CatSnapshotsParams } from 'elasticsearch'; +import { CatTasksParams } from 'elasticsearch'; +import { CatThreadPoolParams } from 'elasticsearch'; +import { ClearScrollParams } from 'elasticsearch'; +import { Client } from 'elasticsearch'; +import { ClusterAllocationExplainParams } from 'elasticsearch'; +import { ClusterGetSettingsParams } from 'elasticsearch'; +import { ClusterHealthParams } from 'elasticsearch'; +import { ClusterPendingTasksParams } from 'elasticsearch'; +import { ClusterPutSettingsParams } from 'elasticsearch'; +import { ClusterRerouteParams } from 'elasticsearch'; +import { ClusterStateParams } from 'elasticsearch'; +import { ClusterStatsParams } from 'elasticsearch'; +import { ConfigOptions } from 'elasticsearch'; +import { CountParams } from 'elasticsearch'; +import { CreateDocumentParams } from 'elasticsearch'; +import { DeleteDocumentByQueryParams } from 'elasticsearch'; +import { DeleteDocumentParams } from 'elasticsearch'; +import { DeleteScriptParams } from 'elasticsearch'; +import { DeleteTemplateParams } from 'elasticsearch'; +import { DetailedPeerCertificate } from 'tls'; +import { Duration } from 'moment'; +import { ExistsParams } from 'elasticsearch'; +import { ExplainParams } from 'elasticsearch'; +import { FieldStatsParams } from 'elasticsearch'; +import { GenericParams } from 'elasticsearch'; +import { GetParams } from 'elasticsearch'; +import { GetResponse } from 'elasticsearch'; +import { GetScriptParams } from 'elasticsearch'; +import { GetSourceParams } from 'elasticsearch'; +import { GetTemplateParams } from 'elasticsearch'; +import { IncomingHttpHeaders } from 'http'; +import { IndexDocumentParams } from 'elasticsearch'; +import { IndicesAnalyzeParams } from 'elasticsearch'; +import { IndicesClearCacheParams } from 'elasticsearch'; +import { IndicesCloseParams } from 'elasticsearch'; +import { IndicesCreateParams } from 'elasticsearch'; +import { IndicesDeleteAliasParams } from 'elasticsearch'; +import { IndicesDeleteParams } from 'elasticsearch'; +import { IndicesDeleteTemplateParams } from 'elasticsearch'; +import { IndicesExistsAliasParams } from 'elasticsearch'; +import { IndicesExistsParams } from 'elasticsearch'; +import { IndicesExistsTemplateParams } from 'elasticsearch'; +import { IndicesExistsTypeParams } from 'elasticsearch'; +import { IndicesFlushParams } from 'elasticsearch'; +import { IndicesFlushSyncedParams } from 'elasticsearch'; +import { IndicesForcemergeParams } from 'elasticsearch'; +import { IndicesGetAliasParams } from 'elasticsearch'; +import { IndicesGetFieldMappingParams } from 'elasticsearch'; +import { IndicesGetMappingParams } from 'elasticsearch'; +import { IndicesGetParams } from 'elasticsearch'; +import { IndicesGetSettingsParams } from 'elasticsearch'; +import { IndicesGetTemplateParams } from 'elasticsearch'; +import { IndicesGetUpgradeParams } from 'elasticsearch'; +import { IndicesOpenParams } from 'elasticsearch'; +import { IndicesPutAliasParams } from 'elasticsearch'; +import { IndicesPutMappingParams } from 'elasticsearch'; +import { IndicesPutSettingsParams } from 'elasticsearch'; +import { IndicesPutTemplateParams } from 'elasticsearch'; +import { IndicesRecoveryParams } from 'elasticsearch'; +import { IndicesRefreshParams } from 'elasticsearch'; +import { IndicesRolloverParams } from 'elasticsearch'; +import { IndicesSegmentsParams } from 'elasticsearch'; +import { IndicesShardStoresParams } from 'elasticsearch'; +import { IndicesShrinkParams } from 'elasticsearch'; +import { IndicesStatsParams } from 'elasticsearch'; +import { IndicesUpdateAliasesParams } from 'elasticsearch'; +import { IndicesUpgradeParams } from 'elasticsearch'; +import { IndicesValidateQueryParams } from 'elasticsearch'; +import { InfoParams } from 'elasticsearch'; +import { IngestDeletePipelineParams } from 'elasticsearch'; +import { IngestGetPipelineParams } from 'elasticsearch'; +import { IngestPutPipelineParams } from 'elasticsearch'; +import { IngestSimulateParams } from 'elasticsearch'; +import { KibanaConfigType } from 'src/core/server/kibana_config'; +import { Logger as Logger_2 } from 'src/core/server/logging'; +import { MGetParams } from 'elasticsearch'; +import { MGetResponse } from 'elasticsearch'; +import { MSearchParams } from 'elasticsearch'; +import { MSearchResponse } from 'elasticsearch'; +import { MSearchTemplateParams } from 'elasticsearch'; +import { MTermVectorsParams } from 'elasticsearch'; +import { NodesHotThreadsParams } from 'elasticsearch'; +import { NodesInfoParams } from 'elasticsearch'; +import { NodesStatsParams } from 'elasticsearch'; +import { ObjectType } from '@kbn/config-schema'; +import { Observable } from 'rxjs'; +import { PeerCertificate } from 'tls'; +import { PingParams } from 'elasticsearch'; +import { PutScriptParams } from 'elasticsearch'; +import { PutTemplateParams } from 'elasticsearch'; +import { Readable } from 'stream'; +import { ReindexParams } from 'elasticsearch'; +import { ReindexRethrottleParams } from 'elasticsearch'; +import { RenderSearchTemplateParams } from 'elasticsearch'; +import { Request } from 'hapi'; +import { ResponseObject } from 'hapi'; +import { ResponseToolkit } from 'hapi'; +import { ScrollParams } from 'elasticsearch'; +import { SearchParams } from 'elasticsearch'; +import { SearchResponse } from 'elasticsearch'; +import { SearchShardsParams } from 'elasticsearch'; +import { SearchTemplateParams } from 'elasticsearch'; +import { Server } from 'hapi'; +import { ShallowPromise } from '@kbn/utility-types'; +import { SnapshotCreateParams } from 'elasticsearch'; +import { SnapshotCreateRepositoryParams } from 'elasticsearch'; +import { SnapshotDeleteParams } from 'elasticsearch'; +import { SnapshotDeleteRepositoryParams } from 'elasticsearch'; +import { SnapshotGetParams } from 'elasticsearch'; +import { SnapshotGetRepositoryParams } from 'elasticsearch'; +import { SnapshotRestoreParams } from 'elasticsearch'; +import { SnapshotStatusParams } from 'elasticsearch'; +import { SnapshotVerifyRepositoryParams } from 'elasticsearch'; +import { Stream } from 'stream'; +import { SuggestParams } from 'elasticsearch'; +import { TasksCancelParams } from 'elasticsearch'; +import { TasksGetParams } from 'elasticsearch'; +import { TasksListParams } from 'elasticsearch'; +import { TermvectorsParams } from 'elasticsearch'; +import { Type } from '@kbn/config-schema'; +import { TypeOf } from '@kbn/config-schema'; +import { UpdateDocumentByQueryParams } from 'elasticsearch'; +import { UpdateDocumentParams } from 'elasticsearch'; +import { Url } from 'url'; + +// @public (undocumented) +export interface APICaller { + // (undocumented) + (endpoint: 'cluster.state', params: ClusterStateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'bulk', params: BulkIndexDocumentsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'count', params: CountParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'create', params: CreateDocumentParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'delete', params: DeleteDocumentParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'deleteByQuery', params: DeleteDocumentByQueryParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'deleteScript', params: DeleteScriptParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'deleteTemplate', params: DeleteTemplateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'exists', params: ExistsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'explain', params: ExplainParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'fieldStats', params: FieldStatsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'get', params: GetParams, options?: CallAPIOptions): Promise>; + // (undocumented) + (endpoint: 'getScript', params: GetScriptParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'getSource', params: GetSourceParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'getTemplate', params: GetTemplateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'index', params: IndexDocumentParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'info', params: InfoParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'mget', params: MGetParams, options?: CallAPIOptions): Promise>; + // (undocumented) + (endpoint: 'msearch', params: MSearchParams, options?: CallAPIOptions): Promise>; + // (undocumented) + (endpoint: 'msearchTemplate', params: MSearchTemplateParams, options?: CallAPIOptions): Promise>; + // (undocumented) + (endpoint: 'mtermvectors', params: MTermVectorsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'ping', params: PingParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'putScript', params: PutScriptParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'putTemplate', params: PutTemplateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'reindex', params: ReindexParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'reindexRethrottle', params: ReindexRethrottleParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'renderSearchTemplate', params: RenderSearchTemplateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'scroll', params: ScrollParams, options?: CallAPIOptions): Promise>; + // (undocumented) + (endpoint: 'search', params: SearchParams, options?: CallAPIOptions): Promise>; + // (undocumented) + (endpoint: 'searchShards', params: SearchShardsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'searchTemplate', params: SearchTemplateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'suggest', params: SuggestParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'termvectors', params: TermvectorsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'update', params: UpdateDocumentParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'updateByQuery', params: UpdateDocumentByQueryParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.aliases', params: CatAliasesParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.allocation', params: CatAllocationParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.count', params: CatAllocationParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.fielddata', params: CatFielddataParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.health', params: CatHealthParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.help', params: CatHelpParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.indices', params: CatIndicesParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.master', params: CatCommonParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.nodeattrs', params: CatCommonParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.nodes', params: CatCommonParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.pendingTasks', params: CatCommonParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.plugins', params: CatCommonParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.recovery', params: CatRecoveryParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.repositories', params: CatCommonParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.segments', params: CatSegmentsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.shards', params: CatShardsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.snapshots', params: CatSnapshotsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.tasks', params: CatTasksParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cat.threadPool', params: CatThreadPoolParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cluster.allocationExplain', params: ClusterAllocationExplainParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cluster.getSettings', params: ClusterGetSettingsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cluster.health', params: ClusterHealthParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cluster.pendingTasks', params: ClusterPendingTasksParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cluster.putSettings', params: ClusterPutSettingsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cluster.reroute', params: ClusterRerouteParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'clearScroll', params: ClearScrollParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'cluster.stats', params: ClusterStatsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.analyze', params: IndicesAnalyzeParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.clearCache', params: IndicesClearCacheParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.close', params: IndicesCloseParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.create', params: IndicesCreateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.delete', params: IndicesDeleteParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.deleteAlias', params: IndicesDeleteAliasParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.deleteTemplate', params: IndicesDeleteTemplateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.exists', params: IndicesExistsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.existsAlias', params: IndicesExistsAliasParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.existsTemplate', params: IndicesExistsTemplateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.existsType', params: IndicesExistsTypeParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.flush', params: IndicesFlushParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.flushSynced', params: IndicesFlushSyncedParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.forcemerge', params: IndicesForcemergeParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.get', params: IndicesGetParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.getAlias', params: IndicesGetAliasParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.getFieldMapping', params: IndicesGetFieldMappingParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.getMapping', params: IndicesGetMappingParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.getSettings', params: IndicesGetSettingsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.getTemplate', params: IndicesGetTemplateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.getUpgrade', params: IndicesGetUpgradeParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.open', params: IndicesOpenParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.putAlias', params: IndicesPutAliasParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.putMapping', params: IndicesPutMappingParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.putSettings', params: IndicesPutSettingsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.putTemplate', params: IndicesPutTemplateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.recovery', params: IndicesRecoveryParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.refresh', params: IndicesRefreshParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.rollover', params: IndicesRolloverParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.segments', params: IndicesSegmentsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.shardStores', params: IndicesShardStoresParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.shrink', params: IndicesShrinkParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.stats', params: IndicesStatsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.updateAliases', params: IndicesUpdateAliasesParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.upgrade', params: IndicesUpgradeParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'indices.validateQuery', params: IndicesValidateQueryParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'ingest.deletePipeline', params: IngestDeletePipelineParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'ingest.getPipeline', params: IngestGetPipelineParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'ingest.putPipeline', params: IngestPutPipelineParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'ingest.simulate', params: IngestSimulateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'nodes.hotThreads', params: NodesHotThreadsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'nodes.info', params: NodesInfoParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'nodes.stats', params: NodesStatsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'snapshot.create', params: SnapshotCreateParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'snapshot.createRepository', params: SnapshotCreateRepositoryParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'snapshot.delete', params: SnapshotDeleteParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'snapshot.deleteRepository', params: SnapshotDeleteRepositoryParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'snapshot.get', params: SnapshotGetParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'snapshot.getRepository', params: SnapshotGetRepositoryParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'snapshot.restore', params: SnapshotRestoreParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'snapshot.status', params: SnapshotStatusParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'snapshot.verifyRepository', params: SnapshotVerifyRepositoryParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'tasks.cancel', params: TasksCancelParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'tasks.get', params: TasksGetParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'tasks.list', params: TasksListParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'transport.request', clientParams: AssistantAPIClientParams, options?: CallAPIOptions): Promise; + // (undocumented) + (endpoint: 'transport.request', clientParams: DeprecationAPIClientParams, options?: CallAPIOptions): Promise; + // (undocumented) + (endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; +} + +// @public (undocumented) +export interface AssistanceAPIResponse { + // (undocumented) + indices: { + [indexName: string]: { + action_required: MIGRATION_ASSISTANCE_INDEX_ACTION; + }; + }; +} + +// @public (undocumented) +export interface AssistantAPIClientParams extends GenericParams { + // (undocumented) + method: 'GET'; + // (undocumented) + path: '/_migration/assistance'; +} + +// @public (undocumented) +export interface Authenticated extends AuthResultParams { + // (undocumented) + type: AuthResultType.authenticated; +} + +// @public +export type AuthenticationHandler = (request: KibanaRequest, response: LifecycleResponseFactory, toolkit: AuthToolkit) => AuthResult | IKibanaResponse | Promise; + +// @public +export type AuthHeaders = Record; + +// @public (undocumented) +export type AuthResult = Authenticated; + +// @public +export interface AuthResultParams { + requestHeaders?: AuthHeaders; + responseHeaders?: AuthHeaders; + state?: Record; +} + +// @public (undocumented) +export enum AuthResultType { + // (undocumented) + authenticated = "authenticated" +} + +// @public +export enum AuthStatus { + authenticated = "authenticated", + unauthenticated = "unauthenticated", + unknown = "unknown" +} + +// @public +export interface AuthToolkit { + authenticated: (data?: AuthResultParams) => AuthResult; +} + +// @public +export class BasePath { + // @internal + constructor(serverBasePath?: string); + get: (request: KibanaRequest | LegacyRequest) => string; + prepend: (path: string) => string; + remove: (path: string) => string; + readonly serverBasePath: string; + set: (request: KibanaRequest | LegacyRequest, requestSpecificBasePath: string) => void; +} + +// Warning: (ae-forgotten-export) The symbol "BootstrapArgs" needs to be exported by the entry point index.d.ts +// +// @internal (undocumented) +export function bootstrap({ configs, cliArgs, applyConfigOverrides, features, }: BootstrapArgs): Promise; + +// @public +export interface CallAPIOptions { + signal?: AbortSignal; + wrap401Errors?: boolean; +} + +// @public +export interface Capabilities { + [key: string]: Record>; + catalogue: Record; + management: { + [sectionId: string]: Record; + }; + navLinks: Record; +} + +// @public +export type CapabilitiesProvider = () => Partial; + +// @public +export interface CapabilitiesSetup { + registerProvider(provider: CapabilitiesProvider): void; + registerSwitcher(switcher: CapabilitiesSwitcher): void; +} + +// @public +export interface CapabilitiesStart { + resolveCapabilities(request: KibanaRequest): Promise; +} + +// @public +export type CapabilitiesSwitcher = (request: KibanaRequest, uiCapabilities: Capabilities) => Partial | Promise>; + +// @public +export class ClusterClient implements IClusterClient { + constructor(config: ElasticsearchClientConfig, log: Logger, getAuthHeaders?: GetAuthHeaders); + asScoped(request?: KibanaRequest | LegacyRequest | FakeRequest): IScopedClusterClient; + callAsInternalUser: APICaller; + close(): void; + } + +// @public (undocumented) +export type ConfigPath = string | string[]; + +// @internal (undocumented) +export class ConfigService { + // Warning: (ae-forgotten-export) The symbol "Config" 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(config$: Observable, env: Env, logger: LoggerFactory); + atPath(path: ConfigPath): Observable; + getConfig$(): Observable; + // (undocumented) + getUnusedPaths(): Promise; + // (undocumented) + getUsedPaths(): Promise; + // (undocumented) + isEnabledAtPath(path: ConfigPath): Promise; + optionalAtPath(path: ConfigPath): Observable; + setSchema(path: ConfigPath, schema: Type): Promise; + } + +// @public +export interface ContextSetup { + createContextContainer>(): IContextContainer; +} + +// @internal (undocumented) +export type CoreId = symbol; + +// @public +export interface CoreSetup { + // (undocumented) + capabilities: CapabilitiesSetup; + // (undocumented) + context: ContextSetup; + // (undocumented) + elasticsearch: ElasticsearchServiceSetup; + // (undocumented) + http: HttpServiceSetup; + // (undocumented) + savedObjects: SavedObjectsServiceSetup; + // (undocumented) + uiSettings: UiSettingsServiceSetup; +} + +// @public +export interface CoreStart { + // (undocumented) + capabilities: CapabilitiesStart; + // (undocumented) + savedObjects: SavedObjectsServiceStart; +} + +// @public +export interface CustomHttpResponseOptions { + body?: T; + headers?: ResponseHeaders; + // (undocumented) + statusCode: number; +} + +// @public (undocumented) +export interface DeprecationAPIClientParams extends GenericParams { + // (undocumented) + method: 'GET'; + // (undocumented) + path: '/_migration/deprecations'; +} + +// @public (undocumented) +export interface DeprecationAPIResponse { + // (undocumented) + cluster_settings: DeprecationInfo[]; + // (undocumented) + index_settings: IndexSettingsDeprecationInfo; + // (undocumented) + ml_settings: DeprecationInfo[]; + // (undocumented) + node_settings: DeprecationInfo[]; +} + +// @public (undocumented) +export interface DeprecationInfo { + // (undocumented) + details?: string; + // (undocumented) + level: MIGRATION_DEPRECATION_LEVEL; + // (undocumented) + message: string; + // (undocumented) + url: string; +} + +// @public +export interface DiscoveredPlugin { + readonly configPath: ConfigPath; + readonly id: PluginName; + readonly optionalPlugins: readonly PluginName[]; + readonly requiredPlugins: readonly PluginName[]; +} + +// Warning: (ae-forgotten-export) The symbol "ElasticsearchConfig" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export type ElasticsearchClientConfig = Pick & Pick & { + pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout']; + requestTimeout?: ElasticsearchConfig['requestTimeout'] | ConfigOptions['requestTimeout']; + sniffInterval?: ElasticsearchConfig['sniffInterval'] | ConfigOptions['sniffInterval']; + ssl?: Partial; +}; + +// @public (undocumented) +export interface ElasticsearchError extends Boom { + // (undocumented) + [code]?: string; +} + +// @public +export class ElasticsearchErrorHelpers { + // (undocumented) + static decorateNotAuthorizedError(error: Error, reason?: string): ElasticsearchError; + // (undocumented) + static isNotAuthorizedError(error: any): error is ElasticsearchError; +} + +// @public (undocumented) +export interface ElasticsearchServiceSetup { + readonly adminClient$: Observable; + readonly createClient: (type: string, clientConfig?: Partial) => IClusterClient; + readonly dataClient$: Observable; +} + +// @public (undocumented) +export interface EnvironmentMode { + // (undocumented) + dev: boolean; + // (undocumented) + name: 'development' | 'production'; + // (undocumented) + prod: boolean; +} + +// @public +export interface ErrorHttpResponseOptions { + body?: ResponseError; + headers?: ResponseHeaders; +} + +// @public +export interface FakeRequest { + headers: Headers; +} + +// @public +export type GetAuthHeaders = (request: KibanaRequest | LegacyRequest) => AuthHeaders | undefined; + +// @public +export type GetAuthState = (request: KibanaRequest | LegacyRequest) => { + status: AuthStatus; + state: unknown; +}; + +// @public +export type HandlerContextType> = T extends HandlerFunction ? U : never; + +// @public +export type HandlerFunction = (context: T, ...args: any[]) => any; + +// @public +export type HandlerParameters> = T extends (context: any, ...args: infer U) => any ? U : never; + +// @public +export type Headers = { + [header in KnownHeaders]?: string | string[] | undefined; +} & { + [header: string]: string | string[] | undefined; +}; + +// @public +export interface HttpResponseOptions { + body?: HttpResponsePayload; + headers?: ResponseHeaders; +} + +// @public +export type HttpResponsePayload = undefined | string | Record | Buffer | Stream; + +// @public +export interface HttpServiceSetup { + basePath: IBasePath; + createCookieSessionStorageFactory: (cookieOptions: SessionStorageCookieOptions) => Promise>; + createRouter: () => IRouter; + isTlsEnabled: boolean; + registerAuth: (handler: AuthenticationHandler) => void; + registerOnPostAuth: (handler: OnPostAuthHandler) => void; + registerOnPreAuth: (handler: OnPreAuthHandler) => void; + registerRouteHandlerContext: (contextName: T, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; +} + +// @public (undocumented) +export interface HttpServiceStart { + isListening: (port: number) => boolean; +} + +// @public +export type IBasePath = Pick; + +// @public +export type IClusterClient = Pick; + +// @public +export interface IContextContainer> { + createHandler(pluginOpaqueId: PluginOpaqueId, handler: THandler): (...rest: HandlerParameters) => ShallowPromise>; + registerContext>(pluginOpaqueId: PluginOpaqueId, contextName: TContextName, provider: IContextProvider): this; +} + +// @public +export type IContextProvider, TContextName extends keyof HandlerContextType> = (context: Partial>, ...rest: HandlerParameters) => Promise[TContextName]> | HandlerContextType[TContextName]; + +// @public +export interface IKibanaResponse { + // (undocumented) + readonly options: HttpResponseOptions; + // (undocumented) + readonly payload?: T; + // (undocumented) + readonly status: number; +} + +// @public +export interface IKibanaSocket { + readonly authorizationError?: Error; + readonly authorized?: boolean; + // (undocumented) + getPeerCertificate(detailed: true): DetailedPeerCertificate | null; + // (undocumented) + getPeerCertificate(detailed: false): PeerCertificate | null; + getPeerCertificate(detailed?: boolean): PeerCertificate | DetailedPeerCertificate | null; +} + +// @public (undocumented) +export interface IndexSettingsDeprecationInfo { + // (undocumented) + [indexName: string]: DeprecationInfo[]; +} + +// @public +export interface IRouter { + delete: RouteRegistrar<'delete'>; + get: RouteRegistrar<'get'>; + // Warning: (ae-forgotten-export) The symbol "RouterRoute" needs to be exported by the entry point index.d.ts + // + // @internal + getRoutes: () => RouterRoute[]; + handleLegacyErrors:

(handler: RequestHandler) => RequestHandler; + patch: RouteRegistrar<'patch'>; + post: RouteRegistrar<'post'>; + put: RouteRegistrar<'put'>; + routerPath: string; +} + +// @public +export type IsAuthenticated = (request: KibanaRequest | LegacyRequest) => boolean; + +// @public +export type ISavedObjectsRepository = Pick; + +// @public +export type IScopedClusterClient = Pick; + +// @public +export interface IUiSettingsClient { + get: (key: string) => Promise; + getAll: () => Promise>; + getRegistered: () => Readonly>; + getUserProvided: () => Promise>>; + isOverridden: (key: string) => boolean; + remove: (key: string) => Promise; + removeMany: (keys: string[]) => Promise; + set: (key: string, value: any) => Promise; + setMany: (changes: Record) => Promise; +} + +// @public +export class KibanaRequest { + // @internal (undocumented) + protected readonly [requestSymbol]: Request; + constructor(request: Request, params: Params, query: Query, body: Body, withoutSecretHeaders: boolean); + // (undocumented) + readonly body: Body; + // @internal + static from

| Type>(req: Request, routeSchemas?: RouteSchemas, withoutSecretHeaders?: boolean): KibanaRequest; + readonly headers: Headers; + // (undocumented) + readonly params: Params; + // (undocumented) + readonly query: Query; + readonly route: RecursiveReadonly>; + // (undocumented) + readonly socket: IKibanaSocket; + readonly url: Url; + } + +// @public +export interface KibanaRequestRoute { + // (undocumented) + method: Method; + // (undocumented) + options: KibanaRequestRouteOptions; + // (undocumented) + path: string; +} + +// @public +export type KibanaRequestRouteOptions = Method extends 'get' | 'options' ? Required, 'body'>> : Required>; + +// @public +export type KibanaResponseFactory = typeof kibanaResponseFactory; + +// @public +export const kibanaResponseFactory: { + custom: | Buffer | Stream | { + message: string | Error; + attributes?: Record | undefined; + } | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; + badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse; + unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse; + forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse; + notFound: (options?: ErrorHttpResponseOptions) => KibanaResponse; + conflict: (options?: ErrorHttpResponseOptions) => KibanaResponse; + internalError: (options?: ErrorHttpResponseOptions) => KibanaResponse; + customError: (options: CustomHttpResponseOptions) => KibanaResponse; + redirected: (options: RedirectResponseOptions) => KibanaResponse | Buffer | Stream>; + ok: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; + accepted: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; + noContent: (options?: HttpResponseOptions) => KibanaResponse; +}; + +// Warning: (ae-forgotten-export) The symbol "KnownKeys" needs to be exported by the entry point index.d.ts +// +// @public +export type KnownHeaders = KnownKeys; + +// @public @deprecated (undocumented) +export interface LegacyRequest extends Request { +} + +// @public @deprecated (undocumented) +export interface LegacyServiceSetupDeps { + // Warning: (ae-forgotten-export) The symbol "InternalCoreSetup" needs to be exported by the entry point index.d.ts + // + // (undocumented) + core: InternalCoreSetup & { + plugins: PluginsServiceSetup; + }; + // (undocumented) + plugins: Record; +} + +// @public @deprecated (undocumented) +export interface LegacyServiceStartDeps { + // Warning: (ae-forgotten-export) The symbol "InternalCoreStart" needs to be exported by the entry point index.d.ts + // + // (undocumented) + core: InternalCoreStart & { + plugins: PluginsServiceStart; + }; + // (undocumented) + plugins: Record; +} + +// Warning: (ae-forgotten-export) The symbol "lifecycleResponseFactory" needs to be exported by the entry point index.d.ts +// +// @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; + 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; +} + +// @public +export interface LoggerFactory { + get(...contextParts: string[]): Logger; +} + +// @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; +} + +// @public +export interface LogMeta { + // (undocumented) + [key: string]: any; +} + +// @internal +export interface LogRecord { + // (undocumented) + context: string; + // (undocumented) + error?: Error; + // (undocumented) + level: LogLevel; + // (undocumented) + message: string; + // (undocumented) + meta?: { + [name: string]: any; + }; + // (undocumented) + timestamp: Date; +} + +// @public (undocumented) +export type MIGRATION_ASSISTANCE_INDEX_ACTION = 'upgrade' | 'reindex'; + +// @public (undocumented) +export type MIGRATION_DEPRECATION_LEVEL = 'none' | 'info' | 'warning' | 'critical'; + +// @public +export type MutatingOperationRefreshSetting = boolean | 'wait_for'; + +// Warning: (ae-forgotten-export) The symbol "OnPostAuthResult" needs to be exported by the entry point index.d.ts +// +// @public +export type OnPostAuthHandler = (request: KibanaRequest, response: LifecycleResponseFactory, toolkit: OnPostAuthToolkit) => OnPostAuthResult | KibanaResponse | Promise; + +// @public +export interface OnPostAuthToolkit { + next: () => OnPostAuthResult; +} + +// Warning: (ae-forgotten-export) The symbol "OnPreAuthResult" needs to be exported by the entry point index.d.ts +// +// @public +export type OnPreAuthHandler = (request: KibanaRequest, response: LifecycleResponseFactory, toolkit: OnPreAuthToolkit) => OnPreAuthResult | KibanaResponse | Promise; + +// @public +export interface OnPreAuthToolkit { + next: () => OnPreAuthResult; + rewriteUrl: (url: string) => OnPreAuthResult; +} + +// @public (undocumented) +export interface PackageInfo { + // (undocumented) + branch: string; + // (undocumented) + buildNum: number; + // (undocumented) + buildSha: string; + // (undocumented) + dist: boolean; + // (undocumented) + version: string; +} + +// @public +export interface Plugin { + // (undocumented) + setup(core: CoreSetup, plugins: TPluginsSetup): TSetup | Promise; + // (undocumented) + start(core: CoreStart, plugins: TPluginsStart): TStart | Promise; + // (undocumented) + stop?(): void; +} + +// @public +export interface PluginConfigDescriptor { + exposeToBrowser?: { + [P in keyof T]?: boolean; + }; + schema: PluginConfigSchema; +} + +// @public +export type PluginConfigSchema = Type; + +// @public +export type PluginInitializer = (core: PluginInitializerContext) => Plugin; + +// @public +export interface PluginInitializerContext { + // (undocumented) + config: { + create: () => Observable; + createIfExists: () => Observable; + }; + // (undocumented) + env: { + mode: EnvironmentMode; + packageInfo: Readonly; + }; + // (undocumented) + logger: LoggerFactory; + // (undocumented) + opaqueId: PluginOpaqueId; +} + +// @public +export interface PluginManifest { + readonly configPath: ConfigPath; + readonly id: PluginName; + readonly kibanaVersion: string; + readonly optionalPlugins: readonly PluginName[]; + readonly requiredPlugins: readonly PluginName[]; + readonly server: boolean; + readonly ui: boolean; + readonly version: string; +} + +// @public +export type PluginName = string; + +// @public (undocumented) +export type PluginOpaqueId = symbol; + +// @public (undocumented) +export interface PluginsServiceSetup { + // (undocumented) + contracts: Map; + // (undocumented) + uiPlugins: { + internal: Map; + public: Map; + browserConfigs: Map>; + }; +} + +// @public (undocumented) +export interface PluginsServiceStart { + // (undocumented) + contracts: Map; +} + +// Warning: (ae-forgotten-export) The symbol "RecursiveReadonlyArray" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export type RecursiveReadonly = T extends (...args: any[]) => any ? T : T extends any[] ? RecursiveReadonlyArray : T extends object ? Readonly<{ + [K in keyof T]: RecursiveReadonly; +}> : T; + +// @public +export type RedirectResponseOptions = HttpResponseOptions & { + headers: { + location: string; + }; +}; + +// @public +export type RequestHandler

| Type, Method extends RouteMethod = any> = (context: RequestHandlerContext, request: KibanaRequest, TypeOf, TypeOf, Method>, response: KibanaResponseFactory) => IKibanaResponse | Promise>; + +// @public +export interface RequestHandlerContext { + // (undocumented) + core: { + savedObjects: { + client: SavedObjectsClientContract; + }; + elasticsearch: { + dataClient: IScopedClusterClient; + adminClient: IScopedClusterClient; + }; + uiSettings: { + client: IUiSettingsClient; + }; + }; +} + +// @public +export type RequestHandlerContextContainer = IContextContainer>; + +// @public +export type RequestHandlerContextProvider = IContextProvider, TContextName>; + +// @public +export type ResponseError = string | Error | { + message: string | Error; + attributes?: ResponseErrorAttributes; +}; + +// @public +export type ResponseErrorAttributes = Record; + +// @public +export type ResponseHeaders = { + [header in KnownHeaders]?: string | string[]; +} & { + [header: string]: string | string[]; +}; + +// @public +export interface RouteConfig

| Type, Method extends RouteMethod> { + options?: RouteConfigOptions; + path: string; + validate: RouteSchemas | false; +} + +// @public +export interface RouteConfigOptions { + authRequired?: boolean; + body?: Method extends 'get' | 'options' ? undefined : RouteConfigOptionsBody; + tags?: readonly string[]; +} + +// @public +export interface RouteConfigOptionsBody { + accepts?: RouteContentType | RouteContentType[] | string | string[]; + maxBytes?: number; + output?: typeof validBodyOutput[number]; + parse?: boolean | 'gunzip'; +} + +// @public +export type RouteContentType = 'application/json' | 'application/*+json' | 'application/octet-stream' | 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/*'; + +// @public +export type RouteMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options'; + +// @public +export type RouteRegistrar =

| Type>(route: RouteConfig, handler: RequestHandler) => void; + +// @public +export interface RouteSchemas

| Type> { + // (undocumented) + body?: B; + // (undocumented) + params?: P; + // (undocumented) + query?: Q; +} + +// @public (undocumented) +export interface SavedObject { + attributes: T; + // (undocumented) + error?: { + message: string; + statusCode: number; + }; + id: string; + migrationVersion?: SavedObjectsMigrationVersion; + references: SavedObjectReference[]; + type: string; + updated_at?: string; + version?: string; +} + +// @public +export type SavedObjectAttribute = SavedObjectAttributeSingle | SavedObjectAttributeSingle[]; + +// @public +export interface SavedObjectAttributes { + // (undocumented) + [key: string]: SavedObjectAttribute; +} + +// @public +export type SavedObjectAttributeSingle = string | number | boolean | null | undefined | SavedObjectAttributes; + +// @public +export interface SavedObjectReference { + // (undocumented) + id: string; + // (undocumented) + name: string; + // (undocumented) + type: string; +} + +// @public (undocumented) +export interface SavedObjectsBaseOptions { + namespace?: string; +} + +// @public (undocumented) +export interface SavedObjectsBulkCreateObject { + // (undocumented) + attributes: T; + // (undocumented) + id?: string; + migrationVersion?: SavedObjectsMigrationVersion; + // (undocumented) + references?: SavedObjectReference[]; + // (undocumented) + type: string; +} + +// @public (undocumented) +export interface SavedObjectsBulkGetObject { + fields?: string[]; + // (undocumented) + id: string; + // (undocumented) + type: string; +} + +// @public (undocumented) +export interface SavedObjectsBulkResponse { + // (undocumented) + saved_objects: Array>; +} + +// @public (undocumented) +export interface SavedObjectsBulkResponse { + // (undocumented) + saved_objects: Array>; +} + +// @public (undocumented) +export interface SavedObjectsBulkUpdateObject extends Pick { + attributes: Partial; + id: string; + type: string; +} + +// @public (undocumented) +export interface SavedObjectsBulkUpdateOptions extends SavedObjectsBaseOptions { + refresh?: MutatingOperationRefreshSetting; +} + +// @public (undocumented) +export interface SavedObjectsBulkUpdateResponse { + // (undocumented) + saved_objects: Array>; +} + +// @public (undocumented) +export class SavedObjectsClient { + // @internal + constructor(repository: ISavedObjectsRepository); + bulkCreate(objects: Array>, options?: SavedObjectsCreateOptions): Promise>; + bulkGet(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise>; + bulkUpdate(objects: Array>, options?: SavedObjectsBulkUpdateOptions): Promise>; + create(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise>; + delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>; + // (undocumented) + errors: typeof SavedObjectsErrorHelpers; + // (undocumented) + static errors: typeof SavedObjectsErrorHelpers; + find(options: SavedObjectsFindOptions): Promise>; + get(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; + update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>; +} + +// @public +export type SavedObjectsClientContract = Pick; + +// @public +export type SavedObjectsClientFactory = ({ request, }: { + request: Request; +}) => SavedObjectsClientContract; + +// @public +export interface SavedObjectsClientProviderOptions { + // (undocumented) + excludedWrappers?: string[]; +} + +// @public +export type SavedObjectsClientWrapperFactory = (options: SavedObjectsClientWrapperOptions) => SavedObjectsClientContract; + +// @public +export interface SavedObjectsClientWrapperOptions { + // (undocumented) + client: SavedObjectsClientContract; + // (undocumented) + request: Request; +} + +// @public (undocumented) +export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions { + id?: string; + migrationVersion?: SavedObjectsMigrationVersion; + overwrite?: boolean; + // (undocumented) + references?: SavedObjectReference[]; + refresh?: MutatingOperationRefreshSetting; +} + +// @public (undocumented) +export interface SavedObjectsDeleteByNamespaceOptions extends SavedObjectsBaseOptions { + refresh?: MutatingOperationRefreshSetting; +} + +// @public (undocumented) +export interface SavedObjectsDeleteOptions extends SavedObjectsBaseOptions { + refresh?: MutatingOperationRefreshSetting; +} + +// @public (undocumented) +export class SavedObjectsErrorHelpers { + // (undocumented) + static createBadRequestError(reason?: string): DecoratedError; + // (undocumented) + static createEsAutoCreateIndexError(): DecoratedError; + // (undocumented) + static createGenericNotFoundError(type?: string | null, id?: string | null): DecoratedError; + // (undocumented) + static createInvalidVersionError(versionInput?: string): DecoratedError; + // (undocumented) + static createUnsupportedTypeError(type: string): DecoratedError; + // (undocumented) + static decorateBadRequestError(error: Error, reason?: string): DecoratedError; + // (undocumented) + static decorateConflictError(error: Error, reason?: string): DecoratedError; + // (undocumented) + static decorateEsUnavailableError(error: Error, reason?: string): DecoratedError; + // (undocumented) + static decorateForbiddenError(error: Error, reason?: string): DecoratedError; + // (undocumented) + static decorateGeneralError(error: Error, reason?: string): DecoratedError; + // (undocumented) + static decorateNotAuthorizedError(error: Error, reason?: string): DecoratedError; + // (undocumented) + static decorateRequestEntityTooLargeError(error: Error, reason?: string): DecoratedError; + // (undocumented) + static isBadRequestError(error: Error | DecoratedError): boolean; + // (undocumented) + static isConflictError(error: Error | DecoratedError): boolean; + // (undocumented) + static isEsAutoCreateIndexError(error: Error | DecoratedError): boolean; + // (undocumented) + static isEsUnavailableError(error: Error | DecoratedError): boolean; + // (undocumented) + static isForbiddenError(error: Error | DecoratedError): boolean; + // (undocumented) + static isInvalidVersionError(error: Error | DecoratedError): boolean; + // (undocumented) + static isNotAuthorizedError(error: Error | DecoratedError): boolean; + // (undocumented) + static isNotFoundError(error: Error | DecoratedError): boolean; + // (undocumented) + static isRequestEntityTooLargeError(error: Error | DecoratedError): boolean; + // Warning: (ae-forgotten-export) The symbol "DecoratedError" needs to be exported by the entry point index.d.ts + // + // (undocumented) + static isSavedObjectsClientError(error: any): error is DecoratedError; +} + +// @public +export interface SavedObjectsExportOptions { + excludeExportDetails?: boolean; + exportSizeLimit: number; + includeReferencesDeep?: boolean; + namespace?: string; + objects?: Array<{ + id: string; + type: string; + }>; + savedObjectsClient: SavedObjectsClientContract; + search?: string; + types?: string[]; +} + +// @public +export interface SavedObjectsExportResultDetails { + exportedCount: number; + missingRefCount: number; + missingReferences: Array<{ + id: string; + type: string; + }>; +} + +// @public (undocumented) +export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions { + // (undocumented) + defaultSearchOperator?: 'AND' | 'OR'; + fields?: string[]; + // (undocumented) + filter?: string; + // (undocumented) + hasReference?: { + type: string; + id: string; + }; + // (undocumented) + page?: number; + // (undocumented) + perPage?: number; + search?: string; + searchFields?: string[]; + // (undocumented) + sortField?: string; + // (undocumented) + sortOrder?: string; + // (undocumented) + type: string | string[]; +} + +// @public +export interface SavedObjectsFindResponse { + // (undocumented) + page: number; + // (undocumented) + per_page: number; + // (undocumented) + saved_objects: Array>; + // (undocumented) + total: number; +} + +// @public +export interface SavedObjectsImportConflictError { + // (undocumented) + type: 'conflict'; +} + +// @public +export interface SavedObjectsImportError { + // (undocumented) + error: SavedObjectsImportConflictError | SavedObjectsImportUnsupportedTypeError | SavedObjectsImportMissingReferencesError | SavedObjectsImportUnknownError; + // (undocumented) + id: string; + // (undocumented) + title?: string; + // (undocumented) + type: string; +} + +// @public +export interface SavedObjectsImportMissingReferencesError { + // (undocumented) + blocking: Array<{ + type: string; + id: string; + }>; + // (undocumented) + references: Array<{ + type: string; + id: string; + }>; + // (undocumented) + type: 'missing_references'; +} + +// @public +export interface SavedObjectsImportOptions { + // (undocumented) + namespace?: string; + // (undocumented) + objectLimit: number; + // (undocumented) + overwrite: boolean; + // (undocumented) + readStream: Readable; + // (undocumented) + savedObjectsClient: SavedObjectsClientContract; + // (undocumented) + supportedTypes: string[]; +} + +// @public +export interface SavedObjectsImportResponse { + // (undocumented) + errors?: SavedObjectsImportError[]; + // (undocumented) + success: boolean; + // (undocumented) + successCount: number; +} + +// @public +export interface SavedObjectsImportRetry { + // (undocumented) + id: string; + // (undocumented) + overwrite: boolean; + // (undocumented) + replaceReferences: Array<{ + type: string; + from: string; + to: string; + }>; + // (undocumented) + type: string; +} + +// @public +export interface SavedObjectsImportUnknownError { + // (undocumented) + message: string; + // (undocumented) + statusCode: number; + // (undocumented) + type: 'unknown'; +} + +// @public +export interface SavedObjectsImportUnsupportedTypeError { + // (undocumented) + type: 'unsupported_type'; +} + +// @public (undocumented) +export interface SavedObjectsIncrementCounterOptions extends SavedObjectsBaseOptions { + // (undocumented) + migrationVersion?: SavedObjectsMigrationVersion; + refresh?: MutatingOperationRefreshSetting; +} + +// @internal @deprecated (undocumented) +export interface SavedObjectsLegacyService { + // Warning: (ae-forgotten-export) The symbol "SavedObjectsClientProvider" needs to be exported by the entry point index.d.ts + // + // (undocumented) + addScopedSavedObjectsClientWrapperFactory: SavedObjectsClientProvider['addClientWrapperFactory']; + // (undocumented) + getSavedObjectsRepository(...rest: any[]): any; + // (undocumented) + getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient']; + // (undocumented) + importExport: { + objectLimit: number; + importSavedObjects(options: SavedObjectsImportOptions): Promise; + resolveImportErrors(options: SavedObjectsResolveImportErrorsOptions): Promise; + getSortedObjectsForExport(options: SavedObjectsExportOptions): Promise; + }; + // (undocumented) + SavedObjectsClient: typeof SavedObjectsClient; + // (undocumented) + schema: SavedObjectsSchema; + // (undocumented) + setScopedSavedObjectsClientFactory: SavedObjectsClientProvider['setClientFactory']; + // (undocumented) + types: string[]; +} + +// @public (undocumented) +export interface SavedObjectsMigrationLogger { + // (undocumented) + debug: (msg: string) => void; + // (undocumented) + info: (msg: string) => void; + // (undocumented) + warning: (msg: string) => void; +} + +// @public +export interface SavedObjectsMigrationVersion { + // (undocumented) + [pluginName: string]: string; +} + +// Warning: (ae-missing-release-tag) "RawDoc" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface SavedObjectsRawDoc { + // (undocumented) + _id: string; + // (undocumented) + _primary_term?: number; + // (undocumented) + _seq_no?: number; + // (undocumented) + _source: any; + // (undocumented) + _type?: string; +} + +// @public (undocumented) +export class SavedObjectsRepository { + // Warning: (ae-forgotten-export) The symbol "SavedObjectsRepositoryOptions" needs to be exported by the entry point index.d.ts + // + // @internal + constructor(options: SavedObjectsRepositoryOptions); + bulkCreate(objects: Array>, options?: SavedObjectsCreateOptions): Promise>; + bulkGet(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise>; + bulkUpdate(objects: Array>, options?: SavedObjectsBulkUpdateOptions): Promise>; + create(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise>; + delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>; + deleteByNamespace(namespace: string, options?: SavedObjectsDeleteByNamespaceOptions): Promise; + // (undocumented) + find({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespace, type, filter, }: SavedObjectsFindOptions): Promise>; + get(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; + incrementCounter(type: string, id: string, counterFieldName: string, options?: SavedObjectsIncrementCounterOptions): Promise<{ + id: string; + type: string; + updated_at: string; + references: any; + version: string; + attributes: any; + }>; + update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>; + } + +// @public +export interface SavedObjectsResolveImportErrorsOptions { + // (undocumented) + namespace?: string; + // (undocumented) + objectLimit: number; + // (undocumented) + readStream: Readable; + // (undocumented) + retries: SavedObjectsImportRetry[]; + // (undocumented) + savedObjectsClient: SavedObjectsClientContract; + // (undocumented) + supportedTypes: string[]; +} + +// @internal (undocumented) +export class SavedObjectsSchema { + // Warning: (ae-forgotten-export) The symbol "SavedObjectsSchemaDefinition" needs to be exported by the entry point index.d.ts + constructor(schemaDefinition?: SavedObjectsSchemaDefinition); + // (undocumented) + getConvertToAliasScript(type: string): string | undefined; + // (undocumented) + getIndexForType(config: Config, type: string): string | undefined; + // (undocumented) + isHiddenType(type: string): boolean; + // (undocumented) + isNamespaceAgnostic(type: string): boolean; +} + +// @internal (undocumented) +export class SavedObjectsSerializer { + constructor(schema: SavedObjectsSchema); + generateRawId(namespace: string | undefined, type: string, id?: string): string; + isRawSavedObject(rawDoc: SavedObjectsRawDoc): any; + // Warning: (ae-forgotten-export) The symbol "SanitizedSavedObjectDoc" needs to be exported by the entry point index.d.ts + rawToSavedObject(doc: SavedObjectsRawDoc): SanitizedSavedObjectDoc; + savedObjectToRaw(savedObj: SanitizedSavedObjectDoc): SavedObjectsRawDoc; + } + +// @public +export interface SavedObjectsServiceSetup { + addClientWrapper: (priority: number, id: string, factory: SavedObjectsClientWrapperFactory) => void; + createInternalRepository: (extraTypes?: string[]) => ISavedObjectsRepository; + createScopedRepository: (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository; + setClientFactory: (customClientFactory: SavedObjectsClientFactory) => void; +} + +// @public +export interface SavedObjectsServiceStart { + getScopedClient: (req: KibanaRequest, options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; +} + +// @public (undocumented) +export interface SavedObjectsUpdateOptions extends SavedObjectsBaseOptions { + references?: SavedObjectReference[]; + refresh?: MutatingOperationRefreshSetting; + version?: string; +} + +// @public (undocumented) +export interface SavedObjectsUpdateResponse extends Omit, 'attributes' | 'references'> { + // (undocumented) + attributes: Partial; + // (undocumented) + references: SavedObjectReference[] | undefined; +} + +// @public +export class ScopedClusterClient implements IScopedClusterClient { + constructor(internalAPICaller: APICaller, scopedAPICaller: APICaller, headers?: Headers | undefined); + callAsCurrentUser(endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; + callAsInternalUser(endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; + } + +// @public +export interface SessionCookieValidationResult { + isValid: boolean; + path?: string; +} + +// @public +export interface SessionStorage { + clear(): void; + get(): Promise; + set(sessionValue: T): void; +} + +// @public +export interface SessionStorageCookieOptions { + encryptionKey: string; + isSecure: boolean; + name: string; + validate: (sessionValue: T | T[]) => SessionCookieValidationResult; +} + +// @public +export interface SessionStorageFactory { + // (undocumented) + asScoped: (request: KibanaRequest) => SessionStorage; +} + +// @public +export interface UiSettingsParams { + category?: string[]; + description?: string; + name?: string; + optionLabels?: Record; + options?: string[]; + readonly?: boolean; + requiresPageReload?: boolean; + type?: UiSettingsType; + value?: SavedObjectAttribute; +} + +// @public (undocumented) +export interface UiSettingsServiceSetup { + register(settings: Record): void; +} + +// @public +export type UiSettingsType = 'json' | 'markdown' | 'number' | 'select' | 'boolean' | 'string'; + +// @public +export interface UserProvidedValues { + // (undocumented) + isOverridden?: boolean; + // (undocumented) + userValue?: T; +} + +// @public +export const validBodyOutput: readonly ["data", "stream"]; + + +// Warnings were encountered during analysis: +// +// src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/plugins_service.ts:43:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts + +``` diff --git a/src/core/server/server.ts b/src/core/server/server.ts index b36468b85d7a1..555714ee57043 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -40,11 +40,13 @@ import { mapToObject } from '../utils/'; import { ContextService } from './context'; import { RequestHandlerContext } from '.'; import { InternalCoreSetup } from './internal_types'; +import { CapabilitiesService } from './capabilities'; const coreId = Symbol('core'); export class Server { public readonly configService: ConfigService; + private readonly capabilities: CapabilitiesService; private readonly context: ContextService; private readonly elasticsearch: ElasticsearchService; private readonly http: HttpService; @@ -70,6 +72,7 @@ export class Server { this.elasticsearch = new ElasticsearchService(core); this.savedObjects = new SavedObjectsService(core); this.uiSettings = new UiSettingsService(core); + this.capabilities = new CapabilitiesService(core); } public async setup() { @@ -95,6 +98,8 @@ export class Server { this.registerDefaultRoute(httpSetup); + const capabilitiesSetup = this.capabilities.setup({ http: httpSetup }); + const elasticsearchServiceSetup = await this.elasticsearch.setup({ http: httpSetup, }); @@ -109,6 +114,7 @@ export class Server { }); const coreSetup: InternalCoreSetup = { + capabilities: capabilitiesSetup, context: contextServiceSetup, elasticsearch: elasticsearchServiceSetup, http: httpSetup, @@ -131,10 +137,14 @@ export class Server { public async start() { this.log.debug('starting server'); const savedObjectsStart = await this.savedObjects.start({}); - - const pluginsStart = await this.plugins.start({ savedObjects: savedObjectsStart }); + const capabilitiesStart = this.capabilities.start(); + const pluginsStart = await this.plugins.start({ + capabilities: capabilitiesStart, + savedObjects: savedObjectsStart, + }); const coreStart = { + capabilities: capabilitiesStart, savedObjects: savedObjectsStart, plugins: pluginsStart, }; diff --git a/src/legacy/server/capabilities/capabilities_mixin.test.mocks.ts b/src/core/server/test_utils.ts similarity index 82% rename from src/legacy/server/capabilities/capabilities_mixin.test.mocks.ts rename to src/core/server/test_utils.ts index 64300604b3e9d..470b1c2d135b7 100644 --- a/src/legacy/server/capabilities/capabilities_mixin.test.mocks.ts +++ b/src/core/server/test_utils.ts @@ -17,7 +17,4 @@ * under the License. */ -export const mockRegisterCapabilitiesRoute = jest.fn(); -jest.mock('./capabilities_route', () => ({ - registerCapabilitiesRoute: mockRegisterCapabilitiesRoute, -})); +export { createHttpServer } from './http/test_utils'; diff --git a/src/core/types/capabilities.ts b/src/core/types/capabilities.ts new file mode 100644 index 0000000000000..c2ca09528f085 --- /dev/null +++ b/src/core/types/capabilities.ts @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * The 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. + * + * @public + */ +export interface Capabilities { + /** Navigation link capabilities. */ + navLinks: Record; + + /** Management section capabilities. */ + management: { + [sectionId: string]: Record; + }; + + /** Catalogue capabilities. Catalogue entries drive the visibility of the Kibana homepage options. */ + catalogue: Record; + + /** Custom capabilities, registered by plugins. */ + [key: string]: Record>; +} diff --git a/src/core/types/index.ts b/src/core/types/index.ts index 3aae17e6ea594..d01b514c770a7 100644 --- a/src/core/types/index.ts +++ b/src/core/types/index.ts @@ -22,3 +22,4 @@ * types are stripped. */ export * from './core_service'; +export * from './capabilities'; diff --git a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js index 17632a2c0a405..347968ecb80c5 100644 --- a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js +++ b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js @@ -70,9 +70,9 @@ const uiCapabilities = { // Mock fetch for CoreSystem calls. fetchMock.config.fallbackToNetwork = true; -fetchMock.post(/\\/api\\/capabilities/, { +fetchMock.post(/\\/api\\/core\\/capabilities/, { status: 200, - body: JSON.stringify({ capabilities: uiCapabilities }), + body: JSON.stringify(uiCapabilities), headers: { 'Content-Type': 'application/json' }, }); diff --git a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts b/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts index 6e087e3ee7f87..228ef96f8c9f3 100644 --- a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts +++ b/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts @@ -17,7 +17,7 @@ * under the License. */ import { Server } from '../../server/kbn_server'; -import { Capabilities } from '../../../core/public'; +import { Capabilities } from '../../../core/server'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { SavedObjectsManagementDefinition } from '../../../core/server/saved_objects/management'; diff --git a/src/legacy/plugin_discovery/types.ts b/src/legacy/plugin_discovery/types.ts index d987260b099bd..c32418e1aeb62 100644 --- a/src/legacy/plugin_discovery/types.ts +++ b/src/legacy/plugin_discovery/types.ts @@ -18,7 +18,7 @@ */ import { Server } from '../server/kbn_server'; -import { Capabilities } from '../../core/public'; +import { Capabilities } from '../../core/server'; // Disable lint errors for imports from src/core/* until SavedObjects migration is complete // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { SavedObjectsSchemaDefinition } from '../../core/server/saved_objects/schema'; diff --git a/src/legacy/server/capabilities/capabilities_mixin.test.ts b/src/legacy/server/capabilities/capabilities_mixin.test.ts index 9b6827e1bb380..3422d6a8cbb34 100644 --- a/src/legacy/server/capabilities/capabilities_mixin.test.ts +++ b/src/legacy/server/capabilities/capabilities_mixin.test.ts @@ -19,81 +19,47 @@ import { Server } from 'hapi'; import KbnServer from '../kbn_server'; -import { mockRegisterCapabilitiesRoute } from './capabilities_mixin.test.mocks'; import { capabilitiesMixin } from './capabilities_mixin'; describe('capabilitiesMixin', () => { + let registerMock: jest.Mock; + const getKbnServer = (pluginSpecs: any[] = []) => { - return { + return ({ afterPluginsInit: (callback: () => void) => callback(), pluginSpecs, - } as KbnServer; + newPlatform: { + setup: { + core: { + capabilities: { + registerProvider: registerMock, + }, + }, + }, + }, + } as unknown) as KbnServer; }; let server: Server; beforeEach(() => { server = new Server(); + server.getUiNavLinks = () => []; + registerMock = jest.fn(); }); - afterEach(() => { - mockRegisterCapabilitiesRoute.mockClear(); - }); - - it('calls registerCapabilitiesRoute with merged uiCapabilitiesProviers', async () => { - const kbnServer = getKbnServer([ - { - getUiCapabilitiesProvider: () => () => ({ - app1: { read: true }, - management: { section1: { feature1: true } }, - }), - }, - { - getUiCapabilitiesProvider: () => () => ({ - app2: { write: true }, - catalogue: { feature3: true }, - management: { section2: { feature2: true } }, - }), - }, - ]); - - await capabilitiesMixin(kbnServer, server); - - expect(mockRegisterCapabilitiesRoute).toHaveBeenCalledWith( - server, - { - app1: { read: true }, - app2: { write: true }, - catalogue: { feature3: true }, - management: { - section1: { feature1: true }, - section2: { feature2: true }, - }, - navLinks: {}, - }, - [] - ); - }); + it('calls capabilities#registerCapabilitiesProvider for each legacy plugin specs', async () => { + const getPluginSpec = (provider: () => any) => ({ + getUiCapabilitiesProvider: () => provider, + }); - it('exposes server#registerCapabilitiesModifier for providing modifiers to the route', async () => { - const kbnServer = getKbnServer(); + const capaA = { catalogue: { A: true } }; + const capaB = { catalogue: { B: true } }; + const kbnServer = getKbnServer([getPluginSpec(() => capaA), getPluginSpec(() => capaB)]); await capabilitiesMixin(kbnServer, server); - const mockModifier1 = jest.fn(); - const mockModifier2 = jest.fn(); - server.registerCapabilitiesModifier(mockModifier1); - server.registerCapabilitiesModifier(mockModifier2); - - expect(mockRegisterCapabilitiesRoute.mock.calls[0][2]).toEqual([mockModifier1, mockModifier2]); - }); - it('exposes request#getCapabilities for retrieving legacy capabilities', async () => { - const kbnServer = getKbnServer(); - jest.spyOn(server, 'decorate'); - await capabilitiesMixin(kbnServer, server); - expect(server.decorate).toHaveBeenCalledWith( - 'request', - 'getCapabilities', - expect.any(Function) - ); + expect(registerMock).toHaveBeenCalledTimes(2); + expect(registerMock.mock.calls[0][0]()).toEqual(capaA); + expect(registerMock.mock.calls[1][0]()).toEqual(capaB); }); }); diff --git a/src/legacy/server/capabilities/capabilities_mixin.ts b/src/legacy/server/capabilities/capabilities_mixin.ts index b41dfe42c40b2..23a0c35414ae6 100644 --- a/src/legacy/server/capabilities/capabilities_mixin.ts +++ b/src/legacy/server/capabilities/capabilities_mixin.ts @@ -17,51 +17,26 @@ * under the License. */ -import { Server, Request } from 'hapi'; - -import { Capabilities } from '../../../core/public'; +import { Server } from 'hapi'; import KbnServer from '../kbn_server'; -import { registerCapabilitiesRoute } from './capabilities_route'; -import { mergeCapabilities } from './merge_capabilities'; -import { resolveCapabilities } from './resolve_capabilities'; - -export type CapabilitiesModifier = ( - request: Request, - uiCapabilities: Capabilities -) => Capabilities | Promise; export async function capabilitiesMixin(kbnServer: KbnServer, server: Server) { - const modifiers: CapabilitiesModifier[] = []; + const registerLegacyCapabilities = async () => { + const capabilitiesList = await Promise.all( + kbnServer.pluginSpecs + .map(spec => spec.getUiCapabilitiesProvider()) + .filter(provider => !!provider) + .map(provider => provider(server)) + ); - server.decorate('server', 'registerCapabilitiesModifier', (provider: CapabilitiesModifier) => { - modifiers.push(provider); - }); + capabilitiesList.forEach(capabilities => { + kbnServer.newPlatform.setup.core.capabilities.registerProvider(() => capabilities); + }); + }; // Some plugin capabilities are derived from data provided by other plugins, // so we need to wait until after all plugins have been init'd to fetch uiCapabilities. kbnServer.afterPluginsInit(async () => { - const defaultCapabilities = mergeCapabilities( - ...(await Promise.all( - kbnServer.pluginSpecs - .map(spec => spec.getUiCapabilitiesProvider()) - .filter(provider => !!provider) - .map(provider => provider(server)) - )) - ); - - server.decorate('request', 'getCapabilities', function() { - // Get legacy nav links - const navLinks = server.getUiNavLinks().reduce( - (acc, spec) => ({ - ...acc, - [spec._id]: true, - }), - {} as Record - ); - - return resolveCapabilities(this, modifiers, defaultCapabilities, { navLinks }); - }); - - registerCapabilitiesRoute(server, defaultCapabilities, modifiers); + await registerLegacyCapabilities(); }); } diff --git a/src/legacy/server/capabilities/capabilities_route.test.ts b/src/legacy/server/capabilities/capabilities_route.test.ts deleted file mode 100644 index 6f3f706379642..0000000000000 --- a/src/legacy/server/capabilities/capabilities_route.test.ts +++ /dev/null @@ -1,137 +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 { Server } from 'hapi'; -import { registerCapabilitiesRoute } from './capabilities_route'; -import { Capabilities } from '../../../core/public'; - -describe('capabilities api', () => { - const defaultCapabilities = { - catalogue: { - feature1: true, - feature2: true, - }, - management: { - section1: { - read: true, - }, - section2: { - write: true, - }, - }, - navLinks: { - app1: true, - app2: true, - }, - myApp: { - read: true, - write: true, - kioskMode: true, - }, - } as Capabilities; - - let server: Server; - - beforeEach(() => { - server = new Server(); - }); - - it('returns unmodified uiCapabilities if no modifiers are available', async () => { - registerCapabilitiesRoute(server, defaultCapabilities, []); - const resp = await server.inject({ - method: 'POST', - url: '/api/capabilities', - payload: { capabilities: {} }, - }); - expect(JSON.parse(resp.payload)).toEqual({ - capabilities: defaultCapabilities, - }); - }); - - it('merges payload capabilities with defaultCapabilities', async () => { - registerCapabilitiesRoute(server, defaultCapabilities, []); - const resp = await server.inject({ - method: 'POST', - url: '/api/capabilities', - payload: { capabilities: { navLinks: { app3: true } } }, - }); - expect(JSON.parse(resp.payload)).toEqual({ - capabilities: { - ...defaultCapabilities, - navLinks: { - ...defaultCapabilities.navLinks, - app3: true, - }, - }, - }); - }); - - it('allows a single provider to modify uiCapabilities', async () => { - registerCapabilitiesRoute(server, defaultCapabilities, [ - (req, caps) => { - caps.management.section2.write = false; - caps.myApp.write = false; - return caps; - }, - ]); - const resp = await server.inject({ - method: 'POST', - url: '/api/capabilities', - payload: { capabilities: {} }, - }); - const results = JSON.parse(resp.payload); - expect(results.capabilities.management.section2.write).toBe(false); - expect(results.capabilities.myApp.write).toBe(false); - }); - - it('allows multiple providers to modify uiCapabilities', async () => { - registerCapabilitiesRoute(server, defaultCapabilities, [ - (req, caps) => { - caps.management.section2.write = false; - return caps; - }, - (req, caps) => { - caps.myApp.write = false; - return caps; - }, - ]); - const resp = await server.inject({ - method: 'POST', - url: '/api/capabilities', - payload: { capabilities: {} }, - }); - const results = JSON.parse(resp.payload); - expect(results.capabilities.management.section2.write).toBe(false); - expect(results.capabilities.myApp.write).toBe(false); - }); - - it('returns an error if any providers fail', async () => { - registerCapabilitiesRoute(server, defaultCapabilities, [ - (req, caps) => { - throw new Error(`Couldn't fetch license`); - }, - ]); - const resp = await server.inject({ - method: 'POST', - url: '/api/capabilities', - payload: { capabilities: {} }, - }); - expect(resp.statusCode).toBe(500); - }); -}); diff --git a/src/legacy/server/capabilities/capabilities_route.ts b/src/legacy/server/capabilities/capabilities_route.ts deleted file mode 100644 index 5564fbb295a62..0000000000000 --- a/src/legacy/server/capabilities/capabilities_route.ts +++ /dev/null @@ -1,54 +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 { Server } from 'hapi'; - -import { Capabilities } from '../../../core/public'; -import { CapabilitiesModifier } from './capabilities_mixin'; -import { resolveCapabilities } from './resolve_capabilities'; - -export const registerCapabilitiesRoute = ( - server: Server, - defaultCapabilities: Capabilities, - modifiers: CapabilitiesModifier[] -) => { - server.route({ - path: '/api/capabilities', - method: 'POST', - options: { - validate: { - payload: Joi.object({ - capabilities: Joi.object().required(), - }).required(), - }, - }, - async handler(request) { - const { capabilities } = request.payload as { capabilities: Capabilities }; - return { - capabilities: await resolveCapabilities( - request, - modifiers, - defaultCapabilities, - capabilities - ), - }; - }, - }); -}; diff --git a/src/legacy/server/capabilities/index.ts b/src/legacy/server/capabilities/index.ts index 09461a40c008a..8c5dea1226f2b 100644 --- a/src/legacy/server/capabilities/index.ts +++ b/src/legacy/server/capabilities/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { CapabilitiesModifier, capabilitiesMixin } from './capabilities_mixin'; +export { capabilitiesMixin } from './capabilities_mixin'; diff --git a/src/legacy/server/capabilities/merge_capabilities.test.ts b/src/legacy/server/capabilities/merge_capabilities.test.ts deleted file mode 100644 index a73b81bdaf0a3..0000000000000 --- a/src/legacy/server/capabilities/merge_capabilities.test.ts +++ /dev/null @@ -1,84 +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 { mergeCapabilities } from './merge_capabilities'; - -const defaultProps = { - catalogue: {}, - management: {}, - navLinks: {}, -}; - -test(`"{ foo: {} }" doesn't clobber "{ foo: { bar: true } }"`, () => { - const output1 = mergeCapabilities({ foo: { bar: true } }, { foo: {} }); - expect(output1).toEqual({ ...defaultProps, foo: { bar: true } }); - - const output2 = mergeCapabilities({ foo: { bar: true } }, { foo: {} }); - expect(output2).toEqual({ ...defaultProps, foo: { bar: true } }); -}); - -test(`"{ foo: { bar: true } }" doesn't clobber "{ baz: { quz: true } }"`, () => { - const output1 = mergeCapabilities({ foo: { bar: true } }, { baz: { quz: true } }); - expect(output1).toEqual({ ...defaultProps, foo: { bar: true }, baz: { quz: true } }); - - const output2 = mergeCapabilities({ baz: { quz: true } }, { foo: { bar: true } }); - expect(output2).toEqual({ ...defaultProps, foo: { bar: true }, baz: { quz: true } }); -}); - -test(`"{ foo: { bar: { baz: true } } }" doesn't clobber "{ foo: { bar: { quz: true } } }"`, () => { - const output1 = mergeCapabilities( - { foo: { bar: { baz: true } } }, - { foo: { bar: { quz: true } } } - ); - expect(output1).toEqual({ ...defaultProps, foo: { bar: { baz: true, quz: true } } }); - - const output2 = mergeCapabilities( - { foo: { bar: { quz: true } } }, - { foo: { bar: { baz: true } } } - ); - expect(output2).toEqual({ ...defaultProps, foo: { bar: { baz: true, quz: true } } }); -}); - -test(`error is thrown if boolean and object clash`, () => { - expect(() => { - mergeCapabilities({ foo: { bar: { baz: true } } }, { foo: { bar: true } }); - }).toThrowErrorMatchingInlineSnapshot(`"a boolean and an object can't be merged"`); - - expect(() => { - mergeCapabilities({ foo: { bar: true } }, { foo: { bar: { baz: true } } }); - }).toThrowErrorMatchingInlineSnapshot(`"a boolean and an object can't be merged"`); -}); - -test(`supports duplicates as long as the booleans are the same`, () => { - const output1 = mergeCapabilities({ foo: { bar: true } }, { foo: { bar: true } }); - expect(output1).toEqual({ ...defaultProps, foo: { bar: true } }); - - const output2 = mergeCapabilities({ foo: { bar: false } }, { foo: { bar: false } }); - expect(output2).toEqual({ ...defaultProps, foo: { bar: false } }); -}); - -test(`error is thrown if merging "true" and "false"`, () => { - expect(() => { - mergeCapabilities({ foo: { bar: false } }, { foo: { bar: true } }); - }).toThrowErrorMatchingInlineSnapshot(`"\\"true\\" and \\"false\\" can't be merged"`); - - expect(() => { - mergeCapabilities({ foo: { bar: true } }, { foo: { bar: false } }); - }).toThrowErrorMatchingInlineSnapshot(`"\\"true\\" and \\"false\\" can't be merged"`); -}); diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index 1cb9ed15a083b..61c358a320921 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -20,7 +20,7 @@ import { ResponseObject, Server } from 'hapi'; import { UnwrapPromise } from '@kbn/utility-types'; -import { SavedObjectsClientProviderOptions, CoreSetup } from 'src/core/server'; +import { SavedObjectsClientProviderOptions, CoreSetup, CoreStart } from 'src/core/server'; import { ConfigService, ElasticsearchServiceSetup, @@ -39,9 +39,8 @@ import { SavedObjectsManagement } from '../../core/server/saved_objects/manageme import { ApmOssPlugin } from '../core_plugins/apm_oss'; import { CallClusterWithRequest, ElasticsearchPlugin } from '../core_plugins/elasticsearch'; import { UsageCollectionSetup } from '../../plugins/usage_collection/server'; -import { CapabilitiesModifier } from './capabilities'; import { IndexPatternsServiceFactory } from './index_patterns'; -import { Capabilities } from '../../core/public'; +import { Capabilities } from '../../core/server'; import { UiSettingsServiceFactoryOptions } from '../../legacy/ui/ui_settings/ui_settings_service_factory'; export interface KibanaConfig { @@ -69,7 +68,6 @@ declare module 'hapi' { savedObjects: SavedObjectsLegacyService; injectUiAppVars: (pluginName: string, getAppVars: () => { [key: string]: any }) => void; getHiddenUiAppById(appId: string): UiApp; - registerCapabilitiesModifier: (provider: CapabilitiesModifier) => void; addScopedTutorialContextFactory: ( scopedTutorialContextFactory: (...args: any[]) => any ) => void; @@ -90,7 +88,6 @@ declare module 'hapi' { getBasePath(): string; getDefaultRoute(): Promise; getUiSettingsService(): IUiSettingsClient; - getCapabilities(): Promise; } interface ResponseToolkit { @@ -127,7 +124,7 @@ export default class KbnServer { plugins: PluginsSetup; }; start: { - core: CoreSetup; + core: CoreStart; plugins: Record; }; stop: null; diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index 763167c6b5ccf..931f8b184421c 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -279,8 +279,6 @@ export function uiRenderMixin(kbnServer, server, config) { uiPlugins, legacyMetadata, - - capabilities: await request.getCapabilities(), }, }); diff --git a/x-pack/legacy/plugins/security/index.js b/x-pack/legacy/plugins/security/index.js index f7cd5a3afa6b1..05d4e38b276c8 100644 --- a/x-pack/legacy/plugins/security/index.js +++ b/x-pack/legacy/plugins/security/index.js @@ -149,7 +149,6 @@ export const security = (kibana) => new kibana.Plugin({ isSystemAPIRequest: server.plugins.kibana.systemApi.isSystemApiRequest.bind( server.plugins.kibana.systemApi ), - capabilities: { registerCapabilitiesModifier: server.registerCapabilitiesModifier }, cspRules: createCSPRuleString(config.get('csp.rules')), kibanaIndexName: config.get('kibana.index'), }); diff --git a/x-pack/legacy/plugins/spaces/index.ts b/x-pack/legacy/plugins/spaces/index.ts index 8f995d3c12c2a..6ea06f47b9012 100644 --- a/x-pack/legacy/plugins/spaces/index.ts +++ b/x-pack/legacy/plugins/spaces/index.ts @@ -129,9 +129,6 @@ export const spaces = (kibana: Record) => tutorial: { addScopedTutorialContextFactory: server.addScopedTutorialContextFactory, }, - capabilities: { - registerCapabilitiesModifier: server.registerCapabilitiesModifier, - }, auditLogger: { create: (pluginId: string) => new AuditLogger(server, pluginId, server.config(), server.plugins.xpack_main.info), diff --git a/x-pack/plugins/features/server/feature_schema.ts b/x-pack/plugins/features/server/feature_schema.ts index 9ae55a61c910f..b5ba10f8d0300 100644 --- a/x-pack/plugins/features/server/feature_schema.ts +++ b/x-pack/plugins/features/server/feature_schema.ts @@ -7,7 +7,7 @@ import Joi from 'joi'; import { difference } from 'lodash'; -import { Capabilities as UICapabilities } from '../../../../src/core/public'; +import { Capabilities as UICapabilities } from '../../../../src/core/server'; import { FeatureWithAllOrReadPrivileges } from './feature'; // Each feature gets its own property on the UICapabilities object, diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index ce7fea129bb6f..f3a3375d1936b 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -10,7 +10,7 @@ import { PluginInitializerContext, RecursiveReadonly, } from '../../../../src/core/server'; -import { Capabilities as UICapabilities } from '../../../../src/core/public'; +import { Capabilities as UICapabilities } from '../../../../src/core/server'; import { deepFreeze } from '../../../../src/core/utils'; import { XPackInfo } from '../../../legacy/plugins/xpack_main/server/lib/xpack_info'; import { PluginSetupContract as TimelionSetupContract } from '../../../../src/plugins/timelion/server'; diff --git a/x-pack/plugins/features/server/ui_capabilities_for_features.ts b/x-pack/plugins/features/server/ui_capabilities_for_features.ts index 22c9379686b34..368b38ce7df91 100644 --- a/x-pack/plugins/features/server/ui_capabilities_for_features.ts +++ b/x-pack/plugins/features/server/ui_capabilities_for_features.ts @@ -5,7 +5,7 @@ */ import _ from 'lodash'; -import { Capabilities as UICapabilities } from '../../../../src/core/public'; +import { Capabilities as UICapabilities } from '../../../../src/core/server'; import { Feature } from './feature'; const ELIGIBLE_FLAT_MERGE_KEYS = ['catalogue']; diff --git a/x-pack/plugins/security/server/authorization/actions/ui.ts b/x-pack/plugins/security/server/authorization/actions/ui.ts index c243b4f0bbdc1..9e77c319a9b3a 100644 --- a/x-pack/plugins/security/server/authorization/actions/ui.ts +++ b/x-pack/plugins/security/server/authorization/actions/ui.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { isString } from 'lodash'; -import { Capabilities as UICapabilities } from '../../../../../../src/core/public'; +import { Capabilities as UICapabilities } from '../../../../../../src/core/server'; import { uiCapabilitiesRegex } from '../../../../features/server'; export class UIActions { diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 98fc93883f176..dd057e8c6d295 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -20,7 +20,6 @@ import { deepFreeze } from '../../../../src/core/utils'; import { SpacesPluginSetup } from '../../spaces/server'; import { PluginSetupContract as FeaturesSetupContract } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; -import { CapabilitiesModifier } from '../../../../src/legacy/server/capabilities'; import { Authentication, setupAuthentication } from './authentication'; import { Authorization, setupAuthorization } from './authorization'; @@ -44,7 +43,6 @@ export type FeaturesService = Pick; export interface LegacyAPI { serverConfig: { protocol: string; hostname: string; port: number }; isSystemAPIRequest: (request: KibanaRequest) => boolean; - capabilities: { registerCapabilitiesModifier: (provider: CapabilitiesModifier) => void }; kibanaIndexName: string; cspRules: string; savedObjects: SavedObjectsLegacyService; @@ -157,6 +155,8 @@ export class Plugin { featuresService: features, }); + core.capabilities.registerSwitcher(authz.disableUnauthorizedCapabilities); + defineRoutes({ router: core.http.createRouter(), basePath: core.http.basePath, @@ -196,10 +196,6 @@ export class Plugin { authz, legacyAPI, }); - - legacyAPI.capabilities.registerCapabilitiesModifier((request, capabilities) => - authz.disableUnauthorizedCapabilities(KibanaRequest.from(request), capabilities) - ); }, registerPrivilegesWithCluster: async () => await authz.registerPrivilegesWithCluster(), diff --git a/x-pack/plugins/spaces/server/plugin.ts b/x-pack/plugins/spaces/server/plugin.ts index 9d45dbb1b748d..732731ac35ecd 100644 --- a/x-pack/plugins/spaces/server/plugin.ts +++ b/x-pack/plugins/spaces/server/plugin.ts @@ -6,13 +6,11 @@ import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; -import { CapabilitiesModifier } from 'src/legacy/server/capabilities'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { HomeServerPluginSetup } from 'src/plugins/home/server'; import { SavedObjectsLegacyService, CoreSetup, - KibanaRequest, Logger, PluginInitializerContext, } from '../../../../src/core/server'; @@ -42,9 +40,6 @@ export interface LegacyAPI { tutorial: { addScopedTutorialContextFactory: (factory: any) => void; }; - capabilities: { - registerCapabilitiesModifier: (provider: CapabilitiesModifier) => void; - }; auditLogger: { create: (pluginId: string) => AuditLogger; }; @@ -132,6 +127,16 @@ export class Plugin { features: plugins.features, }); + core.capabilities.registerSwitcher(async (request, uiCapabilities) => { + try { + const activeSpace = await spacesService.getActiveSpace(request); + const features = plugins.features.getFeatures(); + return toggleUICapabilities(features, uiCapabilities, activeSpace); + } catch (e) { + return uiCapabilities; + } + }); + if (plugins.security) { plugins.security.registerSpacesService(spacesService); } @@ -189,14 +194,5 @@ export class Plugin { features: featuresSetup, licensing: licensingSetup, }); - legacyAPI.capabilities.registerCapabilitiesModifier(async (request, uiCapabilities) => { - try { - const activeSpace = await spacesService.getActiveSpace(KibanaRequest.from(request)); - const features = featuresSetup.getFeatures(); - return toggleUICapabilities(features, uiCapabilities, activeSpace); - } catch (e) { - return uiCapabilities; - } - }); } } diff --git a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_legacy_api.ts b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_legacy_api.ts index 62820466b571c..cd2bcdcd5aa34 100644 --- a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_legacy_api.ts +++ b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_legacy_api.ts @@ -104,7 +104,6 @@ export const createLegacyAPI = ({ kibanaIndex: '', }, auditLogger: {} as any, - capabilities: {} as any, tutorial: {} as any, xpackMain: {} as any, savedObjects: savedObjectsService, diff --git a/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts b/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts index 56969879b6322..4af7d81e5a7b4 100644 --- a/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts +++ b/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import axios, { AxiosInstance } from 'axios'; -import cheerio from 'cheerio'; import { UICapabilities } from 'ui/capabilities'; import { format as formatUrl } from 'url'; import util from 'util'; import { ToolingLog } from '@kbn/dev-utils'; import { FtrProviderContext } from '../ftr_provider_context'; +import { FeaturesService, FeaturesProvider } from './features'; export interface BasicCredentials { username: string; @@ -30,8 +30,9 @@ interface GetUICapabilitiesResult { export class UICapabilitiesService { private readonly log: ToolingLog; private readonly axios: AxiosInstance; + private readonly featureService: FeaturesService; - constructor(url: string, log: ToolingLog) { + constructor(url: string, log: ToolingLog, featureService: FeaturesService) { this.log = log; this.axios = axios.create({ headers: { 'kbn-xsrf': 'x-pack/ftr/services/ui_capabilities' }, @@ -39,6 +40,7 @@ export class UICapabilitiesService { maxRedirects: 0, validateStatus: () => true, // we'll handle our own statusCodes and throw informative errors }); + this.featureService = featureService; } public async get({ @@ -48,8 +50,15 @@ export class UICapabilitiesService { credentials?: BasicCredentials; spaceId?: string; }): Promise { + const features = await this.featureService.get(); + const applications = Object.values(features) + .map(feature => feature.navLinkId) + .filter(link => !!link); + const spaceUrlPrefix = spaceId ? `/s/${spaceId}` : ''; - this.log.debug(`requesting ${spaceUrlPrefix}/app/kibana to parse the uiCapabilities`); + this.log.debug( + `requesting ${spaceUrlPrefix}/api/core/capabilities to parse the uiCapabilities` + ); const requestHeaders = credentials ? { Authorization: `Basic ${Buffer.from( @@ -57,9 +66,13 @@ export class UICapabilitiesService { ).toString('base64')}`, } : {}; - const response = await this.axios.get(`${spaceUrlPrefix}/app/kibana`, { - headers: requestHeaders, - }); + const response = await this.axios.post( + `${spaceUrlPrefix}/api/core/capabilities`, + { applications: [...applications, 'kibana:management'] }, + { + headers: requestHeaders, + } + ); if (response.status === 302 && response.headers.location === '/spaces/space_selector') { return { @@ -83,35 +96,20 @@ export class UICapabilitiesService { ); } - const dom = cheerio.load(response.data.toString()); - const element = dom('kbn-injected-metadata'); - if (!element) { - throw new Error('Unable to find "kbn-injected-metadata" element'); - } - - const dataAttrJson = element.attr('data'); - - try { - const dataAttr = JSON.parse(dataAttrJson); - return { - success: true, - value: dataAttr.capabilities as UICapabilities, - }; - } catch (err) { - throw new Error( - `Unable to parse JSON from the kbn-injected-metadata data attribute: ${dataAttrJson}` - ); - } + return { + success: true, + value: response.data, + }; } } -export function UICapabilitiesProvider({ getService }: FtrProviderContext) { - const log = getService('log'); - const config = getService('config'); +export function UICapabilitiesProvider(context: FtrProviderContext) { + const log = context.getService('log'); + const config = context.getService('config'); const noAuthUrl = formatUrl({ ...config.get('servers.kibana'), auth: undefined, }); - return new UICapabilitiesService(noAuthUrl, log); + return new UICapabilitiesService(noAuthUrl, log, FeaturesProvider(context)); } diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts index 6b15d1bff4209..d7d15ddfeec9c 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts @@ -7,10 +7,7 @@ import expect from '@kbn/expect'; import { mapValues } from 'lodash'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { - GetUICapabilitiesFailureReason, - UICapabilitiesService, -} from '../../common/services/ui_capabilities'; +import { UICapabilitiesService } from '../../common/services/ui_capabilities'; import { UserAtSpaceScenarios } from '../scenarios'; export default function catalogueTests({ getService }: FtrProviderContext) { @@ -58,15 +55,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) { case 'dual_privileges_all at nothing_space': case 'dual_privileges_read at nothing_space': case 'nothing_space_all at nothing_space': - case 'nothing_space_read at nothing_space': { - expect(uiCapabilities.success).to.be(true); - expect(uiCapabilities.value).to.have.property('catalogue'); - // everything is disabled - const expected = mapValues(uiCapabilities.value!.catalogue, () => false); - expect(uiCapabilities.value!.catalogue).to.eql(expected); - break; - } - // if we don't have access at the space itself, security interceptor responds with 404. + case 'nothing_space_read at nothing_space': case 'no_kibana_privileges at everything_space': case 'no_kibana_privileges at nothing_space': case 'legacy_all at everything_space': @@ -74,10 +63,14 @@ export default function catalogueTests({ getService }: FtrProviderContext) { case 'everything_space_all at nothing_space': case 'everything_space_read at nothing_space': case 'nothing_space_all at everything_space': - case 'nothing_space_read at everything_space': - expect(uiCapabilities.success).to.be(false); - expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound); + case 'nothing_space_read at everything_space': { + expect(uiCapabilities.success).to.be(true); + expect(uiCapabilities.value).to.have.property('catalogue'); + // everything is disabled + const expected = mapValues(uiCapabilities.value!.catalogue, () => false); + expect(uiCapabilities.value!.catalogue).to.eql(expected); break; + } default: throw new UnreachableError(scenario); } diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/foo.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/foo.ts index ad4c3582d468f..d9cec21a41bc8 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/foo.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/foo.ts @@ -6,10 +6,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { - GetUICapabilitiesFailureReason, - UICapabilitiesService, -} from '../../common/services/ui_capabilities'; +import { UICapabilitiesService } from '../../common/services/ui_capabilities'; import { UserAtSpaceScenarios } from '../scenarios'; export default function fooTests({ getService }: FtrProviderContext) { @@ -61,16 +58,6 @@ export default function fooTests({ getService }: FtrProviderContext) { case 'dual_privileges_read at nothing_space': case 'nothing_space_all at nothing_space': case 'nothing_space_read at nothing_space': - expect(uiCapabilities.success).to.be(true); - expect(uiCapabilities.value).to.have.property('foo'); - expect(uiCapabilities.value!.foo).to.eql({ - create: false, - edit: false, - delete: false, - show: false, - }); - break; - // if we don't have access at the space itself, security interceptor responds with 404. case 'no_kibana_privileges at everything_space': case 'no_kibana_privileges at nothing_space': case 'legacy_all at everything_space': @@ -79,8 +66,14 @@ export default function fooTests({ getService }: FtrProviderContext) { case 'everything_space_read at nothing_space': case 'nothing_space_all at everything_space': case 'nothing_space_read at everything_space': - expect(uiCapabilities.success).to.be(false); - expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound); + expect(uiCapabilities.success).to.be(true); + expect(uiCapabilities.value).to.have.property('foo'); + expect(uiCapabilities.value!.foo).to.eql({ + create: false, + edit: false, + delete: false, + show: false, + }); break; default: throw new UnreachableError(scenario); diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts index e9d0cf28e96ec..beb781fdfe8b6 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts @@ -8,10 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { NavLinksBuilder } from '../../common/nav_links_builder'; import { FeaturesService } from '../../common/services'; -import { - GetUICapabilitiesFailureReason, - UICapabilitiesService, -} from '../../common/services/ui_capabilities'; +import { UICapabilitiesService } from '../../common/services/ui_capabilities'; import { UserAtSpaceScenarios } from '../scenarios'; export default function navLinksTests({ getService }: FtrProviderContext) { @@ -58,11 +55,6 @@ export default function navLinksTests({ getService }: FtrProviderContext) { case 'global_read at nothing_space': case 'nothing_space_all at nothing_space': case 'nothing_space_read at nothing_space': - expect(uiCapabilities.success).to.be(true); - expect(uiCapabilities.value).to.have.property('navLinks'); - expect(uiCapabilities.value!.navLinks).to.eql(navLinksBuilder.only('management')); - break; - // if we don't have access at the space itself, security interceptor responds with 404. case 'no_kibana_privileges at everything_space': case 'no_kibana_privileges at nothing_space': case 'legacy_all at everything_space': @@ -71,8 +63,9 @@ export default function navLinksTests({ getService }: FtrProviderContext) { case 'everything_space_read at nothing_space': case 'nothing_space_all at everything_space': case 'nothing_space_read at everything_space': - expect(uiCapabilities.success).to.be(false); - expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound); + expect(uiCapabilities.success).to.be(true); + expect(uiCapabilities.value).to.have.property('navLinks'); + expect(uiCapabilities.value!.navLinks).to.eql(navLinksBuilder.only('management')); break; default: throw new UnreachableError(scenario); diff --git a/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts b/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts index 9a46820e87f3a..232fd19c5c5b8 100644 --- a/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts +++ b/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts @@ -7,10 +7,7 @@ import expect from '@kbn/expect'; import { mapValues } from 'lodash'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { - GetUICapabilitiesFailureReason, - UICapabilitiesService, -} from '../../common/services/ui_capabilities'; +import { UICapabilitiesService } from '../../common/services/ui_capabilities'; import { UserScenarios } from '../scenarios'; export default function catalogueTests({ getService }: FtrProviderContext) { @@ -63,8 +60,11 @@ export default function catalogueTests({ getService }: FtrProviderContext) { // these users have no access to even get the ui capabilities case 'legacy_all': case 'no_kibana_privileges': - expect(uiCapabilities.success).to.be(false); - expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound); + expect(uiCapabilities.success).to.be(true); + expect(uiCapabilities.value).to.have.property('catalogue'); + // only foo is enabled + const expected = mapValues(uiCapabilities.value!.catalogue, () => false); + expect(uiCapabilities.value!.catalogue).to.eql(expected); break; default: throw new UnreachableError(scenario); diff --git a/x-pack/test/ui_capabilities/security_only/tests/foo.ts b/x-pack/test/ui_capabilities/security_only/tests/foo.ts index a2a663921e6ea..f5f9ae3d5020e 100644 --- a/x-pack/test/ui_capabilities/security_only/tests/foo.ts +++ b/x-pack/test/ui_capabilities/security_only/tests/foo.ts @@ -6,10 +6,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { - GetUICapabilitiesFailureReason, - UICapabilitiesService, -} from '../../common/services/ui_capabilities'; +import { UICapabilitiesService } from '../../common/services/ui_capabilities'; import { UserScenarios } from '../scenarios'; export default function fooTests({ getService }: FtrProviderContext) { @@ -55,8 +52,14 @@ export default function fooTests({ getService }: FtrProviderContext) { // these users have no access to even get the ui capabilities case 'legacy_all': case 'no_kibana_privileges': - expect(uiCapabilities.success).to.be(false); - expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound); + expect(uiCapabilities.success).to.be(true); + expect(uiCapabilities.value).to.have.property('foo'); + expect(uiCapabilities.value!.foo).to.eql({ + create: false, + edit: false, + delete: false, + show: false, + }); break; // all other users can't do anything with Foo default: diff --git a/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts b/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts index f643753a5c37c..dd451bba3b538 100644 --- a/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts +++ b/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts @@ -8,10 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { NavLinksBuilder } from '../../common/nav_links_builder'; import { FeaturesService } from '../../common/services'; -import { - GetUICapabilitiesFailureReason, - UICapabilitiesService, -} from '../../common/services/ui_capabilities'; +import { UICapabilitiesService } from '../../common/services/ui_capabilities'; import { UserScenarios } from '../scenarios'; export default function navLinksTests({ getService }: FtrProviderContext) { @@ -59,8 +56,9 @@ export default function navLinksTests({ getService }: FtrProviderContext) { break; case 'legacy_all': case 'no_kibana_privileges': - expect(uiCapabilities.success).to.be(false); - expect(uiCapabilities.failureReason).to.be(GetUICapabilitiesFailureReason.NotFound); + expect(uiCapabilities.success).to.be(true); + expect(uiCapabilities.value).to.have.property('navLinks'); + expect(uiCapabilities.value!.navLinks).to.eql(navLinksBuilder.only('management')); break; default: throw new UnreachableError(scenario);