diff --git a/docs/development/core/public/kibana-plugin-public.chromedoctitle.md b/docs/development/core/public/kibana-plugin-public.chromedoctitle.md index 3c6cfab486288..feb3b3ab966ef 100644 --- a/docs/development/core/public/kibana-plugin-public.chromedoctitle.md +++ b/docs/development/core/public/kibana-plugin-public.chromedoctitle.md @@ -12,13 +12,6 @@ APIs for accessing and updating the document title. export interface ChromeDocTitle ``` -## Methods - -| Method | Description | -| --- | --- | -| [change(newTitle)](./kibana-plugin-public.chromedoctitle.change.md) | Changes the current document title. | -| [reset()](./kibana-plugin-public.chromedoctitle.reset.md) | Resets the document title to it's initial value. (meaning the one present in the title meta at application load.) | - ## Example 1 How to change the title of the document @@ -37,3 +30,10 @@ chrome.docTitle.reset() ``` +## Methods + +| Method | Description | +| --- | --- | +| [change(newTitle)](./kibana-plugin-public.chromedoctitle.change.md) | Changes the current document title. | +| [reset()](./kibana-plugin-public.chromedoctitle.reset.md) | Resets the document title to it's initial value. (meaning the one present in the title meta at application load.) | + diff --git a/docs/development/core/public/kibana-plugin-public.chromenavcontrols.md b/docs/development/core/public/kibana-plugin-public.chromenavcontrols.md index a34db9bb33d9d..30b9a6869d1ff 100644 --- a/docs/development/core/public/kibana-plugin-public.chromenavcontrols.md +++ b/docs/development/core/public/kibana-plugin-public.chromenavcontrols.md @@ -12,13 +12,6 @@ export interface ChromeNavControls ``` -## Methods - -| Method | Description | -| --- | --- | -| [registerLeft(navControl)](./kibana-plugin-public.chromenavcontrols.registerleft.md) | Register a nav control to be presented on the left side of the chrome header. | -| [registerRight(navControl)](./kibana-plugin-public.chromenavcontrols.registerright.md) | Register a nav control to be presented on the right side of the chrome header. | - ## Example Register a left-side nav control rendered with React. @@ -33,3 +26,10 @@ chrome.navControls.registerLeft({ ``` +## Methods + +| Method | Description | +| --- | --- | +| [registerLeft(navControl)](./kibana-plugin-public.chromenavcontrols.registerleft.md) | Register a nav control to be presented on the left side of the chrome header. | +| [registerRight(navControl)](./kibana-plugin-public.chromenavcontrols.registerright.md) | Register a nav control to be presented on the right side of the chrome header. | + diff --git a/docs/development/core/public/kibana-plugin-public.chromestart.md b/docs/development/core/public/kibana-plugin-public.chromestart.md index d5d99f3d5be65..4e44e5bf05074 100644 --- a/docs/development/core/public/kibana-plugin-public.chromestart.md +++ b/docs/development/core/public/kibana-plugin-public.chromestart.md @@ -12,6 +12,31 @@ ChromeStart allows plugins to customize the global chrome header UI and enrich t export interface ChromeStart ``` +## Remarks + +While ChromeStart exposes many APIs, they should be used sparingly and the developer should understand how they affect other plugins and applications. + +## Example 1 + +How to add a recently accessed item to the sidebar: + +```ts +core.chrome.recentlyAccessed.add('/app/map/1234', 'Map 1234', '1234'); + +``` + +## Example 2 + +How to set the help dropdown extension: + +```tsx +core.chrome.setHelpExtension(elem => { + ReactDOM.render(, elem); + return () => ReactDOM.unmountComponentAtNode(elem); +}); + +``` + ## Properties | Property | Type | Description | @@ -43,28 +68,3 @@ export interface ChromeStart | [setIsCollapsed(isCollapsed)](./kibana-plugin-public.chromestart.setiscollapsed.md) | Set the collapsed state of the chrome navigation. | | [setIsVisible(isVisible)](./kibana-plugin-public.chromestart.setisvisible.md) | Set the temporary visibility for the chrome. This does nothing if the chrome is hidden by default and should be used to hide the chrome for things like full-screen modes with an exit button. | -## Remarks - -While ChromeStart exposes many APIs, they should be used sparingly and the developer should understand how they affect other plugins and applications. - -## Example 1 - -How to add a recently accessed item to the sidebar: - -```ts -core.chrome.recentlyAccessed.add('/app/map/1234', 'Map 1234', '1234'); - -``` - -## Example 2 - -How to set the help dropdown extension: - -```tsx -core.chrome.setHelpExtension(elem => { - ReactDOM.render(, elem); - return () => ReactDOM.unmountComponentAtNode(elem); -}); - -``` - diff --git a/docs/development/core/public/kibana-plugin-public.contextsetup.md b/docs/development/core/public/kibana-plugin-public.contextsetup.md index a006fa7205ca6..d4399b6ba70c4 100644 --- a/docs/development/core/public/kibana-plugin-public.contextsetup.md +++ b/docs/development/core/public/kibana-plugin-public.contextsetup.md @@ -12,12 +12,6 @@ An object that handles registration of context providers and configuring handler export interface ContextSetup ``` -## Methods - -| Method | Description | -| --- | --- | -| [createContextContainer()](./kibana-plugin-public.contextsetup.createcontextcontainer.md) | Creates a new [IContextContainer](./kibana-plugin-public.icontextcontainer.md) for a service owner. | - ## Remarks A [IContextContainer](./kibana-plugin-public.icontextcontainer.md) can be used by any Core service or plugin (known as the "service owner") which wishes to expose APIs in a handler function. The container object will manage registering context providers and configuring a handler with all of the contexts that should be exposed to the handler's plugin. This is dependent on the dependencies that the handler's plugin declares. @@ -136,3 +130,9 @@ class VizRenderingPlugin { ``` +## Methods + +| Method | Description | +| --- | --- | +| [createContextContainer()](./kibana-plugin-public.contextsetup.createcontextcontainer.md) | Creates a new [IContextContainer](./kibana-plugin-public.icontextcontainer.md) for a service owner. | + diff --git a/docs/development/core/public/kibana-plugin-public.icontextcontainer.md b/docs/development/core/public/kibana-plugin-public.icontextcontainer.md index f16c07b3d7906..7a21df6b93bb5 100644 --- a/docs/development/core/public/kibana-plugin-public.icontextcontainer.md +++ b/docs/development/core/public/kibana-plugin-public.icontextcontainer.md @@ -12,13 +12,6 @@ An object that handles registration of context providers and configuring handler export interface IContextContainer> ``` -## Methods - -| Method | Description | -| --- | --- | -| [createHandler(pluginOpaqueId, handler)](./kibana-plugin-public.icontextcontainer.createhandler.md) | Create a new handler function pre-wired to context for the plugin. | -| [registerContext(pluginOpaqueId, contextName, provider)](./kibana-plugin-public.icontextcontainer.registercontext.md) | Register a new context provider. | - ## Remarks A [IContextContainer](./kibana-plugin-public.icontextcontainer.md) can be used by any Core service or plugin (known as the "service owner") which wishes to expose APIs in a handler function. The container object will manage registering context providers and configuring a handler with all of the contexts that should be exposed to the handler's plugin. This is dependent on the dependencies that the handler's plugin declares. @@ -78,3 +71,10 @@ class MyPlugin { ``` +## Methods + +| Method | Description | +| --- | --- | +| [createHandler(pluginOpaqueId, handler)](./kibana-plugin-public.icontextcontainer.createhandler.md) | Create a new handler function pre-wired to context for the plugin. | +| [registerContext(pluginOpaqueId, contextName, provider)](./kibana-plugin-public.icontextcontainer.registercontext.md) | Register a new context provider. | + diff --git a/docs/development/core/public/kibana-plugin-public.legacycoresetup.md b/docs/development/core/public/kibana-plugin-public.legacycoresetup.md index a753300437c1c..803c96cd0b22c 100644 --- a/docs/development/core/public/kibana-plugin-public.legacycoresetup.md +++ b/docs/development/core/public/kibana-plugin-public.legacycoresetup.md @@ -16,13 +16,13 @@ Setup interface exposed to the legacy platform via the `ui/new_platform` module. export interface LegacyCoreSetup extends CoreSetup ``` +## Remarks + +Some methods are not supported in the legacy platform and while present to make this type compatibile with [CoreSetup](./kibana-plugin-public.coresetup.md), unsupported methods will throw exceptions when called. + ## Properties | Property | Type | Description | | --- | --- | --- | | [injectedMetadata](./kibana-plugin-public.legacycoresetup.injectedmetadata.md) | InjectedMetadataSetup | | -## Remarks - -Some methods are not supported in the legacy platform and while present to make this type compatibile with [CoreSetup](./kibana-plugin-public.coresetup.md), unsupported methods will throw exceptions when called. - diff --git a/docs/development/core/public/kibana-plugin-public.legacycorestart.md b/docs/development/core/public/kibana-plugin-public.legacycorestart.md index 775c3fb1ffe3d..438a3d6110776 100644 --- a/docs/development/core/public/kibana-plugin-public.legacycorestart.md +++ b/docs/development/core/public/kibana-plugin-public.legacycorestart.md @@ -16,13 +16,13 @@ Start interface exposed to the legacy platform via the `ui/new_platform` module. export interface LegacyCoreStart extends CoreStart ``` +## Remarks + +Some methods are not supported in the legacy platform and while present to make this type compatibile with [CoreStart](./kibana-plugin-public.corestart.md), unsupported methods will throw exceptions when called. + ## Properties | Property | Type | Description | | --- | --- | --- | | [injectedMetadata](./kibana-plugin-public.legacycorestart.injectedmetadata.md) | InjectedMetadataStart | | -## Remarks - -Some methods are not supported in the legacy platform and while present to make this type compatibile with [CoreStart](./kibana-plugin-public.corestart.md), unsupported methods will throw exceptions when called. - diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md index 6033c667c1866..3b916db972673 100644 --- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md +++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md @@ -12,6 +12,10 @@ Saved Objects is Kibana's data persisentence mechanism allowing plugins to use E export declare class SavedObjectsClient ``` +## Remarks + +The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `SavedObjectsClient` class. + ## Properties | Property | Modifiers | Type | Description | @@ -30,7 +34,3 @@ export declare class SavedObjectsClient | [bulkUpdate(objects)](./kibana-plugin-public.savedobjectsclient.bulkupdate.md) | | Update multiple documents at once | | [update(type, id, attributes, { version, migrationVersion, references })](./kibana-plugin-public.savedobjectsclient.update.md) | | Updates an object | -## Remarks - -The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `SavedObjectsClient` class. - diff --git a/docs/development/core/server/kibana-plugin-server.basepath.md b/docs/development/core/server/kibana-plugin-server.basepath.md index 77f50abc60369..50a30f7c43fe6 100644 --- a/docs/development/core/server/kibana-plugin-server.basepath.md +++ b/docs/development/core/server/kibana-plugin-server.basepath.md @@ -12,6 +12,10 @@ Access or manipulate the Kibana base path export declare class BasePath ``` +## Remarks + +The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `BasePath` class. + ## Properties | Property | Modifiers | Type | Description | @@ -22,7 +26,3 @@ export declare class BasePath | [serverBasePath](./kibana-plugin-server.basepath.serverbasepath.md) | | string | returns the server's basePathSee [BasePath.get](./kibana-plugin-server.basepath.get.md) for getting the basePath value for a specific request | | [set](./kibana-plugin-server.basepath.set.md) | | (request: KibanaRequest<unknown, unknown, unknown, any> | LegacyRequest, requestSpecificBasePath: string) => void | sets basePath value, specific for an incoming request. | -## Remarks - -The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `BasePath` class. - diff --git a/docs/development/core/server/kibana-plugin-server.configdeprecationfactory.md b/docs/development/core/server/kibana-plugin-server.configdeprecationfactory.md index f022d6c1d064a..0302797147cff 100644 --- a/docs/development/core/server/kibana-plugin-server.configdeprecationfactory.md +++ b/docs/development/core/server/kibana-plugin-server.configdeprecationfactory.md @@ -14,15 +14,6 @@ See methods documentation for more detailed examples. export interface ConfigDeprecationFactory ``` -## Methods - -| Method | Description | -| --- | --- | -| [rename(oldKey, newKey)](./kibana-plugin-server.configdeprecationfactory.rename.md) | Rename a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the oldKey was found and deprecation applied. | -| [renameFromRoot(oldKey, newKey)](./kibana-plugin-server.configdeprecationfactory.renamefromroot.md) | Rename a configuration property from the root configuration. Will log a deprecation warning if the oldKey was found and deprecation applied.This should be only used when renaming properties from different configuration's path. To rename properties from inside a plugin's configuration, use 'rename' instead. | -| [unused(unusedKey)](./kibana-plugin-server.configdeprecationfactory.unused.md) | Remove a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the unused key was found and deprecation applied. | -| [unusedFromRoot(unusedKey)](./kibana-plugin-server.configdeprecationfactory.unusedfromroot.md) | Remove a configuration property from the root configuration. Will log a deprecation warning if the unused key was found and deprecation applied.This should be only used when removing properties from outside of a plugin's configuration. To remove properties from inside a plugin's configuration, use 'unused' instead. | - ## Example @@ -34,3 +25,12 @@ const provider: ConfigDeprecationProvider = ({ rename, unused }) => [ ``` +## Methods + +| Method | Description | +| --- | --- | +| [rename(oldKey, newKey)](./kibana-plugin-server.configdeprecationfactory.rename.md) | Rename a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the oldKey was found and deprecation applied. | +| [renameFromRoot(oldKey, newKey)](./kibana-plugin-server.configdeprecationfactory.renamefromroot.md) | Rename a configuration property from the root configuration. Will log a deprecation warning if the oldKey was found and deprecation applied.This should be only used when renaming properties from different configuration's path. To rename properties from inside a plugin's configuration, use 'rename' instead. | +| [unused(unusedKey)](./kibana-plugin-server.configdeprecationfactory.unused.md) | Remove a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the unused key was found and deprecation applied. | +| [unusedFromRoot(unusedKey)](./kibana-plugin-server.configdeprecationfactory.unusedfromroot.md) | Remove a configuration property from the root configuration. Will log a deprecation warning if the unused key was found and deprecation applied.This should be only used when removing properties from outside of a plugin's configuration. To remove properties from inside a plugin's configuration, use 'unused' instead. | + diff --git a/docs/development/core/server/kibana-plugin-server.contextsetup.md b/docs/development/core/server/kibana-plugin-server.contextsetup.md index 1f285efe92b68..1b2a1e2f1b621 100644 --- a/docs/development/core/server/kibana-plugin-server.contextsetup.md +++ b/docs/development/core/server/kibana-plugin-server.contextsetup.md @@ -12,12 +12,6 @@ An object that handles registration of context providers and configuring handler export interface ContextSetup ``` -## Methods - -| Method | Description | -| --- | --- | -| [createContextContainer()](./kibana-plugin-server.contextsetup.createcontextcontainer.md) | Creates a new [IContextContainer](./kibana-plugin-server.icontextcontainer.md) for a service owner. | - ## Remarks A [IContextContainer](./kibana-plugin-server.icontextcontainer.md) can be used by any Core service or plugin (known as the "service owner") which wishes to expose APIs in a handler function. The container object will manage registering context providers and configuring a handler with all of the contexts that should be exposed to the handler's plugin. This is dependent on the dependencies that the handler's plugin declares. @@ -136,3 +130,9 @@ class VizRenderingPlugin { ``` +## Methods + +| Method | Description | +| --- | --- | +| [createContextContainer()](./kibana-plugin-server.contextsetup.createcontextcontainer.md) | Creates a new [IContextContainer](./kibana-plugin-server.icontextcontainer.md) for a service owner. | + diff --git a/docs/development/core/server/kibana-plugin-server.cspconfig.md b/docs/development/core/server/kibana-plugin-server.cspconfig.md index e5276991be404..7e491cb0df912 100644 --- a/docs/development/core/server/kibana-plugin-server.cspconfig.md +++ b/docs/development/core/server/kibana-plugin-server.cspconfig.md @@ -12,6 +12,10 @@ CSP configuration for use in Kibana. export declare class CspConfig implements ICspConfig ``` +## Remarks + +The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `CspConfig` class. + ## Properties | Property | Modifiers | Type | Description | @@ -22,7 +26,3 @@ export declare class CspConfig implements ICspConfig | [strict](./kibana-plugin-server.cspconfig.strict.md) | | boolean | | | [warnLegacyBrowsers](./kibana-plugin-server.cspconfig.warnlegacybrowsers.md) | | boolean | | -## Remarks - -The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `CspConfig` class. - diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearcherrorhelpers.md b/docs/development/core/server/kibana-plugin-server.elasticsearcherrorhelpers.md index c823da392042a..2e615acfeac6b 100644 --- a/docs/development/core/server/kibana-plugin-server.elasticsearcherrorhelpers.md +++ b/docs/development/core/server/kibana-plugin-server.elasticsearcherrorhelpers.md @@ -12,13 +12,6 @@ Helpers for working with errors returned from the Elasticsearch service.Since th export declare class ElasticsearchErrorHelpers ``` -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [decorateNotAuthorizedError(error, reason)](./kibana-plugin-server.elasticsearcherrorhelpers.decoratenotauthorizederror.md) | static | | -| [isNotAuthorizedError(error)](./kibana-plugin-server.elasticsearcherrorhelpers.isnotauthorizederror.md) | static | | - ## Example Handle errors @@ -33,3 +26,10 @@ try { ``` +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [decorateNotAuthorizedError(error, reason)](./kibana-plugin-server.elasticsearcherrorhelpers.decoratenotauthorizederror.md) | static | | +| [isNotAuthorizedError(error)](./kibana-plugin-server.elasticsearcherrorhelpers.isnotauthorizederror.md) | static | | + diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md index 99d4caf40c0d3..3b1993841339d 100644 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md @@ -12,21 +12,6 @@ Kibana HTTP Service provides own abstraction for work with HTTP stack. Plugins d export interface HttpServiceSetup ``` -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [basePath](./kibana-plugin-server.httpservicesetup.basepath.md) | IBasePath | Access or manipulate the Kibana base path See [IBasePath](./kibana-plugin-server.ibasepath.md). | -| [createCookieSessionStorageFactory](./kibana-plugin-server.httpservicesetup.createcookiesessionstoragefactory.md) | <T>(cookieOptions: SessionStorageCookieOptions<T>) => Promise<SessionStorageFactory<T>> | Creates cookie based session storage factory [SessionStorageFactory](./kibana-plugin-server.sessionstoragefactory.md) | -| [createRouter](./kibana-plugin-server.httpservicesetup.createrouter.md) | () => IRouter | Provides ability to declare a handler function for a particular path and HTTP request method. | -| [csp](./kibana-plugin-server.httpservicesetup.csp.md) | ICspConfig | The CSP config used for Kibana. | -| [isTlsEnabled](./kibana-plugin-server.httpservicesetup.istlsenabled.md) | boolean | Flag showing whether a server was configured to use TLS connection. | -| [registerAuth](./kibana-plugin-server.httpservicesetup.registerauth.md) | (handler: AuthenticationHandler) => void | To define custom authentication and/or authorization mechanism for incoming requests. | -| [registerOnPostAuth](./kibana-plugin-server.httpservicesetup.registeronpostauth.md) | (handler: OnPostAuthHandler) => void | To define custom logic to perform for incoming requests. | -| [registerOnPreAuth](./kibana-plugin-server.httpservicesetup.registeronpreauth.md) | (handler: OnPreAuthHandler) => void | To define custom logic to perform for incoming requests. | -| [registerOnPreResponse](./kibana-plugin-server.httpservicesetup.registeronpreresponse.md) | (handler: OnPreResponseHandler) => void | To define custom logic to perform for the server response. | -| [registerRouteHandlerContext](./kibana-plugin-server.httpservicesetup.registerroutehandlercontext.md) | <T extends keyof RequestHandlerContext>(contextName: T, provider: RequestHandlerContextProvider<T>) => RequestHandlerContextContainer | Register a context provider for a route handler. | - ## Example To handle an incoming request in your plugin you should: - Create a `Router` instance. @@ -92,3 +77,18 @@ async (context, request, response) => { ``` +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [basePath](./kibana-plugin-server.httpservicesetup.basepath.md) | IBasePath | Access or manipulate the Kibana base path See [IBasePath](./kibana-plugin-server.ibasepath.md). | +| [createCookieSessionStorageFactory](./kibana-plugin-server.httpservicesetup.createcookiesessionstoragefactory.md) | <T>(cookieOptions: SessionStorageCookieOptions<T>) => Promise<SessionStorageFactory<T>> | Creates cookie based session storage factory [SessionStorageFactory](./kibana-plugin-server.sessionstoragefactory.md) | +| [createRouter](./kibana-plugin-server.httpservicesetup.createrouter.md) | () => IRouter | Provides ability to declare a handler function for a particular path and HTTP request method. | +| [csp](./kibana-plugin-server.httpservicesetup.csp.md) | ICspConfig | The CSP config used for Kibana. | +| [isTlsEnabled](./kibana-plugin-server.httpservicesetup.istlsenabled.md) | boolean | Flag showing whether a server was configured to use TLS connection. | +| [registerAuth](./kibana-plugin-server.httpservicesetup.registerauth.md) | (handler: AuthenticationHandler) => void | To define custom authentication and/or authorization mechanism for incoming requests. | +| [registerOnPostAuth](./kibana-plugin-server.httpservicesetup.registeronpostauth.md) | (handler: OnPostAuthHandler) => void | To define custom logic to perform for incoming requests. | +| [registerOnPreAuth](./kibana-plugin-server.httpservicesetup.registeronpreauth.md) | (handler: OnPreAuthHandler) => void | To define custom logic to perform for incoming requests. | +| [registerOnPreResponse](./kibana-plugin-server.httpservicesetup.registeronpreresponse.md) | (handler: OnPreResponseHandler) => void | To define custom logic to perform for the server response. | +| [registerRouteHandlerContext](./kibana-plugin-server.httpservicesetup.registerroutehandlercontext.md) | <T extends keyof RequestHandlerContext>(contextName: T, provider: RequestHandlerContextProvider<T>) => RequestHandlerContextContainer | Register a context provider for a route handler. | + diff --git a/docs/development/core/server/kibana-plugin-server.icontextcontainer.md b/docs/development/core/server/kibana-plugin-server.icontextcontainer.md index 114da31442ff9..8235c40131536 100644 --- a/docs/development/core/server/kibana-plugin-server.icontextcontainer.md +++ b/docs/development/core/server/kibana-plugin-server.icontextcontainer.md @@ -12,13 +12,6 @@ An object that handles registration of context providers and configuring handler export interface IContextContainer> ``` -## Methods - -| Method | Description | -| --- | --- | -| [createHandler(pluginOpaqueId, handler)](./kibana-plugin-server.icontextcontainer.createhandler.md) | Create a new handler function pre-wired to context for the plugin. | -| [registerContext(pluginOpaqueId, contextName, provider)](./kibana-plugin-server.icontextcontainer.registercontext.md) | Register a new context provider. | - ## Remarks A [IContextContainer](./kibana-plugin-server.icontextcontainer.md) can be used by any Core service or plugin (known as the "service owner") which wishes to expose APIs in a handler function. The container object will manage registering context providers and configuring a handler with all of the contexts that should be exposed to the handler's plugin. This is dependent on the dependencies that the handler's plugin declares. @@ -78,3 +71,10 @@ class MyPlugin { ``` +## Methods + +| Method | Description | +| --- | --- | +| [createHandler(pluginOpaqueId, handler)](./kibana-plugin-server.icontextcontainer.createhandler.md) | Create a new handler function pre-wired to context for the plugin. | +| [registerContext(pluginOpaqueId, contextName, provider)](./kibana-plugin-server.icontextcontainer.registercontext.md) | Register a new context provider. | + diff --git a/docs/development/core/server/kibana-plugin-server.pluginconfigdescriptor.md b/docs/development/core/server/kibana-plugin-server.pluginconfigdescriptor.md index 671298a67381a..3d661ac66d2b7 100644 --- a/docs/development/core/server/kibana-plugin-server.pluginconfigdescriptor.md +++ b/docs/development/core/server/kibana-plugin-server.pluginconfigdescriptor.md @@ -12,14 +12,6 @@ Describes a plugin configuration properties. export interface PluginConfigDescriptor ``` -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [deprecations](./kibana-plugin-server.pluginconfigdescriptor.deprecations.md) | ConfigDeprecationProvider | Provider for the [ConfigDeprecation](./kibana-plugin-server.configdeprecation.md) to apply to the plugin configuration. | -| [exposeToBrowser](./kibana-plugin-server.pluginconfigdescriptor.exposetobrowser.md) | {
[P in keyof T]?: boolean;
} | List of configuration properties that will be available on the client-side plugin. | -| [schema](./kibana-plugin-server.pluginconfigdescriptor.schema.md) | PluginConfigSchema<T> | Schema to use to validate the plugin configuration.[PluginConfigSchema](./kibana-plugin-server.pluginconfigschema.md) | - ## Example @@ -48,3 +40,11 @@ export const config: PluginConfigDescriptor = { ``` +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [deprecations](./kibana-plugin-server.pluginconfigdescriptor.deprecations.md) | ConfigDeprecationProvider | Provider for the [ConfigDeprecation](./kibana-plugin-server.configdeprecation.md) to apply to the plugin configuration. | +| [exposeToBrowser](./kibana-plugin-server.pluginconfigdescriptor.exposetobrowser.md) | {
[P in keyof T]?: boolean;
} | List of configuration properties that will be available on the client-side plugin. | +| [schema](./kibana-plugin-server.pluginconfigdescriptor.schema.md) | PluginConfigSchema<T> | Schema to use to validate the plugin configuration.[PluginConfigSchema](./kibana-plugin-server.pluginconfigschema.md) | + diff --git a/docs/development/core/server/kibana-plugin-server.pluginmanifest.md b/docs/development/core/server/kibana-plugin-server.pluginmanifest.md index 4a9498f0e9fab..9bb208a809b22 100644 --- a/docs/development/core/server/kibana-plugin-server.pluginmanifest.md +++ b/docs/development/core/server/kibana-plugin-server.pluginmanifest.md @@ -12,6 +12,10 @@ Describes the set of required and optional properties plugin can define in its m export interface PluginManifest ``` +## Remarks + +Should never be used in code outside of Core but is exported for documentation purposes. + ## Properties | Property | Type | Description | @@ -25,7 +29,3 @@ export interface PluginManifest | [ui](./kibana-plugin-server.pluginmanifest.ui.md) | boolean | Specifies whether plugin includes some client/browser specific functionality that should be included into client bundle via public/ui_plugin.js file. | | [version](./kibana-plugin-server.pluginmanifest.version.md) | string | Version of the plugin. | -## Remarks - -Should never be used in code outside of Core but is exported for documentation purposes. - diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.md index 17d29bb912c83..e68486ecff874 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsclient.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclient.md @@ -10,6 +10,10 @@ export declare class SavedObjectsClient ``` +## Remarks + +The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `SavedObjectsClient` class. + ## Properties | Property | Modifiers | Type | Description | @@ -30,7 +34,3 @@ export declare class SavedObjectsClient | [get(type, id, options)](./kibana-plugin-server.savedobjectsclient.get.md) | | Retrieves a single object | | [update(type, id, attributes, options)](./kibana-plugin-server.savedobjectsclient.update.md) | | Updates an SavedObject | -## Remarks - -The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `SavedObjectsClient` class. - diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.md index dd97b45f590e2..95bd817a43da6 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.md @@ -12,15 +12,6 @@ Saved Objects is Kibana's data persisentence mechanism allowing plugins to use E export interface SavedObjectsServiceSetup ``` -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [addClientWrapper](./kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) | (priority: number, id: string, factory: SavedObjectsClientWrapperFactory<KibanaRequest>) => void | Add a client wrapper with the given priority. | -| [createInternalRepository](./kibana-plugin-server.savedobjectsservicesetup.createinternalrepository.md) | (extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. | -| [createScopedRepository](./kibana-plugin-server.savedobjectsservicesetup.createscopedrepository.md) | (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. | -| [setClientFactory](./kibana-plugin-server.savedobjectsservicesetup.setclientfactory.md) | (customClientFactory: SavedObjectsClientFactory<KibanaRequest>) => void | Set a default factory for creating Saved Objects clients. Only one client factory can be set, subsequent calls to this method will fail. | - ## Remarks Note: The Saved Object setup API's should only be used for creating and registering client wrappers. Constructing a Saved Objects client or repository for use within your own plugin won't have any of the registered wrappers applied and is considered an anti-pattern. Use the Saved Objects client from the [SavedObjectsServiceStart\#getScopedClient](./kibana-plugin-server.savedobjectsservicestart.md) method or the [route handler context](./kibana-plugin-server.requesthandlercontext.md) instead. @@ -33,3 +24,12 @@ import {SavedObjectsClient, CoreSetup} from 'src/core/server'; export class Plugin() { setup: (core: CoreSetup) => { core.savedObjects.setClientFactory(({request: KibanaRequest}) => { return new SavedObjectsClient(core.savedObjects.scopedRepository(request)); }) } } +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [addClientWrapper](./kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) | (priority: number, id: string, factory: SavedObjectsClientWrapperFactory<KibanaRequest>) => void | Add a client wrapper with the given priority. | +| [createInternalRepository](./kibana-plugin-server.savedobjectsservicesetup.createinternalrepository.md) | (extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the internal Kibana user for authenticating with Elasticsearch. | +| [createScopedRepository](./kibana-plugin-server.savedobjectsservicesetup.createscopedrepository.md) | (req: KibanaRequest, extraTypes?: string[]) => ISavedObjectsRepository | Creates a [Saved Objects repository](./kibana-plugin-server.isavedobjectsrepository.md) that uses the credentials from the passed in request to authenticate with Elasticsearch. | +| [setClientFactory](./kibana-plugin-server.savedobjectsservicesetup.setclientfactory.md) | (customClientFactory: SavedObjectsClientFactory<KibanaRequest>) => void | Set a default factory for creating Saved Objects clients. Only one client factory can be set, subsequent calls to this method will fail. | + diff --git a/package.json b/package.json index a0f5dd3af14c0..99151f33962c4 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "**/isomorphic-git/**/base64-js": "^1.2.1", "**/image-diff/gm/debug": "^2.6.9", "**/react-dom": "^16.12.0", + "**/react": "^16.12.0", "**/react-test-renderer": "^16.12.0", "**/deepmerge": "^4.2.2", "**/serialize-javascript": "^2.1.1" @@ -113,7 +114,7 @@ "@babel/core": "^7.5.5", "@babel/register": "^7.7.0", "@elastic/apm-rum": "^4.6.0", - "@elastic/charts": "^14.0.0", + "@elastic/charts": "^16.0.2", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "1.0.5", "@elastic/eui": "17.3.1", @@ -173,6 +174,7 @@ "fast-deep-equal": "^3.1.1", "file-loader": "4.2.0", "font-awesome": "4.7.0", + "fp-ts": "^2.3.1", "getos": "^3.1.0", "glob": "^7.1.2", "glob-all": "^3.1.0", @@ -187,6 +189,7 @@ "hoek": "^5.0.4", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.2", + "immer": "^1.5.0", "inert": "^5.1.0", "inline-style": "^2.0.0", "joi": "^13.5.2", @@ -292,8 +295,8 @@ "@kbn/plugin-generator": "1.0.0", "@kbn/test": "1.0.0", "@kbn/utility-types": "1.0.0", - "@microsoft/api-documenter": "7.4.3", - "@microsoft/api-extractor": "7.4.2", + "@microsoft/api-documenter": "7.7.2", + "@microsoft/api-extractor": "7.7.0", "@percy/agent": "^0.11.0", "@testing-library/react": "^9.3.2", "@testing-library/react-hooks": "^3.2.1", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 4c5e22d20f779..7c5937af441a2 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -3102,11 +3102,25 @@ function times(n, ok, cb) { var fs = __webpack_require__(23) var polyfills = __webpack_require__(24) -var legacy = __webpack_require__(27) -var queue = [] +var legacy = __webpack_require__(26) +var clone = __webpack_require__(28) var util = __webpack_require__(29) +/* istanbul ignore next - node 0.x polyfill */ +var gracefulQueue +var previousSymbol + +/* istanbul ignore else - node 0.x polyfill */ +if (typeof Symbol === 'function' && typeof Symbol.for === 'function') { + gracefulQueue = Symbol.for('graceful-fs.queue') + // This is used in testing by future versions + previousSymbol = Symbol.for('graceful-fs.previous') +} else { + gracefulQueue = '___graceful-fs.queue' + previousSymbol = '___graceful-fs.previous' +} + function noop () {} var debug = noop @@ -3119,48 +3133,71 @@ else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) console.error(m) } -if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) { - process.on('exit', function() { - debug(queue) - __webpack_require__(30).equal(queue.length, 0) +// Once time initialization +if (!global[gracefulQueue]) { + // This queue can be shared by multiple loaded instances + var queue = [] + Object.defineProperty(global, gracefulQueue, { + get: function() { + return queue + } }) -} -module.exports = patch(__webpack_require__(25)) -if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH) { - module.exports = patch(fs) -} + // Patch fs.close/closeSync to shared queue version, because we need + // to retry() whenever a close happens *anywhere* in the program. + // This is essential when multiple graceful-fs instances are + // in play at the same time. + fs.close = (function (fs$close) { + function close (fd, cb) { + return fs$close.call(fs, fd, function (err) { + // This function uses the graceful-fs shared queue + if (!err) { + retry() + } -// Always patch fs.close/closeSync, because we want to -// retry() whenever a close happens *anywhere* in the program. -// This is essential when multiple graceful-fs instances are -// in play at the same time. -module.exports.close = -fs.close = (function (fs$close) { return function (fd, cb) { - return fs$close.call(fs, fd, function (err) { - if (!err) + if (typeof cb === 'function') + cb.apply(this, arguments) + }) + } + + Object.defineProperty(close, previousSymbol, { + value: fs$close + }) + return close + })(fs.close) + + fs.closeSync = (function (fs$closeSync) { + function closeSync (fd) { + // This function uses the graceful-fs shared queue + fs$closeSync.apply(fs, arguments) retry() + } - if (typeof cb === 'function') - cb.apply(this, arguments) - }) -}})(fs.close) + Object.defineProperty(closeSync, previousSymbol, { + value: fs$closeSync + }) + return closeSync + })(fs.closeSync) -module.exports.closeSync = -fs.closeSync = (function (fs$closeSync) { return function (fd) { - // Note that graceful-fs also retries when fs.closeSync() fails. - // Looks like a bug to me, although it's probably a harmless one. - var rval = fs$closeSync.apply(fs, arguments) - retry() - return rval -}})(fs.closeSync) + if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) { + process.on('exit', function() { + debug(global[gracefulQueue]) + __webpack_require__(30).equal(global[gracefulQueue].length, 0) + }) + } +} + +module.exports = patch(clone(fs)) +if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) { + module.exports = patch(fs) + fs.__patched = true; +} function patch (fs) { // Everything that references the open() function needs to be in here polyfills(fs) fs.gracefulify = patch - fs.FileReadStream = ReadStream; // Legacy name. - fs.FileWriteStream = WriteStream; // Legacy name. + fs.createReadStream = createReadStream fs.createWriteStream = createWriteStream var fs$readFile = fs.readFile @@ -3246,6 +3283,7 @@ function patch (fs) { if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) enqueue([go$readdir, [args]]) + else { if (typeof cb === 'function') cb.apply(this, arguments) @@ -3265,15 +3303,61 @@ function patch (fs) { } var fs$ReadStream = fs.ReadStream - ReadStream.prototype = Object.create(fs$ReadStream.prototype) - ReadStream.prototype.open = ReadStream$open + if (fs$ReadStream) { + ReadStream.prototype = Object.create(fs$ReadStream.prototype) + ReadStream.prototype.open = ReadStream$open + } var fs$WriteStream = fs.WriteStream - WriteStream.prototype = Object.create(fs$WriteStream.prototype) - WriteStream.prototype.open = WriteStream$open + if (fs$WriteStream) { + WriteStream.prototype = Object.create(fs$WriteStream.prototype) + WriteStream.prototype.open = WriteStream$open + } - fs.ReadStream = ReadStream - fs.WriteStream = WriteStream + Object.defineProperty(fs, 'ReadStream', { + get: function () { + return ReadStream + }, + set: function (val) { + ReadStream = val + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(fs, 'WriteStream', { + get: function () { + return WriteStream + }, + set: function (val) { + WriteStream = val + }, + enumerable: true, + configurable: true + }) + + // legacy names + var FileReadStream = ReadStream + Object.defineProperty(fs, 'FileReadStream', { + get: function () { + return FileReadStream + }, + set: function (val) { + FileReadStream = val + }, + enumerable: true, + configurable: true + }) + var FileWriteStream = WriteStream + Object.defineProperty(fs, 'FileWriteStream', { + get: function () { + return FileWriteStream + }, + set: function (val) { + FileWriteStream = val + }, + enumerable: true, + configurable: true + }) function ReadStream (path, options) { if (this instanceof ReadStream) @@ -3319,11 +3403,11 @@ function patch (fs) { } function createReadStream (path, options) { - return new ReadStream(path, options) + return new fs.ReadStream(path, options) } function createWriteStream (path, options) { - return new WriteStream(path, options) + return new fs.WriteStream(path, options) } var fs$open = fs.open @@ -3352,11 +3436,11 @@ function patch (fs) { function enqueue (elem) { debug('ENQUEUE', elem[0].name, elem[1]) - queue.push(elem) + global[gracefulQueue].push(elem) } function retry () { - var elem = queue.shift() + var elem = global[gracefulQueue].shift() if (elem) { debug('RETRY', elem[0].name, elem[1]) elem[0].apply(null, elem[1]) @@ -3374,8 +3458,7 @@ module.exports = require("fs"); /* 24 */ /***/ (function(module, exports, __webpack_require__) { -var fs = __webpack_require__(25) -var constants = __webpack_require__(26) +var constants = __webpack_require__(25) var origCwd = process.cwd var cwd = null @@ -3492,20 +3575,26 @@ function patch (fs) { } // if read() returns EAGAIN, then just try it again. - fs.read = (function (fs$read) { return function (fd, buffer, offset, length, position, callback_) { - var callback - if (callback_ && typeof callback_ === 'function') { - var eagCounter = 0 - callback = function (er, _, __) { - if (er && er.code === 'EAGAIN' && eagCounter < 10) { - eagCounter ++ - return fs$read.call(fs, fd, buffer, offset, length, position, callback) + fs.read = (function (fs$read) { + function read (fd, buffer, offset, length, position, callback_) { + var callback + if (callback_ && typeof callback_ === 'function') { + var eagCounter = 0 + callback = function (er, _, __) { + if (er && er.code === 'EAGAIN' && eagCounter < 10) { + eagCounter ++ + return fs$read.call(fs, fd, buffer, offset, length, position, callback) + } + callback_.apply(this, arguments) } - callback_.apply(this, arguments) } + return fs$read.call(fs, fd, buffer, offset, length, position, callback) } - return fs$read.call(fs, fd, buffer, offset, length, position, callback) - }})(fs.read) + + // This ensures `util.promisify` works as it does for native `fs.read`. + read.__proto__ = fs$read + return read + })(fs.read) fs.readSync = (function (fs$readSync) { return function (fd, buffer, offset, length, position) { var eagCounter = 0 @@ -3521,73 +3610,36 @@ function patch (fs) { } } }})(fs.readSync) -} - -function patchLchmod (fs) { - fs.lchmod = function (path, mode, callback) { - fs.open( path - , constants.O_WRONLY | constants.O_SYMLINK - , mode - , function (err, fd) { - if (err) { - if (callback) callback(err) - return - } - // prefer to return the chmod error, if one occurs, - // but still try to close, and report closing errors if they occur. - fs.fchmod(fd, mode, function (err) { - fs.close(fd, function(err2) { - if (callback) callback(err || err2) - }) - }) - }) - } - - fs.lchmodSync = function (path, mode) { - var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode) - - // prefer to return the chmod error, if one occurs, - // but still try to close, and report closing errors if they occur. - var threw = true - var ret - try { - ret = fs.fchmodSync(fd, mode) - threw = false - } finally { - if (threw) { - try { - fs.closeSync(fd) - } catch (er) {} - } else { - fs.closeSync(fd) - } - } - return ret - } -} -function patchLutimes (fs) { - if (constants.hasOwnProperty("O_SYMLINK")) { - fs.lutimes = function (path, at, mt, cb) { - fs.open(path, constants.O_SYMLINK, function (er, fd) { - if (er) { - if (cb) cb(er) + function patchLchmod (fs) { + fs.lchmod = function (path, mode, callback) { + fs.open( path + , constants.O_WRONLY | constants.O_SYMLINK + , mode + , function (err, fd) { + if (err) { + if (callback) callback(err) return } - fs.futimes(fd, at, mt, function (er) { - fs.close(fd, function (er2) { - if (cb) cb(er || er2) + // prefer to return the chmod error, if one occurs, + // but still try to close, and report closing errors if they occur. + fs.fchmod(fd, mode, function (err) { + fs.close(fd, function(err2) { + if (callback) callback(err || err2) }) }) }) } - fs.lutimesSync = function (path, at, mt) { - var fd = fs.openSync(path, constants.O_SYMLINK) - var ret + fs.lchmodSync = function (path, mode) { + var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode) + + // prefer to return the chmod error, if one occurs, + // but still try to close, and report closing errors if they occur. var threw = true + var ret try { - ret = fs.futimesSync(fd, at, mt) + ret = fs.fchmodSync(fd, mode) threw = false } finally { if (threw) { @@ -3600,151 +3652,167 @@ function patchLutimes (fs) { } return ret } + } - } else { - fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb) } - fs.lutimesSync = function () {} + function patchLutimes (fs) { + if (constants.hasOwnProperty("O_SYMLINK")) { + fs.lutimes = function (path, at, mt, cb) { + fs.open(path, constants.O_SYMLINK, function (er, fd) { + if (er) { + if (cb) cb(er) + return + } + fs.futimes(fd, at, mt, function (er) { + fs.close(fd, function (er2) { + if (cb) cb(er || er2) + }) + }) + }) + } + + fs.lutimesSync = function (path, at, mt) { + var fd = fs.openSync(path, constants.O_SYMLINK) + var ret + var threw = true + try { + ret = fs.futimesSync(fd, at, mt) + threw = false + } finally { + if (threw) { + try { + fs.closeSync(fd) + } catch (er) {} + } else { + fs.closeSync(fd) + } + } + return ret + } + + } else { + fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb) } + fs.lutimesSync = function () {} + } } -} -function chmodFix (orig) { - if (!orig) return orig - return function (target, mode, cb) { - return orig.call(fs, target, mode, function (er) { - if (chownErOk(er)) er = null - if (cb) cb.apply(this, arguments) - }) + function chmodFix (orig) { + if (!orig) return orig + return function (target, mode, cb) { + return orig.call(fs, target, mode, function (er) { + if (chownErOk(er)) er = null + if (cb) cb.apply(this, arguments) + }) + } } -} -function chmodFixSync (orig) { - if (!orig) return orig - return function (target, mode) { - try { - return orig.call(fs, target, mode) - } catch (er) { - if (!chownErOk(er)) throw er + function chmodFixSync (orig) { + if (!orig) return orig + return function (target, mode) { + try { + return orig.call(fs, target, mode) + } catch (er) { + if (!chownErOk(er)) throw er + } } } -} -function chownFix (orig) { - if (!orig) return orig - return function (target, uid, gid, cb) { - return orig.call(fs, target, uid, gid, function (er) { - if (chownErOk(er)) er = null - if (cb) cb.apply(this, arguments) - }) + function chownFix (orig) { + if (!orig) return orig + return function (target, uid, gid, cb) { + return orig.call(fs, target, uid, gid, function (er) { + if (chownErOk(er)) er = null + if (cb) cb.apply(this, arguments) + }) + } } -} -function chownFixSync (orig) { - if (!orig) return orig - return function (target, uid, gid) { - try { - return orig.call(fs, target, uid, gid) - } catch (er) { - if (!chownErOk(er)) throw er + function chownFixSync (orig) { + if (!orig) return orig + return function (target, uid, gid) { + try { + return orig.call(fs, target, uid, gid) + } catch (er) { + if (!chownErOk(er)) throw er + } } } -} + function statFix (orig) { + if (!orig) return orig + // Older versions of Node erroneously returned signed integers for + // uid + gid. + return function (target, options, cb) { + if (typeof options === 'function') { + cb = options + options = null + } + function callback (er, stats) { + if (stats) { + if (stats.uid < 0) stats.uid += 0x100000000 + if (stats.gid < 0) stats.gid += 0x100000000 + } + if (cb) cb.apply(this, arguments) + } + return options ? orig.call(fs, target, options, callback) + : orig.call(fs, target, callback) + } + } -function statFix (orig) { - if (!orig) return orig - // Older versions of Node erroneously returned signed integers for - // uid + gid. - return function (target, cb) { - return orig.call(fs, target, function (er, stats) { - if (!stats) return cb.apply(this, arguments) + function statFixSync (orig) { + if (!orig) return orig + // Older versions of Node erroneously returned signed integers for + // uid + gid. + return function (target, options) { + var stats = options ? orig.call(fs, target, options) + : orig.call(fs, target) if (stats.uid < 0) stats.uid += 0x100000000 if (stats.gid < 0) stats.gid += 0x100000000 - if (cb) cb.apply(this, arguments) - }) + return stats; + } } -} -function statFixSync (orig) { - if (!orig) return orig - // Older versions of Node erroneously returned signed integers for - // uid + gid. - return function (target) { - var stats = orig.call(fs, target) - if (stats.uid < 0) stats.uid += 0x100000000 - if (stats.gid < 0) stats.gid += 0x100000000 - return stats; - } -} + // ENOSYS means that the fs doesn't support the op. Just ignore + // that, because it doesn't matter. + // + // if there's no getuid, or if getuid() is something other + // than 0, and the error is EINVAL or EPERM, then just ignore + // it. + // + // This specific case is a silent failure in cp, install, tar, + // and most other unix tools that manage permissions. + // + // When running as root, or if other types of errors are + // encountered, then it's strict. + function chownErOk (er) { + if (!er) + return true -// ENOSYS means that the fs doesn't support the op. Just ignore -// that, because it doesn't matter. -// -// if there's no getuid, or if getuid() is something other -// than 0, and the error is EINVAL or EPERM, then just ignore -// it. -// -// This specific case is a silent failure in cp, install, tar, -// and most other unix tools that manage permissions. -// -// When running as root, or if other types of errors are -// encountered, then it's strict. -function chownErOk (er) { - if (!er) - return true + if (er.code === "ENOSYS") + return true - if (er.code === "ENOSYS") - return true + var nonroot = !process.getuid || process.getuid() !== 0 + if (nonroot) { + if (er.code === "EINVAL" || er.code === "EPERM") + return true + } - var nonroot = !process.getuid || process.getuid() !== 0 - if (nonroot) { - if (er.code === "EINVAL" || er.code === "EPERM") - return true + return false } - - return false } /***/ }), /* 25 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var fs = __webpack_require__(23) - -module.exports = clone(fs) - -function clone (obj) { - if (obj === null || typeof obj !== 'object') - return obj - - if (obj instanceof Object) - var copy = { __proto__: obj.__proto__ } - else - var copy = Object.create(null) - - Object.getOwnPropertyNames(obj).forEach(function (key) { - Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key)) - }) - - return copy -} - - -/***/ }), -/* 26 */ /***/ (function(module, exports) { module.exports = require("constants"); /***/ }), -/* 27 */ +/* 26 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(28).Stream +var Stream = __webpack_require__(27).Stream module.exports = legacy @@ -3865,11 +3933,37 @@ function legacy (fs) { /***/ }), -/* 28 */ +/* 27 */ /***/ (function(module, exports) { module.exports = require("stream"); +/***/ }), +/* 28 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = clone + +function clone (obj) { + if (obj === null || typeof obj !== 'object') + return obj + + if (obj instanceof Object) + var copy = { __proto__: obj.__proto__ } + else + var copy = Object.create(null) + + Object.getOwnPropertyNames(obj).forEach(function (key) { + Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key)) + }) + + return copy +} + + /***/ }), /* 29 */ /***/ (function(module, exports) { @@ -34687,7 +34781,7 @@ module.exports = eos; "use strict"; -const {PassThrough: PassThroughStream} = __webpack_require__(28); +const {PassThrough: PassThroughStream} = __webpack_require__(27); module.exports = options => { options = {...options}; @@ -34747,7 +34841,7 @@ module.exports = options => { "use strict"; -const { PassThrough } = __webpack_require__(28); +const { PassThrough } = __webpack_require__(27); module.exports = function (/*streams...*/) { var sources = [] @@ -37771,7 +37865,7 @@ function retry () { /* 431 */ /***/ (function(module, exports, __webpack_require__) { -var constants = __webpack_require__(26) +var constants = __webpack_require__(25) var origCwd = process.cwd var cwd = null @@ -38106,7 +38200,7 @@ function patch (fs) { /* 432 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(28).Stream +var Stream = __webpack_require__(27).Stream module.exports = legacy @@ -41741,7 +41835,7 @@ var url = __webpack_require__(454); var http = __webpack_require__(472); var https = __webpack_require__(473); var assert = __webpack_require__(30); -var Writable = __webpack_require__(28).Writable; +var Writable = __webpack_require__(27).Writable; var debug = __webpack_require__(475)("follow-redirects"); // RFC7231§4.2.1: Of the request methods defined by this specification, @@ -52721,7 +52815,7 @@ function retry () { /* 547 */ /***/ (function(module, exports, __webpack_require__) { -var constants = __webpack_require__(26) +var constants = __webpack_require__(25) var origCwd = process.cwd var cwd = null @@ -53056,7 +53150,7 @@ function patch (fs) { /* 548 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(28).Stream +var Stream = __webpack_require__(27).Stream module.exports = legacy @@ -53720,7 +53814,7 @@ function retry () { /***/ (function(module, exports, __webpack_require__) { var fs = __webpack_require__(553) -var constants = __webpack_require__(26) +var constants = __webpack_require__(25) var origCwd = process.cwd var cwd = null @@ -54083,7 +54177,7 @@ function clone (obj) { /* 554 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(28).Stream +var Stream = __webpack_require__(27).Stream module.exports = legacy @@ -57230,7 +57324,7 @@ module.exports.cli = __webpack_require__(576); -var stream = __webpack_require__(28); +var stream = __webpack_require__(27); var util = __webpack_require__(29); var fs = __webpack_require__(23); @@ -57428,7 +57522,7 @@ function lineMerger(host) { /* 573 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(28) +var Stream = __webpack_require__(27) // through // @@ -57542,7 +57636,7 @@ function through (write, end, opts) { /* 574 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(28) +var Stream = __webpack_require__(27) var writeMethods = ["write", "end", "destroy"] var readMethods = ["resume", "pause"] var readEvents = ["data", "close"] @@ -60403,7 +60497,7 @@ exports.default = Lockfile; /* 17 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(28); +module.exports = __webpack_require__(27); /***/ }), /* 18 */, @@ -69246,7 +69340,7 @@ module.exports = (...arguments_) => { * Copyright (c) 2014-2016 Teambition * Licensed under the MIT license. */ -const Stream = __webpack_require__(28) +const Stream = __webpack_require__(27) const PassThrough = Stream.PassThrough const slice = Array.prototype.slice @@ -73646,7 +73740,7 @@ exports.default = ProviderAsync; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(28); +const stream_1 = __webpack_require__(27); const fsStat = __webpack_require__(623); const fsWalk = __webpack_require__(628); const reader_1 = __webpack_require__(648); @@ -74647,7 +74741,7 @@ exports.default = Reader; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(28); +const stream_1 = __webpack_require__(27); const async_1 = __webpack_require__(630); class StreamProvider { constructor(_root, _settings) { @@ -75081,7 +75175,7 @@ exports.default = EntryTransformer; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(28); +const stream_1 = __webpack_require__(27); const stream_2 = __webpack_require__(622); const provider_1 = __webpack_require__(649); class ProviderStream extends provider_1.default { @@ -76121,7 +76215,7 @@ module.exports = path => { "use strict"; -const {Transform} = __webpack_require__(28); +const {Transform} = __webpack_require__(27); class ObjectTransform extends Transform { constructor() { @@ -76522,7 +76616,7 @@ function retry () { /* 665 */ /***/ (function(module, exports, __webpack_require__) { -var constants = __webpack_require__(26) +var constants = __webpack_require__(25) var origCwd = process.cwd var cwd = null @@ -76870,7 +76964,7 @@ function patch (fs) { /* 666 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(28).Stream +var Stream = __webpack_require__(27).Stream module.exports = legacy @@ -101698,7 +101792,7 @@ function readdirSync (dir, options, internalOptions) { "use strict"; -const Readable = __webpack_require__(28).Readable; +const Readable = __webpack_require__(27).Readable; const EventEmitter = __webpack_require__(379).EventEmitter; const path = __webpack_require__(16); const normalizeOptions = __webpack_require__(868); @@ -103099,7 +103193,7 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var stream = __webpack_require__(28); +var stream = __webpack_require__(27); var fsStat = __webpack_require__(883); var fs_1 = __webpack_require__(887); var FileSystemStream = /** @class */ (function (_super) { @@ -103333,7 +103427,7 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var stream = __webpack_require__(28); +var stream = __webpack_require__(27); var readdir = __webpack_require__(865); var reader_1 = __webpack_require__(878); var fs_stream_1 = __webpack_require__(882); diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 976eac7f95da6..6b13d0dc32d3f 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -140,23 +140,12 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { } set('plugins.scanDirs', _.compact([].concat(get('plugins.scanDirs'), opts.pluginDir))); - set( 'plugins.paths', _.compact( [].concat( get('plugins.paths'), opts.pluginPath, - opts.runExamples - ? [ - // Ideally this would automatically include all plugins in the examples dir - fromRoot('examples/demo_search'), - fromRoot('examples/search_explorer'), - fromRoot('examples/embeddable_examples'), - fromRoot('examples/embeddable_explorer'), - ] - : [], - XPACK_INSTALLED && !opts.oss ? [XPACK_DIR] : [] ) ) @@ -253,6 +242,7 @@ export default function(program) { silent: !!opts.silent, watch: !!opts.watch, repl: !!opts.repl, + runExamples: !!opts.runExamples, // We want to run without base path when the `--run-examples` flag is given so that we can use local // links in other documentation sources, like "View this tutorial [here](http://localhost:5601/app/tutorial/xyz)". // We can tell users they only have to run with `yarn start --run-examples` to get those diff --git a/src/core/server/config/__mocks__/env.ts b/src/core/server/config/__mocks__/env.ts index 644b499ff56d8..80cfab81fb557 100644 --- a/src/core/server/config/__mocks__/env.ts +++ b/src/core/server/config/__mocks__/env.ts @@ -38,6 +38,7 @@ export function getEnvOptions(options: DeepPartial = {}): EnvOptions basePath: false, optimize: false, oss: false, + runExamples: false, ...(options.cliArgs || {}), }, isDevClusterMaster: diff --git a/src/core/server/config/__snapshots__/env.test.ts.snap b/src/core/server/config/__snapshots__/env.test.ts.snap index 1f4661283de6e..204b8a70aa877 100644 --- a/src/core/server/config/__snapshots__/env.test.ts.snap +++ b/src/core/server/config/__snapshots__/env.test.ts.snap @@ -12,6 +12,7 @@ Env { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -40,7 +41,6 @@ Env { "/test/kibanaRoot/plugins", "/test/kibanaRoot/../kibana-extra", ], - "staticFilesDir": "/test/kibanaRoot/ui", } `; @@ -56,6 +56,7 @@ Env { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -84,7 +85,6 @@ Env { "/test/kibanaRoot/plugins", "/test/kibanaRoot/../kibana-extra", ], - "staticFilesDir": "/test/kibanaRoot/ui", } `; @@ -99,6 +99,7 @@ Env { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -127,7 +128,6 @@ Env { "/test/kibanaRoot/plugins", "/test/kibanaRoot/../kibana-extra", ], - "staticFilesDir": "/test/kibanaRoot/ui", } `; @@ -142,6 +142,7 @@ Env { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -170,7 +171,6 @@ Env { "/test/kibanaRoot/plugins", "/test/kibanaRoot/../kibana-extra", ], - "staticFilesDir": "/test/kibanaRoot/ui", } `; @@ -185,6 +185,7 @@ Env { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -213,7 +214,6 @@ Env { "/test/kibanaRoot/plugins", "/test/kibanaRoot/../kibana-extra", ], - "staticFilesDir": "/test/kibanaRoot/ui", } `; @@ -228,6 +228,7 @@ Env { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -256,6 +257,5 @@ Env { "/some/home/dir/plugins", "/some/home/dir/../kibana-extra", ], - "staticFilesDir": "/some/home/dir/ui", } `; diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 6a401ec6625a2..36fe95e05cb53 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -97,7 +97,6 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({ }) => [ unusedFromRoot('savedObjects.indexCheckTimeout'), unusedFromRoot('server.xsrf.token'), - unusedFromRoot('uiSettings.enabled'), renameFromRoot('optimize.lazy', 'optimize.watch'), renameFromRoot('optimize.lazyPort', 'optimize.watchPort'), renameFromRoot('optimize.lazyHost', 'optimize.watchHost'), diff --git a/src/core/server/config/env.test.ts b/src/core/server/config/env.test.ts index 5812fa93cf18f..c244012e34469 100644 --- a/src/core/server/config/env.test.ts +++ b/src/core/server/config/env.test.ts @@ -152,3 +152,25 @@ test('pluginSearchPaths does not contains x-pack plugins path if --oss flag is t expect(env.pluginSearchPaths).not.toContain('/some/home/dir/x-pack/plugins'); }); + +test('pluginSearchPaths contains examples plugins path if --run-examples flag is true', () => { + const env = new Env( + '/some/home/dir', + getEnvOptions({ + cliArgs: { runExamples: true }, + }) + ); + + expect(env.pluginSearchPaths).toContain('/some/home/dir/examples'); +}); + +test('pluginSearchPaths does not contains examples plugins path if --run-examples flag is false', () => { + const env = new Env( + '/some/home/dir', + getEnvOptions({ + cliArgs: { runExamples: false }, + }) + ); + + expect(env.pluginSearchPaths).not.toContain('/some/home/dir/examples'); +}); diff --git a/src/core/server/config/env.ts b/src/core/server/config/env.ts index 460773d89db85..db363fcd4d751 100644 --- a/src/core/server/config/env.ts +++ b/src/core/server/config/env.ts @@ -43,6 +43,7 @@ export interface CliArgs { optimize: boolean; open: boolean; oss: boolean; + runExamples: boolean; } export class Env { @@ -61,8 +62,6 @@ export class Env { /** @internal */ public readonly logDir: string; /** @internal */ - public readonly staticFilesDir: string; - /** @internal */ public readonly pluginSearchPaths: readonly string[]; /** @@ -100,14 +99,14 @@ export class Env { this.configDir = resolve(this.homeDir, 'config'); this.binDir = resolve(this.homeDir, 'bin'); this.logDir = resolve(this.homeDir, 'log'); - this.staticFilesDir = resolve(this.homeDir, 'ui'); this.pluginSearchPaths = [ resolve(this.homeDir, 'src', 'plugins'), - options.cliArgs.oss ? '' : resolve(this.homeDir, 'x-pack', 'plugins'), + ...(options.cliArgs.oss ? [] : [resolve(this.homeDir, 'x-pack', 'plugins')]), resolve(this.homeDir, 'plugins'), + ...(options.cliArgs.runExamples ? [resolve(this.homeDir, 'examples')] : []), resolve(this.homeDir, '..', 'kibana-extra'), - ].filter(Boolean); + ]; this.cliArgs = Object.freeze(options.cliArgs); this.configs = Object.freeze(options.configs); diff --git a/src/core/server/http/__snapshots__/http_config.test.ts.snap b/src/core/server/http/__snapshots__/http_config.test.ts.snap index 6c690f9da70c3..8856eb95ba722 100644 --- a/src/core/server/http/__snapshots__/http_config.test.ts.snap +++ b/src/core/server/http/__snapshots__/http_config.test.ts.snap @@ -31,11 +31,13 @@ Object { "enabled": true, }, "cors": false, + "customResponseHeaders": Object {}, "host": "localhost", "keepaliveTimeout": 120000, "maxPayload": ByteSizeValue { "valueInBytes": 1048576, }, + "name": "kibana-hostname", "port": 5601, "rewriteBasePath": false, "socketTimeout": 120000, @@ -70,6 +72,10 @@ Object { "TLSv1.2", ], }, + "xsrf": Object { + "disableProtection": false, + "whitelist": Array [], + }, } `; diff --git a/src/core/server/http/cookie_session_storage.test.ts b/src/core/server/http/cookie_session_storage.test.ts index 0e4f3972fe9dc..4ce422e1f65c4 100644 --- a/src/core/server/http/cookie_session_storage.test.ts +++ b/src/core/server/http/cookie_session_storage.test.ts @@ -58,6 +58,10 @@ configService.atPath.mockReturnValue( verificationMode: 'none', }, compression: { enabled: true }, + xsrf: { + disableProtection: true, + whitelist: [], + }, } as any) ); diff --git a/src/core/server/http/http_config.test.ts b/src/core/server/http/http_config.test.ts index 9b6fab8f3daec..3dc5fa48bc366 100644 --- a/src/core/server/http/http_config.test.ts +++ b/src/core/server/http/http_config.test.ts @@ -19,12 +19,15 @@ import uuid from 'uuid'; import { config, HttpConfig } from '.'; -import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; const validHostnames = ['www.example.com', '8.8.8.8', '::1', 'localhost']; const invalidHostname = 'asdf$%^'; +jest.mock('os', () => ({ + ...jest.requireActual('os'), + hostname: () => 'kibana-hostname', +})); + test('has defaults for config', () => { const httpSchema = config.schema; const obj = {}; @@ -86,6 +89,24 @@ test('accepts only valid uuids for server.uuid', () => { ); }); +test('uses os.hostname() as default for server.name', () => { + const httpSchema = config.schema; + const validated = httpSchema.validate({}); + expect(validated.name).toEqual('kibana-hostname'); +}); + +test('throws if xsrf.whitelist element does not start with a slash', () => { + const httpSchema = config.schema; + const obj = { + xsrf: { + whitelist: ['/valid-path', 'invalid-path'], + }, + }; + expect(() => httpSchema.validate(obj)).toThrowErrorMatchingInlineSnapshot( + `"[xsrf.whitelist.1]: must start with a slash"` + ); +}); + describe('with TLS', () => { test('throws if TLS is enabled but `key` is not specified', () => { const httpSchema = config.schema; @@ -265,8 +286,7 @@ describe('with TLS', () => { clientAuthentication: 'none', }, }), - {} as any, - Env.createDefault(getEnvOptions()) + {} as any ); expect(httpConfig.ssl.requestCert).toBe(false); @@ -283,8 +303,7 @@ describe('with TLS', () => { clientAuthentication: 'optional', }, }), - {} as any, - Env.createDefault(getEnvOptions()) + {} as any ); expect(httpConfig.ssl.requestCert).toBe(true); @@ -301,8 +320,7 @@ describe('with TLS', () => { clientAuthentication: 'required', }, }), - {} as any, - Env.createDefault(getEnvOptions()) + {} as any ); expect(httpConfig.ssl.requestCert).toBe(true); diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index ef6a9c0a5f1a5..73f44f3c5ab5c 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -18,7 +18,8 @@ */ import { ByteSizeValue, schema, TypeOf } from '@kbn/config-schema'; -import { Env } from '../config'; +import { hostname } from 'os'; + import { CspConfigType, CspConfig, ICspConfig } from '../csp'; import { SslConfig, sslSchema } from './ssl_config'; @@ -34,21 +35,13 @@ export const config = { path: 'server', schema: schema.object( { + name: schema.string({ defaultValue: () => hostname() }), autoListen: schema.boolean({ defaultValue: true }), basePath: schema.maybe( schema.string({ validate: match(validBasePathRegex, "must start with a slash, don't end with one"), }) ), - defaultRoute: schema.maybe( - schema.string({ - validate(value) { - if (!value.startsWith('/')) { - return 'must start with a slash'; - } - }, - }) - ), cors: schema.conditional( schema.contextRef('dev'), true, @@ -64,6 +57,9 @@ export const config = { ), schema.boolean({ defaultValue: false }) ), + customResponseHeaders: schema.recordOf(schema.string(), schema.string(), { + defaultValue: {}, + }), host: schema.string({ defaultValue: 'localhost', hostname: true, @@ -98,6 +94,13 @@ export const config = { validate: match(uuidRegexp, 'must be a valid uuid'), }) ), + xsrf: schema.object({ + disableProtection: schema.boolean({ defaultValue: false }), + whitelist: schema.arrayOf( + schema.string({ validate: match(/^\//, 'must start with a slash') }), + { defaultValue: [] } + ), + }), }, { validate: rawConfig => { @@ -126,38 +129,40 @@ export const config = { export type HttpConfigType = TypeOf; export class HttpConfig { + public name: string; public autoListen: boolean; public host: string; public keepaliveTimeout: number; public socketTimeout: number; public port: number; public cors: boolean | { origin: string[] }; + public customResponseHeaders: Record; public maxPayload: ByteSizeValue; public basePath?: string; public rewriteBasePath: boolean; - public publicDir: string; - public defaultRoute?: string; public ssl: SslConfig; public compression: { enabled: boolean; referrerWhitelist?: string[] }; public csp: ICspConfig; + public xsrf: { disableProtection: boolean; whitelist: string[] }; /** * @internal */ - constructor(rawHttpConfig: HttpConfigType, rawCspConfig: CspConfigType, env: Env) { + constructor(rawHttpConfig: HttpConfigType, rawCspConfig: CspConfigType) { this.autoListen = rawHttpConfig.autoListen; this.host = rawHttpConfig.host; this.port = rawHttpConfig.port; this.cors = rawHttpConfig.cors; + this.customResponseHeaders = rawHttpConfig.customResponseHeaders; this.maxPayload = rawHttpConfig.maxPayload; + this.name = rawHttpConfig.name; this.basePath = rawHttpConfig.basePath; this.keepaliveTimeout = rawHttpConfig.keepaliveTimeout; this.socketTimeout = rawHttpConfig.socketTimeout; this.rewriteBasePath = rawHttpConfig.rewriteBasePath; - this.publicDir = env.staticFilesDir; this.ssl = new SslConfig(rawHttpConfig.ssl || {}); - this.defaultRoute = rawHttpConfig.defaultRoute; this.compression = rawHttpConfig.compression; this.csp = new CspConfig(rawCspConfig); + this.xsrf = rawHttpConfig.xsrf; } } diff --git a/src/core/server/http/http_server.mocks.ts b/src/core/server/http/http_server.mocks.ts index ba742292e9e83..230a229b36888 100644 --- a/src/core/server/http/http_server.mocks.ts +++ b/src/core/server/http/http_server.mocks.ts @@ -30,6 +30,9 @@ import { RouteMethod, KibanaResponseFactory, } from './router'; +import { OnPreResponseToolkit } from './lifecycle/on_pre_response'; +import { OnPostAuthToolkit } from './lifecycle/on_post_auth'; +import { OnPreAuthToolkit } from './lifecycle/on_pre_auth'; interface RequestFixtureOptions { headers?: Record; @@ -137,9 +140,19 @@ const createLifecycleResponseFactoryMock = (): jest.Mocked; + +const createToolkitMock = (): ToolkitMock => { + return { + next: jest.fn(), + rewriteUrl: jest.fn(), + }; +}; + export const httpServerMock = { createKibanaRequest: createKibanaRequestMock, createRawRequest: createRawRequestMock, createResponseFactory: createResponseFactoryMock, createLifecycleResponseFactory: createLifecycleResponseFactoryMock, + createToolkit: createToolkitMock, }; diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index 994a6cced8914..6b978b71c6f2b 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -60,6 +60,12 @@ export interface HttpServerSetup { }; } +/** @internal */ +export type LifecycleRegistrar = Pick< + HttpServerSetup, + 'registerAuth' | 'registerOnPreAuth' | 'registerOnPostAuth' | 'registerOnPreResponse' +>; + export class HttpServer { private server?: Server; private config?: HttpConfig; diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 700ae04f00d47..6db1ca80ab437 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -68,7 +68,6 @@ const createSetupContractMock = () => { getAuthHeaders: jest.fn(), }, isTlsEnabled: false, - config: {}, }; setupContract.createCookieSessionStorageFactory.mockResolvedValue( sessionStorageMock.createFactory() diff --git a/src/core/server/http/http_service.test.mocks.ts b/src/core/server/http/http_service.test.mocks.ts index c147944f2b7d8..e18008d3b405d 100644 --- a/src/core/server/http/http_service.test.mocks.ts +++ b/src/core/server/http/http_service.test.mocks.ts @@ -27,3 +27,7 @@ jest.mock('./http_server', () => { HttpServer: mockHttpServer, }; }); + +jest.mock('./lifecycle_handlers', () => ({ + registerCoreHandlers: jest.fn(), +})); diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index faeae0b559b6b..ae9d53f9fd3db 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -21,11 +21,10 @@ import { Observable, Subscription, combineLatest } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { Server } from 'hapi'; -import { LoggerFactory } from '../logging'; import { CoreService } from '../../types'; - -import { Logger } from '../logging'; +import { Logger, LoggerFactory } from '../logging'; import { ContextSetup } from '../context'; +import { Env } from '../config'; import { CoreContext } from '../core_context'; import { PluginOpaqueId } from '../plugins'; import { CspConfigType, config as cspConfig } from '../csp'; @@ -43,6 +42,7 @@ import { } from './types'; import { RequestHandlerContext } from '../../server'; +import { registerCoreHandlers } from './lifecycle_handlers'; interface SetupDeps { context: ContextSetup; @@ -57,6 +57,7 @@ export class HttpService implements CoreService(httpConfig.path), - configService.atPath(cspConfig.path) - ).pipe(map(([http, csp]) => new HttpConfig(http, csp, env))); + configService.atPath(cspConfig.path), + ]).pipe(map(([http, csp]) => new HttpConfig(http, csp))); this.httpServer = new HttpServer(logger, 'Kibana'); this.httpsRedirectServer = new HttpsRedirectServer(logger.get('http', 'redirect', 'server')); } @@ -92,6 +94,9 @@ export class HttpService implements CoreService ) => this.requestHandlerContext!.registerContext(pluginOpaqueId, contextName, provider), - - config: { - defaultRoute: config.defaultRoute, - }, }; return contract; diff --git a/src/core/server/http/http_tools.test.ts b/src/core/server/http/http_tools.test.ts index b889ebd64971f..c1322a5aa94db 100644 --- a/src/core/server/http/http_tools.test.ts +++ b/src/core/server/http/http_tools.test.ts @@ -31,8 +31,6 @@ import { HttpConfig, config } from './http_config'; import { Router } from './router'; import { loggingServiceMock } from '../logging/logging_service.mock'; import { ByteSizeValue } from '@kbn/config-schema'; -import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; const emptyOutput = { statusCode: 400, @@ -122,8 +120,7 @@ describe('getServerOptions', () => { certificate: 'some-certificate-path', }, }), - {} as any, - Env.createDefault(getEnvOptions()) + {} as any ); expect(getServerOptions(httpConfig).tls).toMatchInlineSnapshot(` @@ -152,8 +149,7 @@ describe('getServerOptions', () => { clientAuthentication: 'required', }, }), - {} as any, - Env.createDefault(getEnvOptions()) + {} as any ); expect(getServerOptions(httpConfig).tls).toMatchInlineSnapshot(` diff --git a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts new file mode 100644 index 0000000000000..f4c5f16870c7e --- /dev/null +++ b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts @@ -0,0 +1,241 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { resolve } from 'path'; +import supertest from 'supertest'; +import { BehaviorSubject } from 'rxjs'; +import { ByteSizeValue } from '@kbn/config-schema'; + +import { createHttpServer } from '../test_utils'; +import { HttpService } from '../http_service'; +import { HttpServerSetup } from '../http_server'; +import { IRouter, RouteRegistrar } from '../router'; + +import { configServiceMock } from '../../config/config_service.mock'; +import { contextServiceMock } from '../../context/context_service.mock'; + +const pkgPath = resolve(__dirname, '../../../../../package.json'); +const actualVersion = require(pkgPath).version; +const versionHeader = 'kbn-version'; +const xsrfHeader = 'kbn-xsrf'; +const nameHeader = 'kbn-name'; +const whitelistedTestPath = '/xsrf/test/route/whitelisted'; +const kibanaName = 'my-kibana-name'; +const setupDeps = { + context: contextServiceMock.createSetupContract(), +}; + +describe('core lifecycle handlers', () => { + let server: HttpService; + let innerServer: HttpServerSetup['server']; + let router: IRouter; + + beforeEach(async () => { + const configService = configServiceMock.create(); + configService.atPath.mockReturnValue( + new BehaviorSubject({ + hosts: ['localhost'], + maxPayload: new ByteSizeValue(1024), + autoListen: true, + ssl: { + enabled: false, + }, + compression: { enabled: true }, + name: kibanaName, + customResponseHeaders: { + 'some-header': 'some-value', + }, + xsrf: { disableProtection: false, whitelist: [whitelistedTestPath] }, + } as any) + ); + server = createHttpServer({ configService }); + + const serverSetup = await server.setup(setupDeps); + router = serverSetup.createRouter('/'); + innerServer = serverSetup.server; + }, 30000); + + afterEach(async () => { + await server.stop(); + }); + + describe('versionCheck post-auth handler', () => { + const testRoute = '/version_check/test/route'; + + beforeEach(async () => { + router.get({ path: testRoute, validate: false }, (context, req, res) => { + return res.ok({ body: 'ok' }); + }); + await server.start(); + }); + + it('accepts requests with the correct version passed in the version header', async () => { + await supertest(innerServer.listener) + .get(testRoute) + .set(versionHeader, actualVersion) + .expect(200, 'ok'); + }); + + it('accepts requests that do not include a version header', async () => { + await supertest(innerServer.listener) + .get(testRoute) + .expect(200, 'ok'); + }); + + it('rejects requests with an incorrect version passed in the version header', async () => { + await supertest(innerServer.listener) + .get(testRoute) + .set(versionHeader, 'invalid-version') + .expect(400, /Browser client is out of date/); + }); + }); + + describe('customHeaders pre-response handler', () => { + const testRoute = '/custom_headers/test/route'; + const testErrorRoute = '/custom_headers/test/error_route'; + + beforeEach(async () => { + router.get({ path: testRoute, validate: false }, (context, req, res) => { + return res.ok({ body: 'ok' }); + }); + router.get({ path: testErrorRoute, validate: false }, (context, req, res) => { + return res.badRequest({ body: 'bad request' }); + }); + await server.start(); + }); + + it('adds the kbn-name header', async () => { + const result = await supertest(innerServer.listener) + .get(testRoute) + .expect(200, 'ok'); + const headers = result.header as Record; + expect(headers).toEqual( + expect.objectContaining({ + [nameHeader]: kibanaName, + }) + ); + }); + + it('adds the kbn-name header in case of error', async () => { + const result = await supertest(innerServer.listener) + .get(testErrorRoute) + .expect(400); + const headers = result.header as Record; + expect(headers).toEqual( + expect.objectContaining({ + [nameHeader]: kibanaName, + }) + ); + }); + + it('adds the custom headers', async () => { + const result = await supertest(innerServer.listener) + .get(testRoute) + .expect(200, 'ok'); + const headers = result.header as Record; + expect(headers).toEqual(expect.objectContaining({ 'some-header': 'some-value' })); + }); + + it('adds the custom headers in case of error', async () => { + const result = await supertest(innerServer.listener) + .get(testErrorRoute) + .expect(400); + const headers = result.header as Record; + expect(headers).toEqual(expect.objectContaining({ 'some-header': 'some-value' })); + }); + }); + + describe('xsrf post-auth handler', () => { + const testPath = '/xsrf/test/route'; + const destructiveMethods = ['POST', 'PUT', 'DELETE']; + const nonDestructiveMethods = ['GET', 'HEAD']; + + const getSupertest = (method: string, path: string): supertest.Test => { + return (supertest(innerServer.listener) as any)[method.toLowerCase()](path) as supertest.Test; + }; + + beforeEach(async () => { + router.get({ path: testPath, validate: false }, (context, req, res) => { + return res.ok({ body: 'ok' }); + }); + + destructiveMethods.forEach(method => { + ((router as any)[method.toLowerCase()] as RouteRegistrar)( + { path: testPath, validate: false }, + (context, req, res) => { + return res.ok({ body: 'ok' }); + } + ); + ((router as any)[method.toLowerCase()] as RouteRegistrar)( + { path: whitelistedTestPath, validate: false }, + (context, req, res) => { + return res.ok({ body: 'ok' }); + } + ); + }); + + await server.start(); + }); + + nonDestructiveMethods.forEach(method => { + describe(`When using non-destructive ${method} method`, () => { + it('accepts requests without a token', async () => { + await getSupertest(method.toLowerCase(), testPath).expect( + 200, + method === 'HEAD' ? undefined : 'ok' + ); + }); + + it('accepts requests with the xsrf header', async () => { + await getSupertest(method.toLowerCase(), testPath) + .set(xsrfHeader, 'anything') + .expect(200, method === 'HEAD' ? undefined : 'ok'); + }); + }); + }); + + destructiveMethods.forEach(method => { + describe(`When using destructive ${method} method`, () => { + it('accepts requests with the xsrf header', async () => { + await getSupertest(method.toLowerCase(), testPath) + .set(xsrfHeader, 'anything') + .expect(200, 'ok'); + }); + + it('accepts requests with the version header', async () => { + await getSupertest(method.toLowerCase(), testPath) + .set(versionHeader, actualVersion) + .expect(200, 'ok'); + }); + + it('rejects requests without either an xsrf or version header', async () => { + await getSupertest(method.toLowerCase(), testPath).expect(400, { + statusCode: 400, + error: 'Bad Request', + message: 'Request must contain a kbn-xsrf header.', + }); + }); + + it('accepts whitelisted requests without either an xsrf or version header', async () => { + await getSupertest(method.toLowerCase(), whitelistedTestPath).expect(200, 'ok'); + }); + }); + }); + }); +}); diff --git a/src/core/server/http/lifecycle_handlers.test.ts b/src/core/server/http/lifecycle_handlers.test.ts new file mode 100644 index 0000000000000..48a6973b741ba --- /dev/null +++ b/src/core/server/http/lifecycle_handlers.test.ts @@ -0,0 +1,269 @@ +/* + * 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 { + createCustomHeadersPreResponseHandler, + createVersionCheckPostAuthHandler, + createXsrfPostAuthHandler, +} from './lifecycle_handlers'; +import { httpServerMock } from './http_server.mocks'; +import { HttpConfig } from './http_config'; +import { KibanaRequest, RouteMethod } from './router'; + +const createConfig = (partial: Partial): HttpConfig => partial as HttpConfig; + +const forgeRequest = ({ + headers = {}, + path = '/', + method = 'get', +}: Partial<{ + headers: Record; + path: string; + method: RouteMethod; +}>): KibanaRequest => { + return httpServerMock.createKibanaRequest({ headers, path, method }); +}; + +describe('xsrf post-auth handler', () => { + let toolkit: ReturnType; + let responseFactory: ReturnType; + + beforeEach(() => { + toolkit = httpServerMock.createToolkit(); + responseFactory = httpServerMock.createLifecycleResponseFactory(); + }); + + describe('non destructive methods', () => { + it('accepts requests without version or xsrf header', () => { + const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const handler = createXsrfPostAuthHandler(config); + const request = forgeRequest({ method: 'get', headers: {} }); + + toolkit.next.mockReturnValue('next' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(responseFactory.badRequest).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(result).toEqual('next'); + }); + }); + + describe('destructive methods', () => { + it('accepts requests with xsrf header', () => { + const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const handler = createXsrfPostAuthHandler(config); + const request = forgeRequest({ method: 'post', headers: { 'kbn-xsrf': 'xsrf' } }); + + toolkit.next.mockReturnValue('next' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(responseFactory.badRequest).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(result).toEqual('next'); + }); + + it('accepts requests with version header', () => { + const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const handler = createXsrfPostAuthHandler(config); + const request = forgeRequest({ method: 'post', headers: { 'kbn-version': 'some-version' } }); + + toolkit.next.mockReturnValue('next' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(responseFactory.badRequest).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(result).toEqual('next'); + }); + + it('returns a bad request if called without xsrf or version header', () => { + const config = createConfig({ xsrf: { whitelist: [], disableProtection: false } }); + const handler = createXsrfPostAuthHandler(config); + const request = forgeRequest({ method: 'post' }); + + responseFactory.badRequest.mockReturnValue('badRequest' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(toolkit.next).not.toHaveBeenCalled(); + expect(responseFactory.badRequest).toHaveBeenCalledTimes(1); + expect(responseFactory.badRequest.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "body": "Request must contain a kbn-xsrf header.", + } + `); + expect(result).toEqual('badRequest'); + }); + + it('accepts requests if protection is disabled', () => { + const config = createConfig({ xsrf: { whitelist: [], disableProtection: true } }); + const handler = createXsrfPostAuthHandler(config); + const request = forgeRequest({ method: 'post', headers: {} }); + + toolkit.next.mockReturnValue('next' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(responseFactory.badRequest).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(result).toEqual('next'); + }); + + it('accepts requests if path is whitelisted', () => { + const config = createConfig({ + xsrf: { whitelist: ['/some-path'], disableProtection: false }, + }); + const handler = createXsrfPostAuthHandler(config); + const request = forgeRequest({ method: 'post', headers: {}, path: '/some-path' }); + + toolkit.next.mockReturnValue('next' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(responseFactory.badRequest).not.toHaveBeenCalled(); + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(result).toEqual('next'); + }); + }); +}); + +describe('versionCheck post-auth handler', () => { + let toolkit: ReturnType; + let responseFactory: ReturnType; + + beforeEach(() => { + toolkit = httpServerMock.createToolkit(); + responseFactory = httpServerMock.createLifecycleResponseFactory(); + }); + + it('forward the request to the next interceptor if header matches', () => { + const handler = createVersionCheckPostAuthHandler('actual-version'); + const request = forgeRequest({ headers: { 'kbn-version': 'actual-version' } }); + + toolkit.next.mockReturnValue('next' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(responseFactory.badRequest).not.toHaveBeenCalled(); + expect(result).toBe('next'); + }); + + it('returns a badRequest error if header does not match', () => { + const handler = createVersionCheckPostAuthHandler('actual-version'); + const request = forgeRequest({ headers: { 'kbn-version': 'another-version' } }); + + responseFactory.badRequest.mockReturnValue('badRequest' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(toolkit.next).not.toHaveBeenCalled(); + expect(responseFactory.badRequest).toHaveBeenCalledTimes(1); + expect(responseFactory.badRequest.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "body": Object { + "attributes": Object { + "expected": "actual-version", + "got": "another-version", + }, + "message": "Browser client is out of date, please refresh the page (\\"kbn-version\\" header was \\"another-version\\" but should be \\"actual-version\\")", + }, + } + `); + expect(result).toBe('badRequest'); + }); + + it('forward the request to the next interceptor if header is not present', () => { + const handler = createVersionCheckPostAuthHandler('actual-version'); + const request = forgeRequest({ headers: {} }); + + toolkit.next.mockReturnValue('next' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(responseFactory.badRequest).not.toHaveBeenCalled(); + expect(result).toBe('next'); + }); +}); + +describe('customHeaders pre-response handler', () => { + let toolkit: ReturnType; + + beforeEach(() => { + toolkit = httpServerMock.createToolkit(); + }); + + it('adds the kbn-name header to the response', () => { + const config = createConfig({ name: 'my-server-name' }); + const handler = createCustomHeadersPreResponseHandler(config as HttpConfig); + + handler({} as any, {} as any, toolkit); + + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(toolkit.next).toHaveBeenCalledWith({ headers: { 'kbn-name': 'my-server-name' } }); + }); + + it('adds the custom headers defined in the configuration', () => { + const config = createConfig({ + name: 'my-server-name', + customResponseHeaders: { + headerA: 'value-A', + headerB: 'value-B', + }, + }); + const handler = createCustomHeadersPreResponseHandler(config as HttpConfig); + + handler({} as any, {} as any, toolkit); + + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(toolkit.next).toHaveBeenCalledWith({ + headers: { + 'kbn-name': 'my-server-name', + headerA: 'value-A', + headerB: 'value-B', + }, + }); + }); + + it('preserve the kbn-name value from server.name if definied in custom headders ', () => { + const config = createConfig({ + name: 'my-server-name', + customResponseHeaders: { + 'kbn-name': 'custom-name', + headerA: 'value-A', + headerB: 'value-B', + }, + }); + const handler = createCustomHeadersPreResponseHandler(config as HttpConfig); + + handler({} as any, {} as any, toolkit); + + expect(toolkit.next).toHaveBeenCalledTimes(1); + expect(toolkit.next).toHaveBeenCalledWith({ + headers: { + 'kbn-name': 'my-server-name', + headerA: 'value-A', + headerB: 'value-B', + }, + }); + }); +}); diff --git a/src/core/server/http/lifecycle_handlers.ts b/src/core/server/http/lifecycle_handlers.ts new file mode 100644 index 0000000000000..ee877ee031a2b --- /dev/null +++ b/src/core/server/http/lifecycle_handlers.ts @@ -0,0 +1,93 @@ +/* + * 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 { OnPostAuthHandler } from './lifecycle/on_post_auth'; +import { OnPreResponseHandler } from './lifecycle/on_pre_response'; +import { HttpConfig } from './http_config'; +import { Env } from '../config'; +import { LifecycleRegistrar } from './http_server'; + +const VERSION_HEADER = 'kbn-version'; +const XSRF_HEADER = 'kbn-xsrf'; +const KIBANA_NAME_HEADER = 'kbn-name'; + +export const createXsrfPostAuthHandler = (config: HttpConfig): OnPostAuthHandler => { + const { whitelist, disableProtection } = config.xsrf; + + return (request, response, toolkit) => { + if (disableProtection || whitelist.includes(request.route.path)) { + return toolkit.next(); + } + + const isSafeMethod = request.route.method === 'get' || request.route.method === 'head'; + const hasVersionHeader = VERSION_HEADER in request.headers; + const hasXsrfHeader = XSRF_HEADER in request.headers; + + if (!isSafeMethod && !hasVersionHeader && !hasXsrfHeader) { + return response.badRequest({ body: `Request must contain a ${XSRF_HEADER} header.` }); + } + + return toolkit.next(); + }; +}; + +export const createVersionCheckPostAuthHandler = (kibanaVersion: string): OnPostAuthHandler => { + return (request, response, toolkit) => { + const requestVersion = request.headers[VERSION_HEADER]; + if (requestVersion && requestVersion !== kibanaVersion) { + return response.badRequest({ + body: { + message: + `Browser client is out of date, please refresh the page ` + + `("${VERSION_HEADER}" header was "${requestVersion}" but should be "${kibanaVersion}")`, + attributes: { + expected: kibanaVersion, + got: requestVersion, + }, + }, + }); + } + + return toolkit.next(); + }; +}; + +export const createCustomHeadersPreResponseHandler = (config: HttpConfig): OnPreResponseHandler => { + const serverName = config.name; + const customHeaders = config.customResponseHeaders; + + return (request, response, toolkit) => { + const additionalHeaders = { + ...customHeaders, + [KIBANA_NAME_HEADER]: serverName, + }; + + return toolkit.next({ headers: additionalHeaders }); + }; +}; + +export const registerCoreHandlers = ( + registrar: LifecycleRegistrar, + config: HttpConfig, + env: Env +) => { + registrar.registerOnPreResponse(createCustomHeadersPreResponseHandler(config)); + registrar.registerOnPostAuth(createXsrfPostAuthHandler(config)); + registrar.registerOnPostAuth(createVersionCheckPostAuthHandler(env.packageInfo.version)); +}; diff --git a/src/core/server/http/test_utils.ts b/src/core/server/http/test_utils.ts index e0a15cdc6e839..ffdc04d156ca0 100644 --- a/src/core/server/http/test_utils.ts +++ b/src/core/server/http/test_utils.ts @@ -41,6 +41,10 @@ configService.atPath.mockReturnValue( enabled: false, }, compression: { enabled: true }, + xsrf: { + disableProtection: true, + whitelist: [], + }, } as any) ); diff --git a/src/core/server/http/types.ts b/src/core/server/http/types.ts index 92217515a22a1..9c8bfc073a524 100644 --- a/src/core/server/http/types.ts +++ b/src/core/server/http/types.ts @@ -250,16 +250,6 @@ export interface InternalHttpServiceSetup contextName: T, provider: RequestHandlerContextProvider ) => RequestHandlerContextContainer; - config: { - /** - * @internalRemarks - * Deprecated part of the server config, provided until - * /~https://github.com/elastic/kibana/issues/40255 - * - * @deprecated - * */ - defaultRoute?: string; - }; } /** @public */ diff --git a/src/core/server/internal_types.ts b/src/core/server/internal_types.ts index be4d830c55eab..ff68d1544d119 100644 --- a/src/core/server/internal_types.ts +++ b/src/core/server/internal_types.ts @@ -17,7 +17,10 @@ * under the License. */ +import { Type } from '@kbn/config-schema'; + import { CapabilitiesSetup, CapabilitiesStart } from './capabilities'; +import { ConfigDeprecationProvider } from './config'; import { ContextSetup } from './context'; import { InternalElasticsearchServiceSetup } from './elasticsearch'; import { InternalHttpServiceSetup } from './http'; @@ -47,3 +50,18 @@ export interface InternalCoreStart { savedObjects: InternalSavedObjectsServiceStart; uiSettings: InternalUiSettingsServiceStart; } + +/** + * @internal + */ +export interface ServiceConfigDescriptor { + path: string; + /** + * Schema to use to validate the configuration. + */ + schema: Type; + /** + * Provider for the {@link ConfigDeprecation} to apply to the plugin configuration. + */ + deprecations?: ConfigDeprecationProvider; +} diff --git a/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap b/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap index 0ebd8b8371628..74ecaa9f09c0e 100644 --- a/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap +++ b/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap @@ -8,10 +8,13 @@ Object { "enabled": true, }, "cors": false, - "defaultRoute": undefined, + "customResponseHeaders": Object { + "custom-header": "custom-value", + }, "host": "host", "keepaliveTimeout": 5000, "maxPayload": 1000, + "name": "kibana-hostname", "port": 1234, "rewriteBasePath": false, "socketTimeout": 2000, @@ -21,6 +24,10 @@ Object { "someNewValue": "new", }, "uuid": undefined, + "xsrf": Object { + "disableProtection": false, + "whitelist": Array [], + }, } `; @@ -32,10 +39,13 @@ Object { "enabled": true, }, "cors": false, - "defaultRoute": undefined, + "customResponseHeaders": Object { + "custom-header": "custom-value", + }, "host": "host", "keepaliveTimeout": 5000, "maxPayload": 1000, + "name": "kibana-hostname", "port": 1234, "rewriteBasePath": false, "socketTimeout": 2000, @@ -45,6 +55,10 @@ Object { "key": "key", }, "uuid": undefined, + "xsrf": Object { + "disableProtection": false, + "whitelist": Array [], + }, } `; diff --git a/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts b/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts index db2bc117280ca..1c51564187442 100644 --- a/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts +++ b/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts @@ -80,9 +80,11 @@ describe('#get', () => { test('correctly handles server config.', () => { const configAdapter = new LegacyObjectToConfigAdapter({ server: { + name: 'kibana-hostname', autoListen: true, basePath: '/abc', cors: false, + customResponseHeaders: { 'custom-header': 'custom-value' }, host: 'host', maxPayloadBytes: 1000, keepaliveTimeout: 5000, @@ -92,14 +94,20 @@ describe('#get', () => { ssl: { enabled: true, keyPassphrase: 'some-phrase', someNewValue: 'new' }, compression: { enabled: true }, someNotSupportedValue: 'val', + xsrf: { + disableProtection: false, + whitelist: [], + }, }, }); const configAdapterWithDisabledSSL = new LegacyObjectToConfigAdapter({ server: { + name: 'kibana-hostname', autoListen: true, basePath: '/abc', cors: false, + customResponseHeaders: { 'custom-header': 'custom-value' }, host: 'host', maxPayloadBytes: 1000, keepaliveTimeout: 5000, @@ -109,6 +117,10 @@ describe('#get', () => { ssl: { enabled: false, certificate: 'cert', key: 'key' }, compression: { enabled: true }, someNotSupportedValue: 'val', + xsrf: { + disableProtection: false, + whitelist: [], + }, }, }); diff --git a/src/core/server/legacy/config/legacy_object_to_config_adapter.ts b/src/core/server/legacy/config/legacy_object_to_config_adapter.ts index bdcde8262ef98..30bb150e6c15a 100644 --- a/src/core/server/legacy/config/legacy_object_to_config_adapter.ts +++ b/src/core/server/legacy/config/legacy_object_to_config_adapter.ts @@ -60,14 +60,15 @@ export class LegacyObjectToConfigAdapter extends ObjectToConfigAdapter { private static transformServer(configValue: any = {}) { // TODO: New platform uses just a subset of `server` config from the legacy platform, - // new values will be exposed once we need them (eg. customResponseHeaders or xsrf). + // new values will be exposed once we need them return { autoListen: configValue.autoListen, basePath: configValue.basePath, - defaultRoute: configValue.defaultRoute, cors: configValue.cors, + customResponseHeaders: configValue.customResponseHeaders, host: configValue.host, maxPayload: configValue.maxPayloadBytes, + name: configValue.name, port: configValue.port, rewriteBasePath: configValue.rewriteBasePath, ssl: configValue.ssl, @@ -75,6 +76,7 @@ export class LegacyObjectToConfigAdapter extends ObjectToConfigAdapter { socketTimeout: configValue.socketTimeout, compression: configValue.compression, uuid: configValue.uuid, + xsrf: configValue.xsrf, }; } diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 2ed87f4c6d488..e17de7364ce59 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -84,7 +84,7 @@ export class LegacyService implements CoreService { private settings?: LegacyVars; constructor(private readonly coreContext: CoreContext) { - const { logger, configService, env } = coreContext; + const { logger, configService } = coreContext; this.log = logger.get('legacy-service'); this.devConfig$ = configService @@ -93,7 +93,7 @@ export class LegacyService implements CoreService { this.httpConfig$ = combineLatest( configService.atPath(httpConfig.path), configService.atPath(cspConfig.path) - ).pipe(map(([http, csp]) => new HttpConfig(http, csp, env))); + ).pipe(map(([http, csp]) => new HttpConfig(http, csp))); } public async discoverPlugins(): Promise { diff --git a/src/core/server/plugins/discovery/plugins_discovery.test.ts b/src/core/server/plugins/discovery/plugins_discovery.test.ts index bf55fc7caae4c..2902aafdbf146 100644 --- a/src/core/server/plugins/discovery/plugins_discovery.test.ts +++ b/src/core/server/plugins/discovery/plugins_discovery.test.ts @@ -18,13 +18,14 @@ */ import { mockPackage, mockReaddir, mockReadFile, mockStat } from './plugins_discovery.test.mocks'; +import { rawConfigServiceMock } from '../../config/raw_config_service.mock'; +import { loggingServiceMock } from '../../logging/logging_service.mock'; import { resolve } from 'path'; import { first, map, toArray } from 'rxjs/operators'; + import { ConfigService, Env } from '../../config'; -import { rawConfigServiceMock } from '../../config/raw_config_service.mock'; import { getEnvOptions } from '../../config/__mocks__/env'; -import { loggingServiceMock } from '../../logging/logging_service.mock'; import { PluginWrapper } from '../plugin'; import { PluginsConfig, PluginsConfigType, config } from '../plugins_config'; import { discover } from './plugins_discovery'; @@ -37,6 +38,7 @@ const TEST_PLUGIN_SEARCH_PATHS = { const TEST_EXTRA_PLUGIN_PATH = resolve(process.cwd(), 'my-extra-plugin'); const logger = loggingServiceMock.create(); + beforeEach(() => { mockReaddir.mockImplementation((path, cb) => { if (path === TEST_PLUGIN_SEARCH_PATHS.nonEmptySrcPlugins) { @@ -182,12 +184,84 @@ test('properly iterates through plugin search locations', async () => { 'kibana.json' )})`, ]); +}); + +test('logs a warning about --plugin-path when used in development', async () => { + mockPackage.raw = { + branch: 'master', + version: '1.2.3', + build: { + distributable: true, + number: 1, + sha: '', + }, + }; + + const env = Env.createDefault( + getEnvOptions({ + cliArgs: { dev: false, envName: 'development' }, + }) + ); + const configService = new ConfigService( + rawConfigServiceMock.create({ rawConfig: { plugins: { paths: [TEST_EXTRA_PLUGIN_PATH] } } }), + env, + logger + ); + await configService.setSchema(config.path, config.schema); + + const rawConfig = await configService + .atPath('plugins') + .pipe(first()) + .toPromise(); + + discover(new PluginsConfig(rawConfig, env), { + coreId: Symbol(), + configService, + env, + logger, + }); + + expect(loggingServiceMock.collect(logger).warn).toEqual([ + [ + `Explicit plugin paths [${TEST_EXTRA_PLUGIN_PATH}] should only be used in development. Relative imports may not work properly in production.`, + ], + ]); +}); + +test('does not log a warning about --plugin-path when used in production', async () => { + mockPackage.raw = { + branch: 'master', + version: '1.2.3', + build: { + distributable: true, + number: 1, + sha: '', + }, + }; + + const env = Env.createDefault( + getEnvOptions({ + cliArgs: { dev: false, envName: 'production' }, + }) + ); + const configService = new ConfigService( + rawConfigServiceMock.create({ rawConfig: { plugins: { paths: [TEST_EXTRA_PLUGIN_PATH] } } }), + env, + logger + ); + await configService.setSchema(config.path, config.schema); + + const rawConfig = await configService + .atPath('plugins') + .pipe(first()) + .toPromise(); + + discover(new PluginsConfig(rawConfig, env), { + coreId: Symbol(), + configService, + env, + logger, + }); - expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(` -Array [ - Array [ - "Explicit plugin paths [${TEST_EXTRA_PLUGIN_PATH}] are only supported in development. Relative imports will not work in production.", - ], -] -`); + expect(loggingServiceMock.collect(logger).warn).toEqual([]); }); diff --git a/src/core/server/plugins/discovery/plugins_discovery.ts b/src/core/server/plugins/discovery/plugins_discovery.ts index 521d02e487df6..79238afdf5c81 100644 --- a/src/core/server/plugins/discovery/plugins_discovery.ts +++ b/src/core/server/plugins/discovery/plugins_discovery.ts @@ -46,9 +46,9 @@ export function discover(config: PluginsConfig, coreContext: CoreContext) { const log = coreContext.logger.get('plugins-discovery'); log.debug('Discovering plugins...'); - if (config.additionalPluginPaths.length) { + if (config.additionalPluginPaths.length && coreContext.env.mode.dev) { log.warn( - `Explicit plugin paths [${config.additionalPluginPaths}] are only supported in development. Relative imports will not work in production.` + `Explicit plugin paths [${config.additionalPluginPaths}] should only be used in development. Relative imports may not work properly in production.` ); } diff --git a/src/core/server/plugins/plugins_config.test.ts b/src/core/server/plugins/plugins_config.test.ts new file mode 100644 index 0000000000000..180d6093e0404 --- /dev/null +++ b/src/core/server/plugins/plugins_config.test.ts @@ -0,0 +1,44 @@ +/* + * 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 { PluginsConfig, PluginsConfigType } from './plugins_config'; +import { Env } from '../config'; +import { getEnvOptions } from '../config/__mocks__/env'; + +describe('PluginsConfig', () => { + it('retrieves additionalPluginPaths from config.paths when in production mode', () => { + const env = Env.createDefault(getEnvOptions({ cliArgs: { dev: false } })); + const rawConfig: PluginsConfigType = { + initialize: true, + paths: ['some-path', 'another-path'], + }; + const config = new PluginsConfig(rawConfig, env); + expect(config.additionalPluginPaths).toEqual(['some-path', 'another-path']); + }); + + it('retrieves additionalPluginPaths from config.paths when in development mode', () => { + const env = Env.createDefault(getEnvOptions({ cliArgs: { dev: true } })); + const rawConfig: PluginsConfigType = { + initialize: true, + paths: ['some-path', 'another-path'], + }; + const config = new PluginsConfig(rawConfig, env); + expect(config.additionalPluginPaths).toEqual(['some-path', 'another-path']); + }); +}); diff --git a/src/core/server/plugins/plugins_config.ts b/src/core/server/plugins/plugins_config.ts index 93ae229737fae..8ebcc92672dde 100644 --- a/src/core/server/plugins/plugins_config.ts +++ b/src/core/server/plugins/plugins_config.ts @@ -29,7 +29,6 @@ export const config = { /** * Defines an array of directories where another plugin should be loaded from. - * Should only be used in a development environment. */ paths: schema.arrayOf(schema.string(), { defaultValue: [] }), }), @@ -55,7 +54,6 @@ export class PluginsConfig { constructor(rawConfig: PluginsConfigType, env: Env) { this.initialize = rawConfig.initialize; this.pluginSearchPaths = env.pluginSearchPaths; - // Only allow custom plugin paths in dev. - this.additionalPluginPaths = env.mode.dev ? rawConfig.paths : []; + this.additionalPluginPaths = rawConfig.paths; } } diff --git a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap index edde1dee85f4f..5e6e977663bc4 100644 --- a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap +++ b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap @@ -18,6 +18,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -39,7 +40,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/mock-server-basepath/translations/en.json", @@ -89,6 +89,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -110,7 +111,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/mock-server-basepath/translations/en.json", @@ -160,6 +160,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -181,7 +182,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/mock-server-basepath/translations/en.json", @@ -235,6 +235,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -256,7 +257,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/translations/en.json", @@ -306,6 +306,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -327,7 +328,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/mock-server-basepath/translations/en.json", @@ -377,6 +377,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -398,7 +399,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/mock-server-basepath/translations/en.json", @@ -448,6 +448,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -469,7 +470,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/translations/en.json", @@ -519,6 +519,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -540,7 +541,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/mock-server-basepath/translations/en.json", @@ -592,6 +592,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -613,7 +614,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/mock-server-basepath/translations/en.json", @@ -663,6 +663,7 @@ Object { "oss": false, "quiet": false, "repl": false, + "runExamples": false, "silent": false, "watch": false, }, @@ -684,7 +685,6 @@ Object { "version": Any, }, "pluginSearchPaths": Any, - "staticFilesDir": Any, }, "i18n": Object { "translationsUrl": "/mock-server-basepath/translations/en.json", diff --git a/src/core/server/rendering/rendering_service.test.ts b/src/core/server/rendering/rendering_service.test.ts index 63145f2b30573..43ff4f633085c 100644 --- a/src/core/server/rendering/rendering_service.test.ts +++ b/src/core/server/rendering/rendering_service.test.ts @@ -41,7 +41,6 @@ const INJECTED_METADATA = { version: expect.any(String), }, pluginSearchPaths: expect.any(Array), - staticFilesDir: expect.any(String), }, legacyMetadata: { branch: expect.any(String), @@ -50,6 +49,7 @@ const INJECTED_METADATA = { version: expect.any(String), }, }; + const { createKibanaRequest, createRawRequest } = httpServerMock; const legacyApp = { getId: () => 'legacy' }; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 48293eda44d33..35359c5d6c417 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1,1980 +1,1979 @@ -## 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 { RecursiveReadonly as RecursiveReadonly_2 } from 'kibana/public'; -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 { SchemaTypeError } from '@kbn/config-schema'; -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 -export type ConfigDeprecation = (config: Record, fromPath: string, logger: ConfigDeprecationLogger) => Record; - -// @public -export interface ConfigDeprecationFactory { - rename(oldKey: string, newKey: string): ConfigDeprecation; - renameFromRoot(oldKey: string, newKey: string): ConfigDeprecation; - unused(unusedKey: string): ConfigDeprecation; - unusedFromRoot(unusedKey: string): ConfigDeprecation; -} - -// @public -export type ConfigDeprecationLogger = (message: string) => void; - -// @public -export type ConfigDeprecationProvider = (factory: ConfigDeprecationFactory) => ConfigDeprecation[]; - -// @public (undocumented) -export type ConfigPath = string | string[]; - -// @internal (undocumented) -export class ConfigService { - // Warning: (ae-forgotten-export) The symbol "RawConfigurationProvider" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "Env" needs to be exported by the entry point index.d.ts - constructor(rawConfigProvider: RawConfigurationProvider, env: Env, logger: LoggerFactory); - addDeprecationProvider(path: ConfigPath, provider: ConfigDeprecationProvider): void; - atPath(path: ConfigPath): Observable; - // Warning: (ae-forgotten-export) The symbol "Config" needs to be exported by the entry point index.d.ts - getConfig$(): Observable; - // (undocumented) - getUnusedPaths(): Promise; - // (undocumented) - getUsedPaths(): Promise; - // (undocumented) - isEnabledAtPath(path: ConfigPath): Promise; - optionalAtPath(path: ConfigPath): Observable; - setSchema(path: ConfigPath, schema: Type): Promise; - validate(): Promise; - } - -// @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; - // (undocumented) - uuid: UuidServiceSetup; -} - -// @public -export interface CoreStart { - // (undocumented) - capabilities: CapabilitiesStart; - // (undocumented) - savedObjects: SavedObjectsServiceStart; - // (undocumented) - uiSettings: UiSettingsServiceStart; -} - -// @public -export class CspConfig implements ICspConfig { - // @internal - constructor(rawCspConfig?: Partial>); - // (undocumented) - static readonly DEFAULT: CspConfig; - // (undocumented) - readonly header: string; - // (undocumented) - readonly rules: string[]; - // (undocumented) - readonly strict: boolean; - // (undocumented) - readonly warnLegacyBrowsers: boolean; -} - -// @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; - csp: ICspConfig; - isTlsEnabled: boolean; - registerAuth: (handler: AuthenticationHandler) => void; - registerOnPostAuth: (handler: OnPostAuthHandler) => void; - registerOnPreAuth: (handler: OnPreAuthHandler) => void; - registerOnPreResponse: (handler: OnPreResponseHandler) => 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 ICspConfig { - readonly header: string; - readonly rules: string[]; - readonly strict: boolean; - readonly warnLegacyBrowsers: boolean; -} - -// @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 (undocumented) -export interface IRenderOptions { - includeUserSettings?: boolean; -} - -// @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 (undocumented) -export interface IScopedRenderingClient { - render(options?: IRenderOptions): Promise; -} - -// @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; - // Warning: (ae-forgotten-export) The symbol "RouteValidator" needs to be exported by the entry point index.d.ts - // - // @internal - static from(req: Request, routeSchemas?: RouteValidator | RouteValidatorFullConfig, 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; - -// @internal @deprecated -export interface LegacyConfig { - // (undocumented) - get(key?: string): T; - // (undocumented) - has(key: string): boolean; - // (undocumented) - set(key: string, value: any): void; - // Warning: (ae-forgotten-export) The symbol "LegacyVars" needs to be exported by the entry point index.d.ts - // - // (undocumented) - set(config: LegacyVars): void; -} - -// Warning: (ae-forgotten-export) The symbol "ILegacyInternals" needs to be exported by the entry point index.d.ts -// -// @internal @deprecated (undocumented) -export class LegacyInternals implements ILegacyInternals { - constructor(uiExports: LegacyUiExports, config: LegacyConfig, server: Server); - // (undocumented) - getInjectedUiAppVars(id: string): Promise>; - // (undocumented) - getVars(id: string, request: LegacyRequest, injected?: LegacyVars): Promise>; - private get defaultVars(); - // Warning: (ae-forgotten-export) The symbol "VarsInjector" needs to be exported by the entry point index.d.ts - // - // (undocumented) - injectUiAppVars(id: string, injector: VarsInjector): void; - } - -// @internal @deprecated (undocumented) -export interface LegacyRenderOptions extends IRenderOptions { - app?: { - getId(): string; - }; - vars?: Record; -} - -// @public @deprecated (undocumented) -export interface LegacyRequest extends Request { -} - -// Warning: (ae-forgotten-export) The symbol "LegacyPlugins" needs to be exported by the entry point index.d.ts -// -// @internal @deprecated (undocumented) -export interface LegacyServiceDiscoverPlugins extends LegacyPlugins { - // (undocumented) - pluginExtendedConfig: LegacyConfig; - // (undocumented) - settings: LegacyVars; -} - -// @public @deprecated (undocumented) -export interface LegacyServiceSetupDeps { - // Warning: (ae-forgotten-export) The symbol "LegacyCoreSetup" needs to be exported by the entry point index.d.ts - // - // (undocumented) - core: LegacyCoreSetup; - // (undocumented) - plugins: Record; -} - -// @public @deprecated (undocumented) -export interface LegacyServiceStartDeps { - // Warning: (ae-forgotten-export) The symbol "LegacyCoreStart" needs to be exported by the entry point index.d.ts - // - // (undocumented) - core: LegacyCoreStart; - // (undocumented) - plugins: Record; -} - -// Warning: (ae-forgotten-export) The symbol "SavedObjectsLegacyUiExports" needs to be exported by the entry point index.d.ts -// -// @internal @deprecated (undocumented) -export type LegacyUiExports = SavedObjectsLegacyUiExports & { - defaultInjectedVarProviders?: VarsProvider[]; - injectedVarsReplacers?: VarsReplacer[]; - navLinkSpecs?: LegacyNavLinkSpec[] | null; - uiAppSpecs?: Array; - unknown?: [{ - pluginSpec: LegacyPluginSpec; - type: unknown; - }]; -}; - -// 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; - get(...childContextPaths: string[]): Logger; - info(message: string, meta?: LogMeta): void; - // @internal (undocumented) - log(record: LogRecord): void; - trace(message: string, meta?: LogMeta): void; - warn(errorOrMessage: string | Error, meta?: LogMeta): void; -} - -// @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 -export interface OnPreResponseExtensions { - headers?: ResponseHeaders; -} - -// Warning: (ae-forgotten-export) The symbol "OnPreResponseResult" needs to be exported by the entry point index.d.ts -// -// @public -export type OnPreResponseHandler = (request: KibanaRequest, preResponse: OnPreResponseInfo, toolkit: OnPreResponseToolkit) => OnPreResponseResult | Promise; - -// @public -export interface OnPreResponseInfo { - // (undocumented) - statusCode: number; -} - -// @public -export interface OnPreResponseToolkit { - next: (responseExtensions?: OnPreResponseExtensions) => OnPreResponseResult; -} - -// @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 { - deprecations?: ConfigDeprecationProvider; - 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: { - legacy: { - globalConfig$: Observable; - }; - 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; - }; -}; - -// @internal (undocumented) -export interface RenderingServiceSetup { - render(request: R, uiSettings: IUiSettingsClient, options?: R extends LegacyRequest ? LegacyRenderOptions : IRenderOptions): Promise; -} - -// @public -export type RequestHandler

= (context: RequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory) => IKibanaResponse | Promise>; - -// @public -export interface RequestHandlerContext { - // (undocumented) - core: { - rendering: IScopedRenderingClient; - 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 { - options?: RouteConfigOptions; - path: string; - validate: RouteValidatorFullConfig | 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 = (route: RouteConfig, handler: RequestHandler) => void; - -// @public -export class RouteValidationError extends SchemaTypeError { - constructor(error: Error | string, path?: string[]); -} - -// @public -export type RouteValidationFunction = (data: any, validationResult: RouteValidationResultFactory) => { - value: T; - error?: never; -} | { - value?: never; - error: RouteValidationError; -}; - -// @public -export interface RouteValidationResultFactory { - // (undocumented) - badRequest: (error: Error | string, path?: string[]) => { - error: RouteValidationError; - }; - // (undocumented) - ok: (value: T) => { - value: T; - }; -} - -// @public -export type RouteValidationSpec = ObjectType | Type | RouteValidationFunction; - -// @public -export interface RouteValidatorConfig { - body?: RouteValidationSpec; - params?: RouteValidationSpec

; - query?: RouteValidationSpec; -} - -// @public -export type RouteValidatorFullConfig = RouteValidatorConfig & RouteValidatorOptions; - -// @public -export interface RouteValidatorOptions { - unsafe?: { - params?: boolean; - query?: boolean; - body?: boolean; - }; -} - -// @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 { - 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>; - // Warning: (ae-forgotten-export) The symbol "KibanaMigrator" needs to be exported by the entry point index.d.ts - // - // @internal - static createRepository(migrator: KibanaMigrator, schema: SavedObjectsSchema, config: LegacyConfig, indexName: string, callCluster: APICaller, extraTypes?: string[], injectedConstructor?: any): any; - 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: LegacyConfig, 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 (undocumented) -export type SharedGlobalConfig = RecursiveReadonly_2<{ - kibana: Pick; - elasticsearch: Pick; - path: Pick; -}>; - -// @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 (undocumented) -export interface UiSettingsServiceStart { - asScopedToClient(savedObjectsClient: SavedObjectsClientContract): IUiSettingsClient; -} - -// @public -export type UiSettingsType = 'json' | 'markdown' | 'number' | 'select' | 'boolean' | 'string'; - -// @public -export interface UserProvidedValues { - // (undocumented) - isOverridden?: boolean; - // (undocumented) - userValue?: T; -} - -// @public -export interface UuidServiceSetup { - getInstanceUuid(): string; -} - -// @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/legacy/types.ts:158:3 - (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:159:3 - (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:160:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:161:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:162:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/plugins_service.ts:43:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:221:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:221:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:222:3 - (ae-forgotten-export) The symbol "ElasticsearchConfigType" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:223:3 - (ae-forgotten-export) The symbol "PathConfigType" 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 { RecursiveReadonly as RecursiveReadonly_2 } from 'kibana/public'; +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 { SchemaTypeError } from '@kbn/config-schema'; +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: 'bulk', params: BulkIndexDocumentsParams, options?: CallAPIOptions): ReturnType; + // (undocumented) + (endpoint: 'clearScroll', params: ClearScrollParams, 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: 'cluster.state', params: ClusterStateParams, 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 +export type ConfigDeprecation = (config: Record, fromPath: string, logger: ConfigDeprecationLogger) => Record; + +// @public +export interface ConfigDeprecationFactory { + rename(oldKey: string, newKey: string): ConfigDeprecation; + renameFromRoot(oldKey: string, newKey: string): ConfigDeprecation; + unused(unusedKey: string): ConfigDeprecation; + unusedFromRoot(unusedKey: string): ConfigDeprecation; +} + +// @public +export type ConfigDeprecationLogger = (message: string) => void; + +// @public +export type ConfigDeprecationProvider = (factory: ConfigDeprecationFactory) => ConfigDeprecation[]; + +// @public (undocumented) +export type ConfigPath = string | string[]; + +// @internal (undocumented) +export class ConfigService { + // Warning: (ae-forgotten-export) The symbol "RawConfigurationProvider" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "Env" needs to be exported by the entry point index.d.ts + constructor(rawConfigProvider: RawConfigurationProvider, env: Env, logger: LoggerFactory); + addDeprecationProvider(path: ConfigPath, provider: ConfigDeprecationProvider): void; + atPath(path: ConfigPath): Observable; + // Warning: (ae-forgotten-export) The symbol "Config" needs to be exported by the entry point index.d.ts + getConfig$(): Observable; + // (undocumented) + getUnusedPaths(): Promise; + // (undocumented) + getUsedPaths(): Promise; + // (undocumented) + isEnabledAtPath(path: ConfigPath): Promise; + optionalAtPath(path: ConfigPath): Observable; + setSchema(path: ConfigPath, schema: Type): Promise; + validate(): Promise; + } + +// @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; + // (undocumented) + uuid: UuidServiceSetup; +} + +// @public +export interface CoreStart { + // (undocumented) + capabilities: CapabilitiesStart; + // (undocumented) + savedObjects: SavedObjectsServiceStart; + // (undocumented) + uiSettings: UiSettingsServiceStart; +} + +// @public +export class CspConfig implements ICspConfig { + // @internal + constructor(rawCspConfig?: Partial>); + // (undocumented) + static readonly DEFAULT: CspConfig; + // (undocumented) + readonly header: string; + // (undocumented) + readonly rules: string[]; + // (undocumented) + readonly strict: boolean; + // (undocumented) + readonly warnLegacyBrowsers: boolean; +} + +// @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; + csp: ICspConfig; + isTlsEnabled: boolean; + registerAuth: (handler: AuthenticationHandler) => void; + registerOnPostAuth: (handler: OnPostAuthHandler) => void; + registerOnPreAuth: (handler: OnPreAuthHandler) => void; + registerOnPreResponse: (handler: OnPreResponseHandler) => 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 ICspConfig { + readonly header: string; + readonly rules: string[]; + readonly strict: boolean; + readonly warnLegacyBrowsers: boolean; +} + +// @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 (undocumented) +export interface IRenderOptions { + includeUserSettings?: boolean; +} + +// @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 (undocumented) +export interface IScopedRenderingClient { + render(options?: IRenderOptions): Promise; +} + +// @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; + // Warning: (ae-forgotten-export) The symbol "RouteValidator" needs to be exported by the entry point index.d.ts + // + // @internal + static from(req: Request, routeSchemas?: RouteValidator | RouteValidatorFullConfig, 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; + +// @internal @deprecated +export interface LegacyConfig { + // (undocumented) + get(key?: string): T; + // (undocumented) + has(key: string): boolean; + // (undocumented) + set(key: string, value: any): void; + // Warning: (ae-forgotten-export) The symbol "LegacyVars" needs to be exported by the entry point index.d.ts + // + // (undocumented) + set(config: LegacyVars): void; +} + +// Warning: (ae-forgotten-export) The symbol "ILegacyInternals" needs to be exported by the entry point index.d.ts +// +// @internal @deprecated (undocumented) +export class LegacyInternals implements ILegacyInternals { + constructor(uiExports: LegacyUiExports, config: LegacyConfig, server: Server); + // (undocumented) + getInjectedUiAppVars(id: string): Promise>; + // (undocumented) + getVars(id: string, request: LegacyRequest, injected?: LegacyVars): Promise>; + // Warning: (ae-forgotten-export) The symbol "VarsInjector" needs to be exported by the entry point index.d.ts + // + // (undocumented) + injectUiAppVars(id: string, injector: VarsInjector): void; + } + +// @internal @deprecated (undocumented) +export interface LegacyRenderOptions extends IRenderOptions { + app?: { + getId(): string; + }; + vars?: Record; +} + +// @public @deprecated (undocumented) +export interface LegacyRequest extends Request { +} + +// Warning: (ae-forgotten-export) The symbol "LegacyPlugins" needs to be exported by the entry point index.d.ts +// +// @internal @deprecated (undocumented) +export interface LegacyServiceDiscoverPlugins extends LegacyPlugins { + // (undocumented) + pluginExtendedConfig: LegacyConfig; + // (undocumented) + settings: LegacyVars; +} + +// @public @deprecated (undocumented) +export interface LegacyServiceSetupDeps { + // Warning: (ae-forgotten-export) The symbol "LegacyCoreSetup" needs to be exported by the entry point index.d.ts + // + // (undocumented) + core: LegacyCoreSetup; + // (undocumented) + plugins: Record; +} + +// @public @deprecated (undocumented) +export interface LegacyServiceStartDeps { + // Warning: (ae-forgotten-export) The symbol "LegacyCoreStart" needs to be exported by the entry point index.d.ts + // + // (undocumented) + core: LegacyCoreStart; + // (undocumented) + plugins: Record; +} + +// Warning: (ae-forgotten-export) The symbol "SavedObjectsLegacyUiExports" needs to be exported by the entry point index.d.ts +// +// @internal @deprecated (undocumented) +export type LegacyUiExports = SavedObjectsLegacyUiExports & { + defaultInjectedVarProviders?: VarsProvider[]; + injectedVarsReplacers?: VarsReplacer[]; + navLinkSpecs?: LegacyNavLinkSpec[] | null; + uiAppSpecs?: Array; + unknown?: [{ + pluginSpec: LegacyPluginSpec; + type: unknown; + }]; +}; + +// 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; + get(...childContextPaths: string[]): Logger; + info(message: string, meta?: LogMeta): void; + // @internal (undocumented) + log(record: LogRecord): void; + trace(message: string, meta?: LogMeta): void; + warn(errorOrMessage: string | Error, meta?: LogMeta): void; +} + +// @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 +export interface OnPreResponseExtensions { + headers?: ResponseHeaders; +} + +// Warning: (ae-forgotten-export) The symbol "OnPreResponseResult" needs to be exported by the entry point index.d.ts +// +// @public +export type OnPreResponseHandler = (request: KibanaRequest, preResponse: OnPreResponseInfo, toolkit: OnPreResponseToolkit) => OnPreResponseResult | Promise; + +// @public +export interface OnPreResponseInfo { + // (undocumented) + statusCode: number; +} + +// @public +export interface OnPreResponseToolkit { + next: (responseExtensions?: OnPreResponseExtensions) => OnPreResponseResult; +} + +// @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 { + deprecations?: ConfigDeprecationProvider; + 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: { + legacy: { + globalConfig$: Observable; + }; + 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; + }; +}; + +// @internal (undocumented) +export interface RenderingServiceSetup { + render(request: R, uiSettings: IUiSettingsClient, options?: R extends LegacyRequest ? LegacyRenderOptions : IRenderOptions): Promise; +} + +// @public +export type RequestHandler

= (context: RequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory) => IKibanaResponse | Promise>; + +// @public +export interface RequestHandlerContext { + // (undocumented) + core: { + rendering: IScopedRenderingClient; + 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 { + options?: RouteConfigOptions; + path: string; + validate: RouteValidatorFullConfig | 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 = (route: RouteConfig, handler: RequestHandler) => void; + +// @public +export class RouteValidationError extends SchemaTypeError { + constructor(error: Error | string, path?: string[]); +} + +// @public +export type RouteValidationFunction = (data: any, validationResult: RouteValidationResultFactory) => { + value: T; + error?: never; +} | { + value?: never; + error: RouteValidationError; +}; + +// @public +export interface RouteValidationResultFactory { + // (undocumented) + badRequest: (error: Error | string, path?: string[]) => { + error: RouteValidationError; + }; + // (undocumented) + ok: (value: T) => { + value: T; + }; +} + +// @public +export type RouteValidationSpec = ObjectType | Type | RouteValidationFunction; + +// @public +export interface RouteValidatorConfig { + body?: RouteValidationSpec; + params?: RouteValidationSpec

; + query?: RouteValidationSpec; +} + +// @public +export type RouteValidatorFullConfig = RouteValidatorConfig & RouteValidatorOptions; + +// @public +export interface RouteValidatorOptions { + unsafe?: { + params?: boolean; + query?: boolean; + body?: boolean; + }; +} + +// @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) + static errors: typeof SavedObjectsErrorHelpers; + // (undocumented) + 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 { + 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>; + // Warning: (ae-forgotten-export) The symbol "KibanaMigrator" needs to be exported by the entry point index.d.ts + // + // @internal + static createRepository(migrator: KibanaMigrator, schema: SavedObjectsSchema, config: LegacyConfig, indexName: string, callCluster: APICaller, extraTypes?: string[], injectedConstructor?: any): any; + 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: LegacyConfig, 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 (undocumented) +export type SharedGlobalConfig = RecursiveReadonly_2<{ + kibana: Pick; + elasticsearch: Pick; + path: Pick; +}>; + +// @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 (undocumented) +export interface UiSettingsServiceStart { + asScopedToClient(savedObjectsClient: SavedObjectsClientContract): IUiSettingsClient; +} + +// @public +export type UiSettingsType = 'json' | 'markdown' | 'number' | 'select' | 'boolean' | 'string'; + +// @public +export interface UserProvidedValues { + // (undocumented) + isOverridden?: boolean; + // (undocumented) + userValue?: T; +} + +// @public +export interface UuidServiceSetup { + getInstanceUuid(): string; +} + +// @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/legacy/types.ts:158:3 - (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:159:3 - (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:160:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:161:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts +// src/core/server/legacy/types.ts:162:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/plugins_service.ts:43:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:221:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:221:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:222:3 - (ae-forgotten-export) The symbol "ElasticsearchConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:223:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts + +``` diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 6ae9fd07f3d5f..4239e7e6c4254 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -257,6 +257,10 @@ export class Server { ]; this.configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider); + this.configService.addDeprecationProvider( + uiSettingsConfig.path, + uiSettingsConfig.deprecations! + ); for (const [path, schema] of schemas) { await this.configService.setSchema(path, schema); diff --git a/src/core/server/ui_settings/ui_settings_config.ts b/src/core/server/ui_settings/ui_settings_config.ts index 702286f953ef1..a54d482a0296a 100644 --- a/src/core/server/ui_settings/ui_settings_config.ts +++ b/src/core/server/ui_settings/ui_settings_config.ts @@ -18,15 +18,35 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; +import { ConfigDeprecationProvider } from 'src/core/server'; +import { ServiceConfigDescriptor } from '../internal_types'; -export type UiSettingsConfigType = TypeOf; +const deprecations: ConfigDeprecationProvider = ({ unused, renameFromRoot }) => [ + unused('enabled'), + renameFromRoot('server.defaultRoute', 'uiSettings.overrides.defaultRoute'), +]; -export const config = { +const configSchema = schema.object({ + overrides: schema.object( + { + defaultRoute: schema.maybe( + schema.string({ + validate(value) { + if (!value.startsWith('/')) { + return 'must start with a slash'; + } + }, + }) + ), + }, + { allowUnknowns: true } + ), +}); + +export type UiSettingsConfigType = TypeOf; + +export const config: ServiceConfigDescriptor = { path: 'uiSettings', - schema: schema.object({ - overrides: schema.object({}, { allowUnknowns: true }), - // Deprecation is implemented in LP. - // We define schema here not to fail on the validation step. - enabled: schema.maybe(schema.boolean()), - }), + schema: configSchema, + deprecations, }; diff --git a/src/core/server/ui_settings/ui_settings_service.test.ts b/src/core/server/ui_settings/ui_settings_service.test.ts index d908a91a39c70..9b6a5ba0a4884 100644 --- a/src/core/server/ui_settings/ui_settings_service.test.ts +++ b/src/core/server/ui_settings/ui_settings_service.test.ts @@ -22,7 +22,6 @@ import { MockUiSettingsClientConstructor } from './ui_settings_service.test.mock import { UiSettingsService } from './ui_settings_service'; import { httpServiceMock } from '../http/http_service.mock'; -import { loggingServiceMock } from '../logging/logging_service.mock'; import { savedObjectsClientMock } from '../mocks'; import { mockCoreContext } from '../core_context.mock'; @@ -69,27 +68,6 @@ describe('uiSettings', () => { expect(MockUiSettingsClientConstructor.mock.calls[0][0].overrides).toEqual(overrides); }); - it('passes overrides with deprecated "server.defaultRoute"', async () => { - const service = new UiSettingsService(coreContext); - const httpSetupWithDefaultRoute = httpServiceMock.createSetupContract(); - httpSetupWithDefaultRoute.config.defaultRoute = '/defaultRoute'; - const setup = await service.setup({ http: httpSetupWithDefaultRoute }); - setup.asScopedToClient(savedObjectsClient); - - expect(MockUiSettingsClientConstructor.mock.calls[0][0].overrides).toEqual({ - ...overrides, - defaultRoute: '/defaultRoute', - }); - - expect(loggingServiceMock.collect(coreContext.logger).warn).toMatchInlineSnapshot(` - Array [ - Array [ - "Config key \\"server.defaultRoute\\" is deprecated. It has been replaced with \\"uiSettings.overrides.defaultRoute\\"", - ], - ] - `); - }); - it('passes a copy of set defaults to UiSettingsClient', async () => { const service = new UiSettingsService(coreContext); const setup = await service.setup(setupDeps); diff --git a/src/core/server/ui_settings/ui_settings_service.ts b/src/core/server/ui_settings/ui_settings_service.ts index db08c3cad85a2..942c2625ac8e7 100644 --- a/src/core/server/ui_settings/ui_settings_service.ts +++ b/src/core/server/ui_settings/ui_settings_service.ts @@ -56,7 +56,9 @@ export class UiSettingsService public async setup(deps: SetupDeps): Promise { registerRoutes(deps.http.createRouter('')); this.log.debug('Setting up ui settings service'); - this.overrides = await this.getOverrides(deps); + const config = await this.config$.pipe(first()).toPromise(); + this.overrides = config.overrides; + return { register: this.register.bind(this), asScopedToClient: this.getScopedClientFactory(), @@ -95,18 +97,4 @@ export class UiSettingsService this.uiSettingsDefaults.set(key, value); }); } - - private async getOverrides(deps: SetupDeps) { - const config = await this.config$.pipe(first()).toPromise(); - const overrides: Record = config.overrides; - // manually implemented deprecation until New platform Config service - // supports them /~https://github.com/elastic/kibana/issues/40255 - if (typeof deps.http.config.defaultRoute !== 'undefined') { - overrides.defaultRoute = deps.http.config.defaultRoute; - this.log.warn( - 'Config key "server.defaultRoute" is deprecated. It has been replaced with "uiSettings.overrides.defaultRoute"' - ); - } - return overrides; - } } diff --git a/src/dev/run_check_core_api_changes.ts b/src/dev/run_check_core_api_changes.ts index ccf92cc432d2b..56664477df491 100644 --- a/src/dev/run_check_core_api_changes.ts +++ b/src/dev/run_check_core_api_changes.ts @@ -35,6 +35,7 @@ import getopts from 'getopts'; const apiExtractorConfig = (folder: string): ExtractorConfig => { const config: IConfigFile = { + newlineKind: 'lf', compiler: { tsconfigFilePath: '/tsconfig.json', }, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/__snapshots__/dashboard_empty_screen.test.tsx.snap index d48e34b2e4837..f611ec978b6b3 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/__snapshots__/dashboard_empty_screen.test.tsx.snap +++ b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/__snapshots__/dashboard_empty_screen.test.tsx.snap @@ -2,6 +2,31 @@ exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = ` - -

- -
+ - - -
+ - - - - - - -
- - -
-

- This dashboard is empty. Let’s fill it up! -

-
-
- -
- - -
-

- Click the - - - - button in the menu bar above to add a visualization to the dashboard. -

-
-
- -
- -

- - - -

-
- - -
-
-
- + Add an existing + + + +   + + or new object to this dashboard +

+ + + + + +
+ +

+ + + +

+
@@ -373,6 +361,31 @@ exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = ` exports[`DashboardEmptyScreen renders correctly without visualize paragraph 1`] = `
@@ -581,59 +630,48 @@ exports[`DashboardEmptyScreen renders correctly without visualize paragraph 1`] className="euiPageBody" >
- - - - - - -
- + +
-

This dashboard is empty. Let’s fill it up! -

+

- -
-

- Click the - + - - - button in the menu bar above to start working on your new dashboard. -

-
-
+

+ Click + +   + + + + + +   + + in the menu bar above to start adding panels. +

+
+ +
+ +
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/dashboard_empty_screen.test.tsx b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/dashboard_empty_screen.test.tsx index 1c450879ee553..381ced2efd8e3 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/dashboard_empty_screen.test.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/dashboard_empty_screen.test.tsx @@ -24,11 +24,16 @@ import { } from '../np_ready/dashboard_empty_screen'; // @ts-ignore import { findTestSubject } from '@elastic/eui/lib/test'; +import { coreMock } from '../../../../../../core/public/mocks'; describe('DashboardEmptyScreen', () => { + const setupMock = coreMock.createSetup(); + const defaultProps = { showLinkToVisualize: true, onLinkClick: jest.fn(), + uiSettings: setupMock.uiSettings, + http: setupMock.http, }; function mountComponent(props?: DashboardEmptyScreenProps) { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/_dashboard_app.scss b/src/legacy/core_plugins/kibana/public/dashboard/_dashboard_app.scss index d9eadf6c0e37d..03a8a07d6b17d 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/_dashboard_app.scss +++ b/src/legacy/core_plugins/kibana/public/dashboard/_dashboard_app.scss @@ -6,5 +6,31 @@ .dshStartScreen { text-align: center; - padding: $euiSizeS; +} + +.dshStartScreen__pageContent { + padding: $euiSizeXXL; +} + +.dshStartScreen__panelDesc { + max-width: 260px; + margin: 0 auto; +} + +.dshEmptyWidget { + border: $euiBorderThin; + border-style: dashed; + border-radius: $euiBorderRadius; + padding: $euiSizeXXL * 2; + max-width: 400px; + margin-left: $euiSizeS; + text-align: center; +} + +.dshEmptyWidget { + border: 2px dashed $euiColorLightShade; + padding: 4 * $euiSize; + max-width: 20em; + margin-left: 10px; + text-align: center; } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx index 08637174c8cec..8fcc7e4c26321 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_app_controller.tsx @@ -123,7 +123,7 @@ export class DashboardAppController { timefilter: { timefilter }, }, }, - core: { notifications, overlays, chrome, injectedMetadata, uiSettings, savedObjects }, + core: { notifications, overlays, chrome, injectedMetadata, uiSettings, savedObjects, http }, }: DashboardAppControllerDependencies) { new FilterStateManager(globalState, getAppState, filterManager); const queryFilter = filterManager; @@ -197,6 +197,8 @@ export class DashboardAppController { const emptyScreenProps: DashboardEmptyScreenProps = { onLinkClick: shouldShowEditHelp ? $scope.showAddPanel : $scope.enterEditMode, showLinkToVisualize: shouldShowEditHelp, + uiSettings, + http, }; if (shouldShowEditHelp) { emptyScreenProps.onVisualizeClick = addVisualization; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_empty_screen.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_empty_screen.tsx index 2fc78d64d0a0c..ae5319c560ab9 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_empty_screen.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_empty_screen.tsx @@ -19,94 +19,110 @@ import React from 'react'; import { I18nProvider } from '@kbn/i18n/react'; import { - EuiIcon, EuiLink, EuiSpacer, EuiPageContent, EuiPageBody, EuiPage, + EuiImage, EuiText, EuiButton, } from '@elastic/eui'; +import { IUiSettingsClient, HttpStart } from 'kibana/public'; import * as constants from './dashboard_empty_screen_constants'; export interface DashboardEmptyScreenProps { showLinkToVisualize: boolean; onLinkClick: () => void; onVisualizeClick?: () => void; + uiSettings: IUiSettingsClient; + http: HttpStart; } export function DashboardEmptyScreen({ showLinkToVisualize, onLinkClick, onVisualizeClick, + uiSettings, + http, }: DashboardEmptyScreenProps) { + const IS_DARK_THEME = uiSettings.get('theme:darkMode'); + const emptyStateGraphicURL = IS_DARK_THEME + ? '/plugins/kibana/home/assets/welcome_graphic_dark_2x.png' + : '/plugins/kibana/home/assets/welcome_graphic_light_2x.png'; const linkToVisualizeParagraph = (

{constants.createNewVisualizationButton}

); const paragraph = ( - description1: string, + description1: string | null, description2: string, linkText: string, ariaLabel: string, dataTestSubj?: string ) => { return ( - +

{description1} + {description1 &&  } {linkText} +   {description2}

); }; - const addVisualizationParagraph = ( - - {paragraph( - constants.addVisualizationDescription1, - constants.addVisualizationDescription2, - constants.addVisualizationLinkText, - constants.addVisualizationLinkAriaLabel, - 'emptyDashboardAddPanelButton' - )} - - {linkToVisualizeParagraph} - - ); const enterEditModeParagraph = paragraph( constants.howToStartWorkingOnNewDashboardDescription1, constants.howToStartWorkingOnNewDashboardDescription2, constants.howToStartWorkingOnNewDashboardEditLinkText, constants.howToStartWorkingOnNewDashboardEditLinkAriaLabel ); - return ( - - - - - - - -

{constants.fillDashboardTitle}

-
- - {showLinkToVisualize ? addVisualizationParagraph : enterEditModeParagraph} -
-
-
-
+ const enterViewModeParagraph = paragraph( + null, + constants.addNewVisualizationDescription, + constants.addExistingVisualizationLinkText, + constants.addExistingVisualizationLinkAriaLabel + ); + const viewMode = ( + + + + + +

{constants.fillDashboardTitle}

+
+ +
{enterEditModeParagraph}
+
+
+
+ ); + const editMode = ( +
+ {enterViewModeParagraph} + + {linkToVisualizeParagraph} +
); + return {showLinkToVisualize ? editMode : viewMode}; } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_empty_screen_constants.tsx b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_empty_screen_constants.tsx index 03004f6270fef..513e6cb685a7a 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_empty_screen_constants.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_empty_screen_constants.tsx @@ -19,40 +19,20 @@ import { i18n } from '@kbn/i18n'; -export const addVisualizationDescription1: string = i18n.translate( - 'kbn.dashboard.addVisualizationDescription1', - { - defaultMessage: 'Click the ', - } -); -export const addVisualizationDescription2: string = i18n.translate( - 'kbn.dashboard.addVisualizationDescription2', - { - defaultMessage: ' button in the menu bar above to add a visualization to the dashboard.', - } -); -export const addVisualizationLinkText: string = i18n.translate( - 'kbn.dashboard.addVisualizationLinkText', - { - defaultMessage: 'Add', - } -); -export const addVisualizationLinkAriaLabel: string = i18n.translate( - 'kbn.dashboard.addVisualizationLinkAriaLabel', - { - defaultMessage: 'Add visualization', - } -); +/** VIEW MODE CONSTANTS **/ +export const fillDashboardTitle: string = i18n.translate('kbn.dashboard.fillDashboardTitle', { + defaultMessage: 'This dashboard is empty. Let\u2019s fill it up!', +}); export const howToStartWorkingOnNewDashboardDescription1: string = i18n.translate( 'kbn.dashboard.howToStartWorkingOnNewDashboardDescription1', { - defaultMessage: 'Click the ', + defaultMessage: 'Click', } ); export const howToStartWorkingOnNewDashboardDescription2: string = i18n.translate( 'kbn.dashboard.howToStartWorkingOnNewDashboardDescription2', { - defaultMessage: ' button in the menu bar above to start working on your new dashboard.', + defaultMessage: 'in the menu bar above to start adding panels.', } ); export const howToStartWorkingOnNewDashboardEditLinkText: string = i18n.translate( @@ -67,13 +47,23 @@ export const howToStartWorkingOnNewDashboardEditLinkAriaLabel: string = i18n.tra defaultMessage: 'Edit dashboard', } ); -export const fillDashboardTitle: string = i18n.translate('kbn.dashboard.fillDashboardTitle', { - defaultMessage: 'This dashboard is empty. Let\u2019s fill it up!', -}); -export const visualizeAppLinkTest: string = i18n.translate( - 'kbn.dashboard.visitVisualizeAppLinkText', +/** EDIT MODE CONSTANTS **/ +export const addExistingVisualizationLinkText: string = i18n.translate( + 'kbn.dashboard.addExistingVisualizationLinkText', + { + defaultMessage: 'Add an existing', + } +); +export const addExistingVisualizationLinkAriaLabel: string = i18n.translate( + 'kbn.dashboard.addVisualizationLinkAriaLabel', + { + defaultMessage: 'Add an existing visualization', + } +); +export const addNewVisualizationDescription: string = i18n.translate( + 'kbn.dashboard.addNewVisualizationText', { - defaultMessage: 'visit the Visualize app', + defaultMessage: 'or new object to this dashboard', } ); export const createNewVisualizationButton: string = i18n.translate( @@ -82,3 +72,9 @@ export const createNewVisualizationButton: string = i18n.translate( defaultMessage: 'Create new', } ); +export const createNewVisualizationButtonAriaLabel: string = i18n.translate( + 'kbn.dashboard.createNewVisualizationButtonAriaLabel', + { + defaultMessage: 'Create new visualization button', + } +); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/histogram.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/histogram.tsx index 28ce64c0a5f9c..c2f716ff6c45a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/histogram.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/histogram.tsx @@ -30,9 +30,6 @@ import { Chart, HistogramBarSeries, GeometryValue, - getAnnotationId, - getAxisId, - getSpecId, LineAnnotation, Position, ScaleType, @@ -237,20 +234,20 @@ export class DiscoverHistogram extends Component `id:${x}`; -export const getGroupId = x => `groupId:${x}`; - export const BarSeries = () => null; export const AreaSeries = () => null; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap index 822de4cef0813..56504ca11ca39 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/__snapshots__/area_decorator.test.js.snap @@ -24,12 +24,9 @@ exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/ } curve={9} customSeriesColors={ - Map { - Object { - "colorValues": Array [], - "specId": "id:61ca57f1-469d-11e7-af02-69e470af7417:Rome", - } => "rgb(0, 156, 224)", - } + Array [ + "rgb(0, 156, 224)", + ] } data={ Array [ @@ -44,10 +41,10 @@ exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/ ] } enableHistogramMode={true} - groupId="groupId:yaxis_main_group" + groupId="yaxis_main_group" hideInLegend={false} histogramModeAlignment="center" - id="id:61ca57f1-469d-11e7-af02-69e470af7417:Rome" + id="61ca57f1-469d-11e7-af02-69e470af7417:Rome" name="Rome" stackAsPercentage={false} timeZone="local" diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap index 78133f2dda7cc..6317973cef536 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/__snapshots__/bar_decorator.test.js.snap @@ -16,12 +16,9 @@ exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/ } } customSeriesColors={ - Map { - Object { - "colorValues": Array [], - "specId": "id:61ca57f1-469d-11e7-af02-69e470af7417:Rome", - } => "rgb(0, 156, 224)", - } + Array [ + "rgb(0, 156, 224)", + ] } data={ Array [ @@ -36,10 +33,10 @@ exports[`src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/ ] } enableHistogramMode={true} - groupId="groupId:yaxis_main_group" + groupId="yaxis_main_group" hideInLegend={false} histogramModeAlignment="center" - id="id:61ca57f1-469d-11e7-af02-69e470af7417:Rome" + id="61ca57f1-469d-11e7-af02-69e470af7417:Rome" name="Rome" stackAsPercentage={false} timeZone="local" diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/area_decorator.js b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/area_decorator.js index 536064139e6ea..411c0813cad7c 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/area_decorator.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/area_decorator.js @@ -18,8 +18,8 @@ */ import React from 'react'; -import { getSpecId, getGroupId, ScaleType, AreaSeries } from '@elastic/charts'; -import { getSeriesColors, getAreaStyles } from '../utils/series_styles'; +import { ScaleType, AreaSeries } from '@elastic/charts'; +import { getAreaStyles } from '../utils/series_styles'; import { ChartsEntities } from '../model/charts'; import { X_ACCESSOR_INDEX, Y_ACCESSOR_INDEXES } from '../../../constants'; @@ -41,9 +41,9 @@ export function AreaSeriesDecorator({ useDefaultGroupDomain, sortIndex, }) { - const id = getSpecId(seriesId); - const groupId = getGroupId(seriesGroupId); - const customSeriesColors = getSeriesColors(color, id); + const id = seriesId; + const groupId = seriesGroupId; + const customSeriesColors = [color]; const areaSeriesStyle = getAreaStyles({ points, lines, color }); const seriesSettings = { diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/bar_decorator.js b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/bar_decorator.js index 3dbe04dca06b8..9cc8931b48d9f 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/bar_decorator.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/decorators/bar_decorator.js @@ -18,8 +18,8 @@ */ import React from 'react'; -import { getSpecId, getGroupId, ScaleType, BarSeries } from '@elastic/charts'; -import { getSeriesColors, getBarStyles } from '../utils/series_styles'; +import { ScaleType, BarSeries } from '@elastic/charts'; +import { getBarStyles } from '../utils/series_styles'; import { ChartsEntities } from '../model/charts'; import { X_ACCESSOR_INDEX, Y_ACCESSOR_INDEXES } from '../../../constants'; @@ -40,9 +40,9 @@ export function BarSeriesDecorator({ useDefaultGroupDomain, sortIndex, }) { - const id = getSpecId(seriesId); - const groupId = getGroupId(seriesGroupId); - const customSeriesColors = getSeriesColors(color, id); + const id = seriesId; + const groupId = seriesGroupId; + const customSeriesColors = [color]; const barSeriesStyle = getBarStyles(bars, color); const seriesSettings = { diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js index a02ea83e5104b..bcd0b6314cef1 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/index.js @@ -25,11 +25,8 @@ import { Chart, Position, Settings, - getAxisId, - getGroupId, DARK_THEME, LIGHT_THEME, - getAnnotationId, AnnotationDomainTypes, LineAnnotation, TooltipType, @@ -75,7 +72,7 @@ export const TimeSeries = ({ const chartRef = useRef(); const updateCursor = (_, cursor) => { if (chartRef.current) { - chartRef.current.dispatchExternalCursorEvent(cursor); + chartRef.current.dispatchExternalPointerEvent(cursor); } }; @@ -99,7 +96,7 @@ export const TimeSeries = ({ legendPosition={legendPosition} onBrushEnd={onBrush} animateData={false} - onCursorUpdate={handleCursorUpdate} + onPointerUpdate={handleCursorUpdate} theme={ hasBarChart ? {} @@ -126,7 +123,7 @@ export const TimeSeries = ({ return ( } @@ -213,8 +210,8 @@ export const TimeSeries = ({ {yAxis.map(({ id, groupId, position, tickFormatter, domain, hide }) => ( "rgb(224, 0, 221)", -} -`; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/series_styles.js b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/series_styles.js index 63be14790c6c5..2891751f121ca 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/series_styles.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/series_styles.js @@ -56,12 +56,3 @@ export const getBarStyles = ({ show = true, lineWidth = 0, fill = 1 }, color) => }, }, }); - -export const getSeriesColors = (color, specId) => { - const map = new Map(); - const seriesColorsValues = { specId, colorValues: [] }; - - map.set(seriesColorsValues, color); - - return map; -}; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/series_styles.test.js b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/series_styles.test.js index ac0a7610f2660..c04ec457d1523 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/series_styles.test.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/visualizations/views/timeseries/utils/series_styles.test.js @@ -17,12 +17,11 @@ * under the License. */ -import { getBarStyles, getSeriesColors, getAreaStyles } from './series_styles'; +import { getBarStyles, getAreaStyles } from './series_styles'; describe('src/legacy/core_plugins/metrics/public/visualizations/views/timeseries/utils/series_styles.js', () => { let bars; let color; - let specId; let points; let lines; @@ -33,7 +32,6 @@ describe('src/legacy/core_plugins/metrics/public/visualizations/views/timeseries show: true, }; color = 'rgb(224, 0, 221)'; - specId = 'IT'; points = { lineWidth: 1, show: true, @@ -60,12 +58,6 @@ describe('src/legacy/core_plugins/metrics/public/visualizations/views/timeseries }); }); - describe('getSeriesColors()', () => { - test('should match a snapshot', () => { - expect(getSeriesColors(color, specId)).toMatchSnapshot(); - }); - }); - describe('getAreaStyles()', () => { test('should match a snapshot', () => { expect(getAreaStyles({ points, lines, color })).toMatchSnapshot(); diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index f886fd598f5c9..a18cb7de5a61b 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -70,20 +70,6 @@ export default () => server: Joi.object({ name: Joi.string().default(os.hostname()), - defaultRoute: Joi.string().regex(/^\//, `start with a slash`), - customResponseHeaders: Joi.object() - .unknown(true) - .default({}), - xsrf: Joi.object({ - disableProtection: Joi.boolean().default(false), - whitelist: Joi.array() - .items(Joi.string().regex(/^\//, 'start with a slash')) - .default([]), - token: Joi.string() - .optional() - .notes('Deprecated'), - }).default(), - // keep them for BWC, remove when not used in Legacy. // validation should be in sync with one in New platform. // /~https://github.com/elastic/kibana/blob/master/src/core/server/http/http_config.ts @@ -103,12 +89,14 @@ export default () => autoListen: HANDLED_IN_NEW_PLATFORM, cors: HANDLED_IN_NEW_PLATFORM, + customResponseHeaders: HANDLED_IN_NEW_PLATFORM, keepaliveTimeout: HANDLED_IN_NEW_PLATFORM, maxPayloadBytes: HANDLED_IN_NEW_PLATFORM, socketTimeout: HANDLED_IN_NEW_PLATFORM, ssl: HANDLED_IN_NEW_PLATFORM, compression: HANDLED_IN_NEW_PLATFORM, uuid: HANDLED_IN_NEW_PLATFORM, + xsrf: HANDLED_IN_NEW_PLATFORM, }).default(), uiSettings: HANDLED_IN_NEW_PLATFORM, diff --git a/src/legacy/server/config/schema.test.js b/src/legacy/server/config/schema.test.js index 1207a05a47497..03d2fe53c2ce7 100644 --- a/src/legacy/server/config/schema.test.js +++ b/src/legacy/server/config/schema.test.js @@ -19,7 +19,6 @@ import schemaProvider from './schema'; import Joi from 'joi'; -import { set } from 'lodash'; describe('Config schema', function() { let schema; @@ -100,60 +99,5 @@ describe('Config schema', function() { expect(error.details[0]).toHaveProperty('path', ['server', 'rewriteBasePath']); }); }); - - describe('xsrf', () => { - it('disableProtection is `false` by default.', () => { - const { - error, - value: { - server: { - xsrf: { disableProtection }, - }, - }, - } = validate({}); - expect(error).toBe(null); - expect(disableProtection).toBe(false); - }); - - it('whitelist is empty by default.', () => { - const { - value: { - server: { - xsrf: { whitelist }, - }, - }, - } = validate({}); - expect(whitelist).toBeInstanceOf(Array); - expect(whitelist).toHaveLength(0); - }); - - it('whitelist rejects paths that do not start with a slash.', () => { - const config = {}; - set(config, 'server.xsrf.whitelist', ['path/to']); - - const { error } = validate(config); - expect(error).toBeInstanceOf(Object); - expect(error).toHaveProperty('details'); - expect(error.details[0]).toHaveProperty('path', ['server', 'xsrf', 'whitelist', 0]); - }); - - it('whitelist accepts paths that start with a slash.', () => { - const config = {}; - set(config, 'server.xsrf.whitelist', ['/path/to']); - - const { - error, - value: { - server: { - xsrf: { whitelist }, - }, - }, - } = validate(config); - expect(error).toBe(null); - expect(whitelist).toBeInstanceOf(Array); - expect(whitelist).toHaveLength(1); - expect(whitelist).toContain('/path/to'); - }); - }); }); }); diff --git a/src/legacy/server/http/index.js b/src/legacy/server/http/index.js index 9b5ce2046c5d3..265d71e95b301 100644 --- a/src/legacy/server/http/index.js +++ b/src/legacy/server/http/index.js @@ -22,11 +22,9 @@ import { resolve } from 'path'; import _ from 'lodash'; import Boom from 'boom'; -import { setupVersionCheck } from './version_check'; import { registerHapiPlugins } from './register_hapi_plugins'; import { setupBasePathProvider } from './setup_base_path_provider'; import { setupDefaultRouteProvider } from './setup_default_route_provider'; -import { setupXsrf } from './xsrf'; export default async function(kbnServer, server, config) { server = kbnServer.server; @@ -62,29 +60,6 @@ export default async function(kbnServer, server, config) { }); }); - // attach the app name to the server, so we can be sure we are actually talking to kibana - server.ext('onPreResponse', function onPreResponse(req, h) { - const response = req.response; - - const customHeaders = { - ...config.get('server.customResponseHeaders'), - 'kbn-name': kbnServer.name, - }; - - if (response.isBoom) { - response.output.headers = { - ...response.output.headers, - ...customHeaders, - }; - } else { - Object.keys(customHeaders).forEach(name => { - response.header(name, customHeaders[name]); - }); - } - - return h.continue; - }); - server.route({ path: '/', method: 'GET', @@ -116,7 +91,4 @@ export default async function(kbnServer, server, config) { // Expose static assets server.exposeStaticDir('/ui/{path*}', resolve(__dirname, '../../ui/public/assets')); - - setupVersionCheck(server, config); - setupXsrf(server, config); } diff --git a/src/legacy/server/http/integration_tests/version_check.test.js b/src/legacy/server/http/integration_tests/version_check.test.js deleted file mode 100644 index 8d71c98d64969..0000000000000 --- a/src/legacy/server/http/integration_tests/version_check.test.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { resolve } from 'path'; -import * as kbnTestServer from '../../../../test_utils/kbn_server'; - -const src = resolve.bind(null, __dirname, '../../../../../src'); - -const versionHeader = 'kbn-version'; -const version = require(src('../package.json')).version; // eslint-disable-line import/no-dynamic-require - -describe('version_check request filter', function() { - let root; - beforeAll(async () => { - root = kbnTestServer.createRoot(); - - await root.setup(); - await root.start(); - - kbnTestServer.getKbnServer(root).server.route({ - path: '/version_check/test/route', - method: 'GET', - handler: function() { - return 'ok'; - }, - }); - }, 30000); - - afterAll(async () => await root.shutdown()); - - it('accepts requests with the correct version passed in the version header', async function() { - await kbnTestServer.request - .get(root, '/version_check/test/route') - .set(versionHeader, version) - .expect(200, 'ok'); - }); - - it('rejects requests with an incorrect version passed in the version header', async function() { - await kbnTestServer.request - .get(root, '/version_check/test/route') - .set(versionHeader, `invalid:${version}`) - .expect(400, /"Browser client is out of date/); - }); - - it('accepts requests that do not include a version header', async function() { - await kbnTestServer.request.get(root, '/version_check/test/route').expect(200, 'ok'); - }); -}); diff --git a/src/legacy/server/http/integration_tests/xsrf.test.js b/src/legacy/server/http/integration_tests/xsrf.test.js deleted file mode 100644 index a06f4eec4fd5c..0000000000000 --- a/src/legacy/server/http/integration_tests/xsrf.test.js +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { resolve } from 'path'; -import * as kbnTestServer from '../../../../test_utils/kbn_server'; - -const destructiveMethods = ['POST', 'PUT', 'DELETE']; -const src = resolve.bind(null, __dirname, '../../../../../src'); - -const xsrfHeader = 'kbn-xsrf'; -const versionHeader = 'kbn-version'; -const testPath = '/xsrf/test/route'; -const whitelistedTestPath = '/xsrf/test/route/whitelisted'; -const actualVersion = require(src('../package.json')).version; // eslint-disable-line import/no-dynamic-require - -describe('xsrf request filter', () => { - let root; - beforeAll(async () => { - root = kbnTestServer.createRoot({ - server: { - xsrf: { disableProtection: false, whitelist: [whitelistedTestPath] }, - }, - }); - - await root.setup(); - await root.start(); - - const kbnServer = kbnTestServer.getKbnServer(root); - kbnServer.server.route({ - path: testPath, - method: 'GET', - handler: async function() { - return 'ok'; - }, - }); - - kbnServer.server.route({ - path: testPath, - method: destructiveMethods, - config: { - // Disable payload parsing to make HapiJS server accept any content-type header. - payload: { - parse: false, - }, - validate: { payload: null }, - }, - handler: async function() { - return 'ok'; - }, - }); - - kbnServer.server.route({ - path: whitelistedTestPath, - method: destructiveMethods, - config: { - // Disable payload parsing to make HapiJS server accept any content-type header. - payload: { - parse: false, - }, - validate: { payload: null }, - }, - handler: async function() { - return 'ok'; - }, - }); - }, 30000); - - afterAll(async () => await root.shutdown()); - - describe(`nonDestructiveMethod: GET`, function() { - it('accepts requests without a token', async function() { - await kbnTestServer.request.get(root, testPath).expect(200, 'ok'); - }); - - it('accepts requests with the xsrf header', async function() { - await kbnTestServer.request - .get(root, testPath) - .set(xsrfHeader, 'anything') - .expect(200, 'ok'); - }); - }); - - describe(`nonDestructiveMethod: HEAD`, function() { - it('accepts requests without a token', async function() { - await kbnTestServer.request.head(root, testPath).expect(200, undefined); - }); - - it('accepts requests with the xsrf header', async function() { - await kbnTestServer.request - .head(root, testPath) - .set(xsrfHeader, 'anything') - .expect(200, undefined); - }); - }); - - for (const method of destructiveMethods) { - // eslint-disable-next-line no-loop-func - describe(`destructiveMethod: ${method}`, function() { - it('accepts requests with the xsrf header', async function() { - await kbnTestServer.request[method.toLowerCase()](root, testPath) - .set(xsrfHeader, 'anything') - .expect(200, 'ok'); - }); - - // this is still valid for existing csrf protection support - // it does not actually do any validation on the version value itself - it('accepts requests with the version header', async function() { - await kbnTestServer.request[method.toLowerCase()](root, testPath) - .set(versionHeader, actualVersion) - .expect(200, 'ok'); - }); - - it('rejects requests without either an xsrf or version header', async function() { - await kbnTestServer.request[method.toLowerCase()](root, testPath).expect(400, { - statusCode: 400, - error: 'Bad Request', - message: 'Request must contain a kbn-xsrf header.', - }); - }); - - it('accepts whitelisted requests without either an xsrf or version header', async function() { - await kbnTestServer.request[method.toLowerCase()](root, whitelistedTestPath).expect( - 200, - 'ok' - ); - }); - }); - } -}); diff --git a/src/legacy/server/http/setup_default_route_provider.ts b/src/legacy/server/http/setup_default_route_provider.ts index 0e7bcf1f56f6f..9a580dd1c59bd 100644 --- a/src/legacy/server/http/setup_default_route_provider.ts +++ b/src/legacy/server/http/setup_default_route_provider.ts @@ -29,7 +29,7 @@ export function setupDefaultRouteProvider(server: Legacy.Server) { const uiSettings = request.getUiSettingsService(); - const defaultRoute = await uiSettings.get('defaultRoute'); + const defaultRoute = await uiSettings.get('defaultRoute'); const qualifiedDefaultRoute = `${request.getBasePath()}${defaultRoute}`; if (isRelativePath(qualifiedDefaultRoute, serverBasePath)) { diff --git a/src/legacy/server/http/version_check.js b/src/legacy/server/http/version_check.js deleted file mode 100644 index 12666c9a0f3f6..0000000000000 --- a/src/legacy/server/http/version_check.js +++ /dev/null @@ -1,39 +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 { badRequest } from 'boom'; - -export function setupVersionCheck(server, config) { - const versionHeader = 'kbn-version'; - const actualVersion = config.get('pkg.version'); - - server.ext('onPostAuth', function onPostAuthVersionCheck(req, h) { - const versionRequested = req.headers[versionHeader]; - - if (versionRequested && versionRequested !== actualVersion) { - throw badRequest( - `Browser client is out of date, \ - please refresh the page ("${versionHeader}" header was "${versionRequested}" but should be "${actualVersion}")`, - { expected: actualVersion, got: versionRequested } - ); - } - - return h.continue; - }); -} diff --git a/src/legacy/server/http/xsrf.js b/src/legacy/server/http/xsrf.js deleted file mode 100644 index 79ac3af6d9f90..0000000000000 --- a/src/legacy/server/http/xsrf.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { badRequest } from 'boom'; - -export function setupXsrf(server, config) { - const disabled = config.get('server.xsrf.disableProtection'); - const whitelist = config.get('server.xsrf.whitelist'); - const versionHeader = 'kbn-version'; - const xsrfHeader = 'kbn-xsrf'; - - server.ext('onPostAuth', function onPostAuthXsrf(req, h) { - if (disabled) { - return h.continue; - } - - if (whitelist.includes(req.path)) { - return h.continue; - } - - const isSafeMethod = req.method === 'get' || req.method === 'head'; - const hasVersionHeader = versionHeader in req.headers; - const hasXsrfHeader = xsrfHeader in req.headers; - - if (!isSafeMethod && !hasVersionHeader && !hasXsrfHeader) { - throw badRequest(`Request must contain a ${xsrfHeader} header.`); - } - - return h.continue; - }); -} diff --git a/src/test_utils/kbn_server.ts b/src/test_utils/kbn_server.ts index 40700e05bcde8..e1b4a823e7e87 100644 --- a/src/test_utils/kbn_server.ts +++ b/src/test_utils/kbn_server.ts @@ -37,7 +37,7 @@ import { Root } from '../core/server/root'; import KbnServer from '../legacy/server/kbn_server'; import { CallCluster } from '../legacy/core_plugins/elasticsearch'; -type HttpMethod = 'delete' | 'get' | 'head' | 'post' | 'put'; +export type HttpMethod = 'delete' | 'get' | 'head' | 'post' | 'put'; const DEFAULTS_SETTINGS = { server: { @@ -76,6 +76,7 @@ export function createRootWithSettings( repl: false, basePath: false, optimize: false, + runExamples: false, oss: true, ...cliArgs, }, @@ -96,7 +97,7 @@ export function createRootWithSettings( * @param method * @param path */ -function getSupertest(root: Root, method: HttpMethod, path: string) { +export function getSupertest(root: Root, method: HttpMethod, path: string) { const testUserCredentials = Buffer.from(`${kibanaTestUser.username}:${kibanaTestUser.password}`); return supertest((root as any).server.http.httpServer.server.listener) [method](path) diff --git a/test/functional/apps/dashboard/empty_dashboard.js b/test/functional/apps/dashboard/empty_dashboard.js index d46daff183abf..c91b7bd1ecee0 100644 --- a/test/functional/apps/dashboard/empty_dashboard.js +++ b/test/functional/apps/dashboard/empty_dashboard.js @@ -44,13 +44,13 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.gotoDashboardLandingPage(); }); - it('should display add button', async () => { - const addButtonExists = await testSubjects.exists('emptyDashboardAddPanelButton'); - expect(addButtonExists).to.be(true); + it('should display empty widget', async () => { + const emptyWidgetExists = await testSubjects.exists('emptyDashboardWidget'); + expect(emptyWidgetExists).to.be(true); }); it.skip('should open add panel when add button is clicked', async () => { - await testSubjects.click('emptyDashboardAddPanelButton'); + await testSubjects.click('dashboardAddPanelButton'); const isAddPanelOpen = await dashboardAddPanel.isAddPanelOpen(); expect(isAddPanelOpen).to.be(true); }); diff --git a/test/functional/services/elastic_chart.ts b/test/functional/services/elastic_chart.ts index 4f4dbcba5f0b8..afae3f830b3bf 100644 --- a/test/functional/services/elastic_chart.ts +++ b/test/functional/services/elastic_chart.ts @@ -37,7 +37,7 @@ export function ElasticChartProvider({ getService }: FtrProviderContext) { public async getVisualizationRenderingCount(dataTestSubj: string) { const chart = await testSubjects.find(dataTestSubj); - const visContainer = await chart.findByCssSelector('.echChart'); + const visContainer = await chart.findByCssSelector('.echChartStatus'); const renderingCount = await visContainer.getAttribute('data-ech-render-count'); return Number(renderingCount); } diff --git a/x-pack/legacy/plugins/alerting/server/lib/alert_instance.test.ts b/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/alert_instance.test.ts rename to x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/alert_instance.ts b/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts similarity index 97% rename from x-pack/legacy/plugins/alerting/server/lib/alert_instance.ts rename to x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts index 1e2cc26f364ad..a56e2077cdfd8 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/alert_instance.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_instance/alert_instance.ts @@ -5,7 +5,7 @@ */ import { State, Context } from '../types'; -import { parseDuration } from './parse_duration'; +import { parseDuration } from '../lib'; interface Meta { lastScheduledActions?: { diff --git a/x-pack/legacy/plugins/alerting/server/lib/create_alert_instance_factory.test.ts b/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/create_alert_instance_factory.test.ts rename to x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/create_alert_instance_factory.ts b/x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/create_alert_instance_factory.ts rename to x-pack/legacy/plugins/alerting/server/alert_instance/create_alert_instance_factory.ts diff --git a/x-pack/legacy/plugins/infra/public/store/local/log_filter/index.ts b/x-pack/legacy/plugins/alerting/server/alert_instance/index.ts similarity index 58% rename from x-pack/legacy/plugins/infra/public/store/local/log_filter/index.ts rename to x-pack/legacy/plugins/alerting/server/alert_instance/index.ts index 369f5f013d669..40ee0874e805c 100644 --- a/x-pack/legacy/plugins/infra/public/store/local/log_filter/index.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_instance/index.ts @@ -4,8 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as logFilterActions from './actions'; -import * as logFilterSelectors from './selectors'; - -export { logFilterActions, logFilterSelectors }; -export * from './reducer'; +export { AlertInstance } from './alert_instance'; +export { createAlertInstanceFactory } from './create_alert_instance_factory'; diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts b/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts index 57e1b965960e8..8e96ad8dae31c 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_type_registry.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TaskRunnerFactory } from './lib'; +import { TaskRunnerFactory } from './task_runner'; import { AlertTypeRegistry } from './alert_type_registry'; import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; diff --git a/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts b/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts index b7512864c2a98..2003e810a05b5 100644 --- a/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts +++ b/x-pack/legacy/plugins/alerting/server/alert_type_registry.ts @@ -6,8 +6,8 @@ import Boom from 'boom'; import { i18n } from '@kbn/i18n'; -import { TaskRunnerFactory } from './lib'; -import { RunContext } from '../../task_manager/server'; +import { TaskRunnerFactory } from './task_runner'; +import { RunContext } from '../../task_manager'; import { TaskManagerSetupContract } from './shim'; import { AlertType } from './types'; diff --git a/x-pack/legacy/plugins/alerting/server/alerts_client.ts b/x-pack/legacy/plugins/alerting/server/alerts_client.ts index fc0252c86fe50..33a6b716e9b8a 100644 --- a/x-pack/legacy/plugins/alerting/server/alerts_client.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client.ts @@ -15,6 +15,7 @@ import { } from 'src/core/server'; import { Alert, + PartialAlert, RawAlert, AlertTypeRegistry, AlertAction, @@ -69,28 +70,26 @@ export interface FindOptions { }; } -interface FindResult { +export interface FindResult { page: number; perPage: number; total: number; - data: object[]; + data: Alert[]; } interface CreateOptions { - data: Pick< + data: Omit< Alert, - Exclude< - keyof Alert, - | 'createdBy' - | 'updatedBy' - | 'createdAt' - | 'updatedAt' - | 'apiKey' - | 'apiKeyOwner' - | 'muteAll' - | 'mutedInstanceIds' - | 'actions' - > + | 'id' + | 'createdBy' + | 'updatedBy' + | 'createdAt' + | 'updatedAt' + | 'apiKey' + | 'apiKeyOwner' + | 'muteAll' + | 'mutedInstanceIds' + | 'actions' > & { actions: NormalizedAlertAction[] }; options?: { migrationVersion?: Record; @@ -146,7 +145,7 @@ export class AlertsClient { this.encryptedSavedObjectsPlugin = encryptedSavedObjectsPlugin; } - public async create({ data, options }: CreateOptions) { + public async create({ data, options }: CreateOptions): Promise { // Throws an error if alert type isn't registered const alertType = this.alertTypeRegistry.get(data.alertTypeId); const validatedAlertTypeParams = validateAlertTypeParams(alertType, data.params); @@ -199,26 +198,29 @@ export class AlertsClient { ); } - public async get({ id }: { id: string }) { + public async get({ id }: { id: string }): Promise { const result = await this.savedObjectsClient.get('alert', id); return this.getAlertFromRaw(result.id, result.attributes, result.updated_at, result.references); } public async find({ options = {} }: FindOptions = {}): Promise { - const results = await this.savedObjectsClient.find({ + const { + page, + per_page: perPage, + total, + saved_objects: data, + } = await this.savedObjectsClient.find({ ...options, type: 'alert', }); - const data = results.saved_objects.map(result => - this.getAlertFromRaw(result.id, result.attributes, result.updated_at, result.references) - ); - return { - page: results.page, - perPage: results.per_page, - total: results.total, - data, + page, + perPage, + total, + data: data.map(({ id, attributes, updated_at, references }) => + this.getAlertFromRaw(id, attributes, updated_at, references) + ), }; } @@ -234,7 +236,7 @@ export class AlertsClient { return removeResult; } - public async update({ id, data }: UpdateOptions) { + public async update({ id, data }: UpdateOptions): Promise { const decryptedAlertSavedObject = await this.encryptedSavedObjectsPlugin.getDecryptedAsInternalUser< RawAlert >('alert', id, { namespace: this.namespace }); @@ -257,7 +259,7 @@ export class AlertsClient { private async updateAlert( { id, data }: UpdateOptions, { attributes, version }: SavedObject - ) { + ): Promise { const alertType = this.alertTypeRegistry.get(attributes.alertTypeId); // Validate @@ -287,7 +289,7 @@ export class AlertsClient { await this.invalidateApiKey({ apiKey: attributes.apiKey }); - return this.getAlertFromRaw( + return this.getPartialAlertFromRaw( id, updatedObject.attributes, updatedObject.updated_at, @@ -494,24 +496,34 @@ export class AlertsClient { } private getAlertFromRaw( + id: string, + rawAlert: RawAlert, + updatedAt: SavedObject['updated_at'], + references: SavedObjectReference[] | undefined + ): Alert { + // In order to support the partial update API of Saved Objects we have to support + // partial updates of an Alert, but when we receive an actual RawAlert, it is safe + // to cast the result to an Alert + return this.getPartialAlertFromRaw(id, rawAlert, updatedAt, references) as Alert; + } + + private getPartialAlertFromRaw( id: string, rawAlert: Partial, updatedAt: SavedObject['updated_at'], references: SavedObjectReference[] | undefined - ) { - if (!rawAlert.actions) { - return { - id, - ...rawAlert, - }; - } - const actions = this.injectReferencesIntoActions(rawAlert.actions, references || []); + ): PartialAlert { return { id, ...rawAlert, + // we currently only support the Interval Schedule type + // Once we support additional types, this type signature will likely change + schedule: rawAlert.schedule as IntervalSchedule, updatedAt: updatedAt ? new Date(updatedAt) : new Date(rawAlert.createdAt!), createdAt: new Date(rawAlert.createdAt!), - actions, + actions: rawAlert.actions + ? this.injectReferencesIntoActions(rawAlert.actions, references || []) + : [], }; } diff --git a/x-pack/legacy/plugins/alerting/server/lib/alerts_client_factory.test.ts b/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts similarity index 81% rename from x-pack/legacy/plugins/alerting/server/lib/alerts_client_factory.test.ts rename to x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts index 838c567fb2878..519001d07e089 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/alerts_client_factory.test.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client_factory.test.ts @@ -6,13 +6,13 @@ import { Request } from 'hapi'; import { AlertsClientFactory, ConstructorOpts } from './alerts_client_factory'; -import { alertTypeRegistryMock } from '../alert_type_registry.mock'; -import { taskManagerMock } from '../../../task_manager/server/task_manager.mock'; -import { KibanaRequest } from '../../../../../../src/core/server'; -import { loggingServiceMock } from '../../../../../../src/core/server/mocks'; -import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; +import { alertTypeRegistryMock } from './alert_type_registry.mock'; +import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; +import { KibanaRequest } from '../../../../../src/core/server'; +import { loggingServiceMock } from '../../../../../src/core/server/mocks'; +import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks'; -jest.mock('../alerts_client'); +jest.mock('./alerts_client'); const savedObjectsClient = jest.fn(); const securityPluginSetup = { @@ -55,7 +55,7 @@ test('creates an alerts client with proper constructor arguments', async () => { const factory = new AlertsClientFactory(alertsClientFactoryParams); factory.create(KibanaRequest.from(fakeRequest), fakeRequest); - expect(jest.requireMock('../alerts_client').AlertsClient).toHaveBeenCalledWith({ + expect(jest.requireMock('./alerts_client').AlertsClient).toHaveBeenCalledWith({ savedObjectsClient, logger: alertsClientFactoryParams.logger, taskManager: alertsClientFactoryParams.taskManager, @@ -72,7 +72,7 @@ test('creates an alerts client with proper constructor arguments', async () => { test('getUserName() returns null when security is disabled', async () => { const factory = new AlertsClientFactory(alertsClientFactoryParams); factory.create(KibanaRequest.from(fakeRequest), fakeRequest); - const constructorCall = jest.requireMock('../alerts_client').AlertsClient.mock.calls[0][0]; + const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; const userNameResult = await constructorCall.getUserName(); expect(userNameResult).toEqual(null); @@ -84,7 +84,7 @@ test('getUserName() returns a name when security is enabled', async () => { securityPluginSetup: securityPluginSetup as any, }); factory.create(KibanaRequest.from(fakeRequest), fakeRequest); - const constructorCall = jest.requireMock('../alerts_client').AlertsClient.mock.calls[0][0]; + const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; securityPluginSetup.authc.getCurrentUser.mockResolvedValueOnce({ username: 'bob' }); const userNameResult = await constructorCall.getUserName(); @@ -94,7 +94,7 @@ test('getUserName() returns a name when security is enabled', async () => { test('createAPIKey() returns { apiKeysEnabled: false } when security is disabled', async () => { const factory = new AlertsClientFactory(alertsClientFactoryParams); factory.create(KibanaRequest.from(fakeRequest), fakeRequest); - const constructorCall = jest.requireMock('../alerts_client').AlertsClient.mock.calls[0][0]; + const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; const createAPIKeyResult = await constructorCall.createAPIKey(); expect(createAPIKeyResult).toEqual({ apiKeysEnabled: false }); @@ -103,7 +103,7 @@ test('createAPIKey() returns { apiKeysEnabled: false } when security is disabled test('createAPIKey() returns { apiKeysEnabled: false } when security is enabled but ES security is disabled', async () => { const factory = new AlertsClientFactory(alertsClientFactoryParams); factory.create(KibanaRequest.from(fakeRequest), fakeRequest); - const constructorCall = jest.requireMock('../alerts_client').AlertsClient.mock.calls[0][0]; + const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; securityPluginSetup.authc.createAPIKey.mockResolvedValueOnce(null); const createAPIKeyResult = await constructorCall.createAPIKey(); @@ -116,7 +116,7 @@ test('createAPIKey() returns an API key when security is enabled', async () => { securityPluginSetup: securityPluginSetup as any, }); factory.create(KibanaRequest.from(fakeRequest), fakeRequest); - const constructorCall = jest.requireMock('../alerts_client').AlertsClient.mock.calls[0][0]; + const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; securityPluginSetup.authc.createAPIKey.mockResolvedValueOnce({ api_key: '123', id: 'abc' }); const createAPIKeyResult = await constructorCall.createAPIKey(); @@ -132,7 +132,7 @@ test('createAPIKey() throws when security plugin createAPIKey throws an error', securityPluginSetup: securityPluginSetup as any, }); factory.create(KibanaRequest.from(fakeRequest), fakeRequest); - const constructorCall = jest.requireMock('../alerts_client').AlertsClient.mock.calls[0][0]; + const constructorCall = jest.requireMock('./alerts_client').AlertsClient.mock.calls[0][0]; securityPluginSetup.authc.createAPIKey.mockRejectedValueOnce(new Error('TLS disabled')); await expect(constructorCall.createAPIKey()).rejects.toThrowErrorMatchingInlineSnapshot( diff --git a/x-pack/legacy/plugins/alerting/server/lib/alerts_client_factory.ts b/x-pack/legacy/plugins/alerting/server/alerts_client_factory.ts similarity index 92% rename from x-pack/legacy/plugins/alerting/server/lib/alerts_client_factory.ts rename to x-pack/legacy/plugins/alerting/server/alerts_client_factory.ts index 026d6c92b0d75..94a396fbaa806 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/alerts_client_factory.ts +++ b/x-pack/legacy/plugins/alerting/server/alerts_client_factory.ts @@ -6,12 +6,12 @@ import Hapi from 'hapi'; import uuid from 'uuid'; -import { AlertsClient } from '../alerts_client'; -import { AlertTypeRegistry, SpaceIdToNamespaceFunction } from '../types'; -import { SecurityPluginStartContract, TaskManagerStartContract } from '../shim'; -import { KibanaRequest, Logger } from '../../../../../../src/core/server'; -import { InvalidateAPIKeyParams } from '../../../../../plugins/security/server'; -import { PluginStartContract as EncryptedSavedObjectsStartContract } from '../../../../../plugins/encrypted_saved_objects/server'; +import { AlertsClient } from './alerts_client'; +import { AlertTypeRegistry, SpaceIdToNamespaceFunction } from './types'; +import { SecurityPluginStartContract, TaskManagerStartContract } from './shim'; +import { KibanaRequest, Logger } from '../../../../../src/core/server'; +import { InvalidateAPIKeyParams } from '../../../../plugins/security/server'; +import { PluginStartContract as EncryptedSavedObjectsStartContract } from '../../../../plugins/encrypted_saved_objects/server'; export interface ConstructorOpts { logger: Logger; diff --git a/x-pack/legacy/plugins/alerting/server/lib/index.ts b/x-pack/legacy/plugins/alerting/server/lib/index.ts index ca4ddf9e11ad2..c41ea4a5998ff 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/index.ts +++ b/x-pack/legacy/plugins/alerting/server/lib/index.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { AlertInstance } from './alert_instance'; -export { validateAlertTypeParams } from './validate_alert_type_params'; export { parseDuration, getDurationSchema } from './parse_duration'; -export { AlertsClientFactory } from './alerts_client_factory'; -export { TaskRunnerFactory } from './task_runner_factory'; +export { LicenseState } from './license_state'; +export { validateAlertTypeParams } from './validate_alert_type_params'; diff --git a/x-pack/legacy/plugins/alerting/server/lib/result_type.ts b/x-pack/legacy/plugins/alerting/server/lib/result_type.ts new file mode 100644 index 0000000000000..644ae51292249 --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/lib/result_type.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface Ok { + tag: 'ok'; + value: T; +} + +export interface Err { + tag: 'err'; + error: E; +} +export type Result = Ok | Err; + +export function asOk(value: T): Ok { + return { + tag: 'ok', + value, + }; +} + +export function asErr(error: T): Err { + return { + tag: 'err', + error, + }; +} + +export function isOk(result: Result): result is Ok { + return result.tag === 'ok'; +} + +export function isErr(result: Result): result is Err { + return !isOk(result); +} + +export async function promiseResult(future: Promise): Promise> { + try { + return asOk(await future); + } catch (e) { + return asErr(e); + } +} + +export function map( + result: Result, + onOk: (value: T) => Resolution, + onErr: (error: E) => Resolution +): Resolution { + return isOk(result) ? onOk(result.value) : onErr(result.error); +} diff --git a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts deleted file mode 100644 index fd13452e04535..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.test.ts +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import sinon from 'sinon'; -import { schema } from '@kbn/config-schema'; -import { AlertExecutorOptions } from '../types'; -import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager/server'; -import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory'; -import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; -import { - savedObjectsClientMock, - loggingServiceMock, -} from '../../../../../../src/core/server/mocks'; - -const alertType = { - id: 'test', - name: 'My test alert', - actionGroups: ['default'], - executor: jest.fn(), -}; -let fakeTimer: sinon.SinonFakeTimers; -let taskRunnerFactory: TaskRunnerFactory; -let mockedTaskInstance: ConcreteTaskInstance; - -beforeAll(() => { - fakeTimer = sinon.useFakeTimers(); - mockedTaskInstance = { - id: '', - attempts: 0, - status: TaskStatus.Running, - version: '123', - runAt: new Date(), - scheduledAt: new Date(), - startedAt: new Date(), - retryAt: new Date(Date.now() + 5 * 60 * 1000), - state: { - startedAt: new Date(Date.now() - 5 * 60 * 1000), - }, - taskType: 'alerting:test', - params: { - alertId: '1', - }, - ownerId: null, - }; - taskRunnerFactory = new TaskRunnerFactory(); - taskRunnerFactory.initialize(taskRunnerFactoryInitializerParams); -}); - -afterAll(() => fakeTimer.restore()); - -const savedObjectsClient = savedObjectsClientMock.create(); -const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart(); -const services = { - log: jest.fn(), - callCluster: jest.fn(), - savedObjectsClient, -}; - -const taskRunnerFactoryInitializerParams: jest.Mocked = { - getServices: jest.fn().mockReturnValue(services), - executeAction: jest.fn(), - encryptedSavedObjectsPlugin, - logger: loggingServiceMock.create().get(), - spaceIdToNamespace: jest.fn().mockReturnValue(undefined), - getBasePath: jest.fn().mockReturnValue(undefined), -}; - -const mockedAlertTypeSavedObject = { - id: '1', - type: 'alert', - attributes: { - enabled: true, - alertTypeId: '123', - schedule: { interval: '10s' }, - mutedInstanceIds: [], - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], -}; - -beforeEach(() => { - jest.resetAllMocks(); - taskRunnerFactoryInitializerParams.getServices.mockReturnValue(services); -}); - -test(`throws an error if factory isn't initialized`, () => { - const factory = new TaskRunnerFactory(); - expect(() => - factory.create(alertType, { taskInstance: mockedTaskInstance }) - ).toThrowErrorMatchingInlineSnapshot(`"TaskRunnerFactory not initialized"`); -}); - -test(`throws an error if factory is already initialized`, () => { - const factory = new TaskRunnerFactory(); - factory.initialize(taskRunnerFactoryInitializerParams); - expect(() => - factory.initialize(taskRunnerFactoryInitializerParams) - ).toThrowErrorMatchingInlineSnapshot(`"TaskRunnerFactory already initialized"`); -}); - -test('successfully executes the task', async () => { - const taskRunner = taskRunnerFactory.create(alertType, { - taskInstance: mockedTaskInstance, - }); - savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); - encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - }, - references: [], - }); - const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "runAt": 1970-01-01T00:00:10.000Z, - "state": Object { - "alertInstances": Object {}, - "alertTypeState": undefined, - "previousStartedAt": 1970-01-01T00:00:00.000Z, - }, - } - `); - expect(alertType.executor).toHaveBeenCalledTimes(1); - const call = alertType.executor.mock.calls[0][0]; - expect(call.params).toMatchInlineSnapshot(` - Object { - "bar": true, - } - `); - expect(call.startedAt).toMatchInlineSnapshot(`1970-01-01T00:00:00.000Z`); - expect(call.state).toMatchInlineSnapshot(`Object {}`); - expect(call.services.alertInstanceFactory).toBeTruthy(); - expect(call.services.callCluster).toBeTruthy(); - expect(call.services).toBeTruthy(); -}); - -test('executeAction is called per alert instance that is scheduled', async () => { - alertType.executor.mockImplementation(({ services: executorServices }: AlertExecutorOptions) => { - executorServices.alertInstanceFactory('1').scheduleActions('default'); - }); - const taskRunner = taskRunnerFactory.create(alertType, { - taskInstance: mockedTaskInstance, - }); - savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); - encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - }, - references: [], - }); - await taskRunner.run(); - expect(taskRunnerFactoryInitializerParams.executeAction).toHaveBeenCalledTimes(1); - expect(taskRunnerFactoryInitializerParams.executeAction.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", - "id": "1", - "params": Object { - "foo": true, - }, - "spaceId": undefined, - }, - ] - `); -}); - -test('persists alertInstances passed in from state, only if they are scheduled for execution', async () => { - alertType.executor.mockImplementation(({ services: executorServices }: AlertExecutorOptions) => { - executorServices.alertInstanceFactory('1').scheduleActions('default'); - }); - const taskRunner = taskRunnerFactory.create(alertType, { - taskInstance: { - ...mockedTaskInstance, - state: { - ...mockedTaskInstance.state, - alertInstances: { - '1': { meta: {}, state: { bar: false } }, - '2': { meta: {}, state: { bar: false } }, - }, - }, - }, - }); - savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); - encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - }, - references: [], - }); - const runnerResult = await taskRunner.run(); - expect(runnerResult.state.alertInstances).toMatchInlineSnapshot(` - Object { - "1": Object { - "meta": Object { - "lastScheduledActions": Object { - "date": 1970-01-01T00:00:00.000Z, - "group": "default", - }, - }, - "state": Object { - "bar": false, - }, - }, - } - `); -}); - -test('validates params before executing the alert type', async () => { - const taskRunner = taskRunnerFactory.create( - { - ...alertType, - validate: { - params: schema.object({ - param1: schema.string(), - }), - }, - }, - { taskInstance: mockedTaskInstance } - ); - savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); - encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - }, - references: [], - }); - await expect(taskRunner.run()).rejects.toThrowErrorMatchingInlineSnapshot( - `"params invalid: [param1]: expected value of type [string] but got [undefined]"` - ); -}); - -test('throws error if reference not found', async () => { - const taskRunner = taskRunnerFactory.create(alertType, { - taskInstance: mockedTaskInstance, - }); - savedObjectsClient.get.mockResolvedValueOnce({ - ...mockedAlertTypeSavedObject, - references: [], - }); - encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - }, - references: [], - }); - await expect(taskRunner.run()).rejects.toThrowErrorMatchingInlineSnapshot( - `"Action reference \\"action_0\\" not found in alert id: 1"` - ); -}); - -test('uses API key when provided', async () => { - const taskRunner = taskRunnerFactory.create(alertType, { - taskInstance: mockedTaskInstance, - }); - savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); - encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - }, - references: [], - }); - - await taskRunner.run(); - expect(taskRunnerFactoryInitializerParams.getServices).toHaveBeenCalledWith({ - getBasePath: expect.anything(), - headers: { - // base64 encoded "123:abc" - authorization: 'ApiKey MTIzOmFiYw==', - }, - path: '/', - route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - }); -}); - -test(`doesn't use API key when not provided`, async () => { - const factory = new TaskRunnerFactory(); - factory.initialize(taskRunnerFactoryInitializerParams); - const taskRunner = factory.create(alertType, { - taskInstance: mockedTaskInstance, - }); - savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); - encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: {}, - references: [], - }); - - await taskRunner.run(); - - expect(taskRunnerFactoryInitializerParams.getServices).toHaveBeenCalledWith({ - getBasePath: expect.anything(), - headers: {}, - path: '/', - route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - }); -}); diff --git a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts b/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts deleted file mode 100644 index 5614188795ded..0000000000000 --- a/x-pack/legacy/plugins/alerting/server/lib/task_runner_factory.ts +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Logger } from '../../../../../../src/core/server'; -import { RunContext } from '../../../task_manager/server'; -import { createExecutionHandler } from './create_execution_handler'; -import { createAlertInstanceFactory } from './create_alert_instance_factory'; -import { AlertInstance } from './alert_instance'; -import { getNextRunAt } from './get_next_run_at'; -import { validateAlertTypeParams } from './validate_alert_type_params'; -import { PluginStartContract as EncryptedSavedObjectsStartContract } from '../../../../../plugins/encrypted_saved_objects/server'; -import { PluginStartContract as ActionsPluginStartContract } from '../../../actions'; -import { - AlertType, - AlertServices, - GetBasePathFunction, - GetServicesFunction, - RawAlert, - SpaceIdToNamespaceFunction, - IntervalSchedule, -} from '../types'; - -export interface TaskRunnerContext { - logger: Logger; - getServices: GetServicesFunction; - executeAction: ActionsPluginStartContract['execute']; - encryptedSavedObjectsPlugin: EncryptedSavedObjectsStartContract; - spaceIdToNamespace: SpaceIdToNamespaceFunction; - getBasePath: GetBasePathFunction; -} - -export class TaskRunnerFactory { - private isInitialized = false; - private taskRunnerContext?: TaskRunnerContext; - - public initialize(taskRunnerContext: TaskRunnerContext) { - if (this.isInitialized) { - throw new Error('TaskRunnerFactory already initialized'); - } - this.isInitialized = true; - this.taskRunnerContext = taskRunnerContext; - } - - public create(alertType: AlertType, { taskInstance }: RunContext) { - if (!this.isInitialized) { - throw new Error('TaskRunnerFactory not initialized'); - } - - const { - logger, - getServices, - executeAction, - encryptedSavedObjectsPlugin, - spaceIdToNamespace, - getBasePath, - } = this.taskRunnerContext!; - - return { - async run() { - const { alertId, spaceId } = taskInstance.params; - const requestHeaders: Record = {}; - const namespace = spaceIdToNamespace(spaceId); - // Only fetch encrypted attributes here, we'll create a saved objects client - // scoped with the API key to fetch the remaining data. - const { - attributes: { apiKey }, - } = await encryptedSavedObjectsPlugin.getDecryptedAsInternalUser( - 'alert', - alertId, - { namespace } - ); - - if (apiKey) { - requestHeaders.authorization = `ApiKey ${apiKey}`; - } - - const fakeRequest = { - headers: requestHeaders, - getBasePath: () => getBasePath(spaceId), - path: '/', - route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - }; - - const services = getServices(fakeRequest); - // Ensure API key is still valid and user has access - const { - attributes: { params, actions, schedule, throttle, muteAll, mutedInstanceIds }, - references, - } = await services.savedObjectsClient.get('alert', alertId); - - // Validate - const validatedAlertTypeParams = validateAlertTypeParams(alertType, params); - - // Inject ids into actions - const actionsWithIds = actions.map(action => { - const actionReference = references.find(obj => obj.name === action.actionRef); - if (!actionReference) { - throw new Error( - `Action reference "${action.actionRef}" not found in alert id: ${alertId}` - ); - } - return { - ...action, - id: actionReference.id, - }; - }); - - const executionHandler = createExecutionHandler({ - alertId, - logger, - executeAction, - apiKey, - actions: actionsWithIds, - spaceId, - alertType, - }); - const alertInstances: Record = {}; - const alertInstancesData = taskInstance.state.alertInstances || {}; - for (const id of Object.keys(alertInstancesData)) { - alertInstances[id] = new AlertInstance(alertInstancesData[id]); - } - const alertInstanceFactory = createAlertInstanceFactory(alertInstances); - - const alertTypeServices: AlertServices = { - ...services, - alertInstanceFactory, - }; - - const alertTypeState = await alertType.executor({ - alertId, - services: alertTypeServices, - params: validatedAlertTypeParams, - state: taskInstance.state.alertTypeState || {}, - startedAt: taskInstance.startedAt!, - previousStartedAt: taskInstance.state.previousStartedAt, - }); - - await Promise.all( - Object.keys(alertInstances).map(alertInstanceId => { - const alertInstance = alertInstances[alertInstanceId]; - if (alertInstance.hasScheduledActions()) { - if ( - alertInstance.isThrottled(throttle) || - muteAll || - mutedInstanceIds.includes(alertInstanceId) - ) { - return; - } - const { actionGroup, context, state } = alertInstance.getScheduledActionOptions()!; - alertInstance.updateLastScheduledActions(actionGroup); - alertInstance.unscheduleActions(); - return executionHandler({ actionGroup, context, state, alertInstanceId }); - } else { - // Cleanup alert instances that are no longer scheduling actions to avoid over populating the alertInstances object - delete alertInstances[alertInstanceId]; - } - }) - ); - - const nextRunAt = getNextRunAt( - new Date(taskInstance.startedAt!), - // we do not currently have a good way of returning the type - // from SavedObjectsClient, and as we currenrtly require a schedule - // and we only support `interval`, we can cast this safely - schedule as IntervalSchedule - ); - - return { - state: { - alertTypeState, - alertInstances, - previousStartedAt: taskInstance.startedAt!, - }, - runAt: nextRunAt, - }; - }, - }; - } -} diff --git a/x-pack/legacy/plugins/alerting/server/plugin.ts b/x-pack/legacy/plugins/alerting/server/plugin.ts index 24d4467dbd807..ede95f76bf811 100644 --- a/x-pack/legacy/plugins/alerting/server/plugin.ts +++ b/x-pack/legacy/plugins/alerting/server/plugin.ts @@ -9,7 +9,8 @@ import { first } from 'rxjs/operators'; import { Services } from './types'; import { AlertsClient } from './alerts_client'; import { AlertTypeRegistry } from './alert_type_registry'; -import { AlertsClientFactory, TaskRunnerFactory } from './lib'; +import { TaskRunnerFactory } from './task_runner'; +import { AlertsClientFactory } from './alerts_client_factory'; import { LicenseState } from './lib/license_state'; import { IClusterClient, KibanaRequest, Logger } from '../../../../../src/core/server'; import { diff --git a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts b/x-pack/legacy/plugins/alerting/server/routes/create.test.ts index 03b33b0bd40b0..2a0ae78fd78b2 100644 --- a/x-pack/legacy/plugins/alerting/server/routes/create.test.ts +++ b/x-pack/legacy/plugins/alerting/server/routes/create.test.ts @@ -20,6 +20,7 @@ const mockedAlert = { params: { bar: true, }, + throttle: '30s', actions: [ { group: 'default', @@ -44,6 +45,13 @@ test('creates an alert with proper parameters', async () => { const updatedAt = new Date(); alertsClient.create.mockResolvedValueOnce({ ...mockedAlert, + enabled: true, + muteAll: false, + createdBy: '', + updatedBy: '', + apiKey: '', + apiKeyOwner: '', + mutedInstanceIds: [], createdAt, updatedAt, id: '123', @@ -71,8 +79,14 @@ test('creates an alert with proper parameters', async () => { }, ], "alertTypeId": "1", + "apiKey": "", + "apiKeyOwner": "", "consumer": "bar", + "createdBy": "", + "enabled": true, "id": "123", + "muteAll": false, + "mutedInstanceIds": Array [], "name": "abc", "params": Object { "bar": true, @@ -83,6 +97,8 @@ test('creates an alert with proper parameters', async () => { "tags": Array [ "foo", ], + "throttle": "30s", + "updatedBy": "", } `); expect(alertsClient.create).toHaveBeenCalledTimes(1); @@ -112,7 +128,7 @@ test('creates an alert with proper parameters', async () => { "tags": Array [ "foo", ], - "throttle": null, + "throttle": "30s", }, }, ] diff --git a/x-pack/legacy/plugins/alerting/server/routes/get.test.ts b/x-pack/legacy/plugins/alerting/server/routes/get.test.ts index 5b1bdc7f69708..320e9042d87c5 100644 --- a/x-pack/legacy/plugins/alerting/server/routes/get.test.ts +++ b/x-pack/legacy/plugins/alerting/server/routes/get.test.ts @@ -29,6 +29,17 @@ const mockedAlert = { }, }, ], + consumer: 'bar', + name: 'abc', + tags: ['foo'], + enabled: true, + muteAll: false, + createdBy: '', + updatedBy: '', + apiKey: '', + apiKeyOwner: '', + throttle: '30s', + mutedInstanceIds: [], }; beforeEach(() => jest.resetAllMocks()); diff --git a/x-pack/legacy/plugins/alerting/server/lib/create_execution_handler.test.ts b/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/create_execution_handler.test.ts rename to x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/create_execution_handler.ts b/x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/create_execution_handler.ts rename to x-pack/legacy/plugins/alerting/server/task_runner/create_execution_handler.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.test.ts b/x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.test.ts rename to x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.ts b/x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.ts similarity index 92% rename from x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.ts rename to x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.ts index f9867b5372908..cea4584e1f713 100644 --- a/x-pack/legacy/plugins/alerting/server/lib/get_next_run_at.ts +++ b/x-pack/legacy/plugins/alerting/server/task_runner/get_next_run_at.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parseDuration } from './parse_duration'; +import { parseDuration } from '../lib'; import { IntervalSchedule } from '../types'; export function getNextRunAt(currentRunAt: Date, schedule: IntervalSchedule) { diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/index.ts b/x-pack/legacy/plugins/alerting/server/task_runner/index.ts new file mode 100644 index 0000000000000..f5401fbd9cd74 --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/task_runner/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { TaskRunnerFactory } from './task_runner_factory'; diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.test.ts new file mode 100644 index 0000000000000..10627c655eca8 --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.test.ts @@ -0,0 +1,400 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import sinon from 'sinon'; +import { schema } from '@kbn/config-schema'; +import { AlertExecutorOptions } from '../types'; +import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager'; +import { TaskRunnerContext } from './task_runner_factory'; +import { TaskRunner } from './task_runner'; +import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; +import { + savedObjectsClientMock, + loggingServiceMock, +} from '../../../../../../src/core/server/mocks'; + +const alertType = { + id: 'test', + name: 'My test alert', + actionGroups: ['default'], + executor: jest.fn(), +}; +let fakeTimer: sinon.SinonFakeTimers; + +describe('Task Runner', () => { + let mockedTaskInstance: ConcreteTaskInstance; + + beforeAll(() => { + fakeTimer = sinon.useFakeTimers(); + mockedTaskInstance = { + id: '', + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: { + startedAt: new Date(Date.now() - 5 * 60 * 1000), + }, + taskType: 'alerting:test', + params: { + alertId: '1', + }, + ownerId: null, + }; + }); + + afterAll(() => fakeTimer.restore()); + + const savedObjectsClient = savedObjectsClientMock.create(); + const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart(); + const services = { + log: jest.fn(), + callCluster: jest.fn(), + savedObjectsClient, + }; + + const taskRunnerFactoryInitializerParams: jest.Mocked = { + getServices: jest.fn().mockReturnValue(services), + executeAction: jest.fn(), + encryptedSavedObjectsPlugin, + logger: loggingServiceMock.create().get(), + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), + }; + + const mockedAlertTypeSavedObject = { + id: '1', + type: 'alert', + attributes: { + enabled: true, + alertTypeId: '123', + schedule: { interval: '10s' }, + mutedInstanceIds: [], + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }; + + beforeEach(() => { + jest.resetAllMocks(); + taskRunnerFactoryInitializerParams.getServices.mockReturnValue(services); + }); + + test('successfully executes the task', async () => { + const taskRunner = new TaskRunner( + alertType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); + encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + const runnerResult = await taskRunner.run(); + expect(runnerResult).toMatchInlineSnapshot(` + Object { + "runAt": 1970-01-01T00:00:10.000Z, + "state": Object { + "alertInstances": Object {}, + "alertTypeState": undefined, + "previousStartedAt": 1970-01-01T00:00:00.000Z, + }, + } + `); + expect(alertType.executor).toHaveBeenCalledTimes(1); + const call = alertType.executor.mock.calls[0][0]; + expect(call.params).toMatchInlineSnapshot(` + Object { + "bar": true, + } + `); + expect(call.startedAt).toMatchInlineSnapshot(`1970-01-01T00:00:00.000Z`); + expect(call.state).toMatchInlineSnapshot(`Object {}`); + expect(call.services.alertInstanceFactory).toBeTruthy(); + expect(call.services.callCluster).toBeTruthy(); + expect(call.services).toBeTruthy(); + }); + + test('executeAction is called per alert instance that is scheduled', async () => { + alertType.executor.mockImplementation( + ({ services: executorServices }: AlertExecutorOptions) => { + executorServices.alertInstanceFactory('1').scheduleActions('default'); + } + ); + const taskRunner = new TaskRunner( + alertType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); + encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + await taskRunner.run(); + expect(taskRunnerFactoryInitializerParams.executeAction).toHaveBeenCalledTimes(1); + expect(taskRunnerFactoryInitializerParams.executeAction.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "foo": true, + }, + "spaceId": undefined, + }, + ] + `); + }); + + test('persists alertInstances passed in from state, only if they are scheduled for execution', async () => { + alertType.executor.mockImplementation( + ({ services: executorServices }: AlertExecutorOptions) => { + executorServices.alertInstanceFactory('1').scheduleActions('default'); + } + ); + const taskRunner = new TaskRunner( + alertType, + { + ...mockedTaskInstance, + state: { + ...mockedTaskInstance.state, + alertInstances: { + '1': { meta: {}, state: { bar: false } }, + '2': { meta: {}, state: { bar: false } }, + }, + }, + }, + taskRunnerFactoryInitializerParams + ); + savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); + encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + const runnerResult = await taskRunner.run(); + expect(runnerResult.state.alertInstances).toMatchInlineSnapshot(` + Object { + "1": Object { + "meta": Object { + "lastScheduledActions": Object { + "date": 1970-01-01T00:00:00.000Z, + "group": "default", + }, + }, + "state": Object { + "bar": false, + }, + }, + } + `); + }); + + test('validates params before executing the alert type', async () => { + const taskRunner = new TaskRunner( + { + ...alertType, + validate: { + params: schema.object({ + param1: schema.string(), + }), + }, + }, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); + encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + expect(await taskRunner.run()).toMatchInlineSnapshot(` + Object { + "runAt": 1970-01-01T00:00:10.000Z, + "state": Object { + "previousStartedAt": 1970-01-01T00:00:00.000Z, + "startedAt": 1969-12-31T23:55:00.000Z, + }, + } + `); + expect(taskRunnerFactoryInitializerParams.logger.error).toHaveBeenCalledWith( + `Executing Alert \"1\" has resulted in Error: params invalid: [param1]: expected value of type [string] but got [undefined]` + ); + }); + + test('throws error if reference not found', async () => { + const taskRunner = new TaskRunner( + alertType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + savedObjectsClient.get.mockResolvedValueOnce({ + ...mockedAlertTypeSavedObject, + references: [], + }); + encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + expect(await taskRunner.run()).toMatchInlineSnapshot(` + Object { + "runAt": 1970-01-01T00:00:10.000Z, + "state": Object { + "previousStartedAt": 1970-01-01T00:00:00.000Z, + "startedAt": 1969-12-31T23:55:00.000Z, + }, + } + `); + expect(taskRunnerFactoryInitializerParams.logger.error).toHaveBeenCalledWith( + `Executing Alert \"1\" has resulted in Error: Action reference \"action_0\" not found in alert id: 1` + ); + }); + + test('uses API key when provided', async () => { + const taskRunner = new TaskRunner( + alertType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); + encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + + await taskRunner.run(); + expect(taskRunnerFactoryInitializerParams.getServices).toHaveBeenCalledWith({ + getBasePath: expect.anything(), + headers: { + // base64 encoded "123:abc" + authorization: 'ApiKey MTIzOmFiYw==', + }, + path: '/', + route: { settings: {} }, + url: { + href: '/', + }, + raw: { + req: { + url: '/', + }, + }, + }); + }); + + test(`doesn't use API key when not provided`, async () => { + const taskRunner = new TaskRunner( + alertType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); + encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: {}, + references: [], + }); + + await taskRunner.run(); + + expect(taskRunnerFactoryInitializerParams.getServices).toHaveBeenCalledWith({ + getBasePath: expect.anything(), + headers: {}, + path: '/', + route: { settings: {} }, + url: { + href: '/', + }, + raw: { + req: { + url: '/', + }, + }, + }); + }); + + test('recovers gracefully when the AlertType executor throws an exception', async () => { + alertType.executor.mockImplementation( + ({ services: executorServices }: AlertExecutorOptions) => { + throw new Error('OMG'); + } + ); + + const taskRunner = new TaskRunner( + alertType, + mockedTaskInstance, + taskRunnerFactoryInitializerParams + ); + + savedObjectsClient.get.mockResolvedValueOnce(mockedAlertTypeSavedObject); + encryptedSavedObjectsPlugin.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + }, + references: [], + }); + + const runnerResult = await taskRunner.run(); + + expect(runnerResult).toMatchInlineSnapshot(` + Object { + "runAt": 1970-01-01T00:00:10.000Z, + "state": Object { + "previousStartedAt": 1970-01-01T00:00:00.000Z, + "startedAt": 1969-12-31T23:55:00.000Z, + }, + } + `); + }); +}); diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts new file mode 100644 index 0000000000000..2347e9e608ed9 --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner.ts @@ -0,0 +1,241 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { pick, mapValues, omit } from 'lodash'; +import { Logger } from '../../../../../../src/core/server'; +import { SavedObject } from '../../../../../../src/core/server'; +import { TaskRunnerContext } from './task_runner_factory'; +import { ConcreteTaskInstance } from '../../../task_manager'; +import { createExecutionHandler } from './create_execution_handler'; +import { AlertInstance, createAlertInstanceFactory } from '../alert_instance'; +import { getNextRunAt } from './get_next_run_at'; +import { validateAlertTypeParams } from '../lib'; +import { AlertType, RawAlert, IntervalSchedule, Services, State } from '../types'; +import { promiseResult, map } from '../lib/result_type'; + +type AlertInstances = Record; + +export class TaskRunner { + private context: TaskRunnerContext; + private logger: Logger; + private taskInstance: ConcreteTaskInstance; + private alertType: AlertType; + + constructor( + alertType: AlertType, + taskInstance: ConcreteTaskInstance, + context: TaskRunnerContext + ) { + this.context = context; + this.logger = context.logger; + this.alertType = alertType; + this.taskInstance = taskInstance; + } + + async getApiKeyForAlertPermissions(alertId: string, spaceId: string) { + const namespace = this.context.spaceIdToNamespace(spaceId); + // Only fetch encrypted attributes here, we'll create a saved objects client + // scoped with the API key to fetch the remaining data. + const { + attributes: { apiKey }, + } = await this.context.encryptedSavedObjectsPlugin.getDecryptedAsInternalUser( + 'alert', + alertId, + { namespace } + ); + + return apiKey; + } + + async getServicesWithSpaceLevelPermissions(spaceId: string, apiKey: string | null) { + const requestHeaders: Record = {}; + + if (apiKey) { + requestHeaders.authorization = `ApiKey ${apiKey}`; + } + + const fakeRequest = { + headers: requestHeaders, + getBasePath: () => this.context.getBasePath(spaceId), + path: '/', + route: { settings: {} }, + url: { + href: '/', + }, + raw: { + req: { + url: '/', + }, + }, + }; + + return this.context.getServices(fakeRequest); + } + + getExecutionHandler( + alertId: string, + spaceId: string, + apiKey: string | null, + actions: RawAlert['actions'], + references: SavedObject['references'] + ) { + // Inject ids into actions + const actionsWithIds = actions.map(action => { + const actionReference = references.find(obj => obj.name === action.actionRef); + if (!actionReference) { + throw new Error(`Action reference "${action.actionRef}" not found in alert id: ${alertId}`); + } + return { + ...action, + id: actionReference.id, + }; + }); + + return createExecutionHandler({ + alertId, + logger: this.logger, + executeAction: this.context.executeAction, + apiKey, + actions: actionsWithIds, + spaceId, + alertType: this.alertType, + }); + } + + async executeAlertInstance( + alertInstanceId: string, + alertInstance: AlertInstance, + executionHandler: ReturnType + ) { + const { actionGroup, context, state } = alertInstance.getScheduledActionOptions()!; + alertInstance.updateLastScheduledActions(actionGroup); + alertInstance.unscheduleActions(); + return executionHandler({ actionGroup, context, state, alertInstanceId }); + } + + async executeAlertInstances( + services: Services, + { params, throttle, muteAll, mutedInstanceIds }: SavedObject['attributes'], + executionHandler: ReturnType + ): Promise { + const { + params: { alertId }, + state: { alertInstances: alertRawInstances = {}, alertTypeState = {}, previousStartedAt }, + } = this.taskInstance; + + const alertInstances = mapValues( + alertRawInstances, + alert => new AlertInstance(alert) + ); + + const updatedAlertTypeState = await this.alertType.executor({ + alertId, + services: { + ...services, + alertInstanceFactory: createAlertInstanceFactory(alertInstances), + }, + params, + state: alertTypeState, + startedAt: this.taskInstance.startedAt!, + previousStartedAt, + }); + + // Cleanup alert instances that are no longer scheduling actions to avoid over populating the alertInstances object + const instancesWithScheduledActions = pick( + alertInstances, + alertInstance => alertInstance.hasScheduledActions() + ); + + if (!muteAll) { + const enabledAlertInstances = omit( + instancesWithScheduledActions, + ...mutedInstanceIds + ); + + await Promise.all( + Object.entries(enabledAlertInstances) + .filter( + ([, alertInstance]: [string, AlertInstance]) => !alertInstance.isThrottled(throttle) + ) + .map(([id, alertInstance]: [string, AlertInstance]) => + this.executeAlertInstance(id, alertInstance, executionHandler) + ) + ); + } + + return { + alertTypeState: updatedAlertTypeState, + alertInstances: instancesWithScheduledActions, + }; + } + + async validateAndRunAlert( + services: Services, + apiKey: string | null, + attributes: SavedObject['attributes'], + references: SavedObject['references'] + ) { + const { + params: { alertId, spaceId }, + } = this.taskInstance; + + // Validate + const params = validateAlertTypeParams(this.alertType, attributes.params); + const executionHandler = this.getExecutionHandler( + alertId, + spaceId, + apiKey, + attributes.actions, + references + ); + return this.executeAlertInstances(services, { ...attributes, params }, executionHandler); + } + + async run() { + const { + params: { alertId, spaceId }, + startedAt: previousStartedAt, + state: originalState, + } = this.taskInstance; + + const apiKey = await this.getApiKeyForAlertPermissions(alertId, spaceId); + const services = await this.getServicesWithSpaceLevelPermissions(spaceId, apiKey); + + // Ensure API key is still valid and user has access + const { attributes, references } = await services.savedObjectsClient.get( + 'alert', + alertId + ); + + return { + state: map( + await promiseResult( + this.validateAndRunAlert(services, apiKey, attributes, references) + ), + (stateUpdates: State) => { + return { + ...stateUpdates, + previousStartedAt, + }; + }, + (err: Error) => { + this.logger.error(`Executing Alert "${alertId}" has resulted in Error: ${err.message}`); + return { + ...originalState, + previousStartedAt, + }; + } + ), + runAt: getNextRunAt( + new Date(this.taskInstance.startedAt!), + // we do not currently have a good way of returning the type + // from SavedObjectsClient, and as we currenrtly require a schedule + // and we only support `interval`, we can cast this safely + attributes.schedule as IntervalSchedule + ), + }; + } +} diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.test.ts new file mode 100644 index 0000000000000..2ea1256352bec --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.test.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import sinon from 'sinon'; +import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager'; +import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory'; +import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; +import { + savedObjectsClientMock, + loggingServiceMock, +} from '../../../../../../src/core/server/mocks'; + +const alertType = { + id: 'test', + name: 'My test alert', + actionGroups: ['default'], + executor: jest.fn(), +}; +let fakeTimer: sinon.SinonFakeTimers; + +describe('Task Runner Factory', () => { + let mockedTaskInstance: ConcreteTaskInstance; + + beforeAll(() => { + fakeTimer = sinon.useFakeTimers(); + mockedTaskInstance = { + id: '', + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: { + startedAt: new Date(Date.now() - 5 * 60 * 1000), + }, + taskType: 'alerting:test', + params: { + alertId: '1', + }, + ownerId: null, + }; + }); + + afterAll(() => fakeTimer.restore()); + + const savedObjectsClient = savedObjectsClientMock.create(); + const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart(); + const services = { + log: jest.fn(), + callCluster: jest.fn(), + savedObjectsClient, + }; + + const taskRunnerFactoryInitializerParams: jest.Mocked = { + getServices: jest.fn().mockReturnValue(services), + executeAction: jest.fn(), + encryptedSavedObjectsPlugin, + logger: loggingServiceMock.create().get(), + spaceIdToNamespace: jest.fn().mockReturnValue(undefined), + getBasePath: jest.fn().mockReturnValue(undefined), + }; + + beforeEach(() => { + jest.resetAllMocks(); + taskRunnerFactoryInitializerParams.getServices.mockReturnValue(services); + }); + + test(`throws an error if factory isn't initialized`, () => { + const factory = new TaskRunnerFactory(); + expect(() => + factory.create(alertType, { taskInstance: mockedTaskInstance }) + ).toThrowErrorMatchingInlineSnapshot(`"TaskRunnerFactory not initialized"`); + }); + + test(`throws an error if factory is already initialized`, () => { + const factory = new TaskRunnerFactory(); + factory.initialize(taskRunnerFactoryInitializerParams); + expect(() => + factory.initialize(taskRunnerFactoryInitializerParams) + ).toThrowErrorMatchingInlineSnapshot(`"TaskRunnerFactory already initialized"`); + }); +}); diff --git a/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.ts new file mode 100644 index 0000000000000..7186e1e729bda --- /dev/null +++ b/x-pack/legacy/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Logger } from '../../../../../../src/core/server'; +import { RunContext } from '../../../task_manager'; +import { PluginStartContract as EncryptedSavedObjectsStartContract } from '../../../../../plugins/encrypted_saved_objects/server'; +import { PluginStartContract as ActionsPluginStartContract } from '../../../actions'; +import { + AlertType, + GetBasePathFunction, + GetServicesFunction, + SpaceIdToNamespaceFunction, +} from '../types'; +import { TaskRunner } from './task_runner'; + +export interface TaskRunnerContext { + logger: Logger; + getServices: GetServicesFunction; + executeAction: ActionsPluginStartContract['execute']; + encryptedSavedObjectsPlugin: EncryptedSavedObjectsStartContract; + spaceIdToNamespace: SpaceIdToNamespaceFunction; + getBasePath: GetBasePathFunction; +} + +export class TaskRunnerFactory { + private isInitialized = false; + private taskRunnerContext?: TaskRunnerContext; + + public initialize(taskRunnerContext: TaskRunnerContext) { + if (this.isInitialized) { + throw new Error('TaskRunnerFactory already initialized'); + } + this.isInitialized = true; + this.taskRunnerContext = taskRunnerContext; + } + + public create(alertType: AlertType, { taskInstance }: RunContext) { + if (!this.isInitialized) { + throw new Error('TaskRunnerFactory not initialized'); + } + + return new TaskRunner(alertType, taskInstance, this.taskRunnerContext!); + } +} diff --git a/x-pack/legacy/plugins/alerting/server/lib/transform_action_params.test.ts b/x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.test.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/transform_action_params.test.ts rename to x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.test.ts diff --git a/x-pack/legacy/plugins/alerting/server/lib/transform_action_params.ts b/x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.ts similarity index 100% rename from x-pack/legacy/plugins/alerting/server/lib/transform_action_params.ts rename to x-pack/legacy/plugins/alerting/server/task_runner/transform_action_params.ts diff --git a/x-pack/legacy/plugins/alerting/server/types.ts b/x-pack/legacy/plugins/alerting/server/types.ts index a2390bf93d005..9b03f9b02aa0a 100644 --- a/x-pack/legacy/plugins/alerting/server/types.ts +++ b/x-pack/legacy/plugins/alerting/server/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertInstance } from './lib'; +import { AlertInstance } from './alert_instance'; import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; import { SavedObjectAttributes, SavedObjectsClientContract } from '../../../../../src/core/server'; @@ -65,6 +65,7 @@ export interface IntervalSchedule extends SavedObjectAttributes { } export interface Alert { + id: string; enabled: boolean; name: string; tags: string[]; @@ -85,6 +86,8 @@ export interface Alert { mutedInstanceIds: string[]; } +export type PartialAlert = Pick & Partial>; + export interface RawAlert extends SavedObjectAttributes { enabled: boolean; name: string; diff --git a/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts index 79ffe58d419bd..b2cfd826e6207 100644 --- a/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/legacy/plugins/beats_management/public/lib/adapters/framework/kibana_framework_adapter.ts @@ -11,6 +11,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { UIRoutes } from 'ui/routes'; import { isLeft } from 'fp-ts/lib/Either'; +import { npSetup } from 'ui/new_platform'; +import { SecurityPluginSetup } from '../../../../../../../plugins/security/public'; import { BufferedKibanaServiceCall, KibanaAdapterServiceRefs, KibanaUIConfig } from '../../types'; import { FrameworkAdapter, @@ -58,7 +60,7 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter { }; public async waitUntilFrameworkReady(): Promise { - const $injector = await this.onKibanaReady(); + await this.onKibanaReady(); const xpackInfo: any = this.xpackInfoService; let xpackInfoUnpacked: FrameworkInfo; @@ -95,8 +97,10 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter { } this.xpackInfo = xpackInfoUnpacked; + const securitySetup = ((npSetup.plugins as unknown) as { security?: SecurityPluginSetup }) + .security; try { - this.shieldUser = await $injector.get('ShieldUser').getCurrent().$promise; + this.shieldUser = (await securitySetup?.authc.getCurrentUser()) || null; const assertUser = RuntimeFrameworkUser.decode(this.shieldUser); if (isLeft(assertUser)) { diff --git a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx index a700a455cd2bb..9491fe3024f94 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/components/metrics_explorer/chart.tsx @@ -7,15 +7,7 @@ import React, { useCallback, useMemo } from 'react'; import { EuiTitle, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { - Axis, - Chart, - getAxisId, - niceTimeFormatter, - Position, - Settings, - TooltipValue, -} from '@elastic/charts'; +import { Axis, Chart, niceTimeFormatter, Position, Settings, TooltipValue } from '@elastic/charts'; import { first, last } from 'lodash'; import moment from 'moment'; import { MetricsExplorerSeries } from '../../../server/routes/metrics_explorer/types'; @@ -139,13 +131,13 @@ export const MetricsExplorerChart = ({ /> ))} = { @@ -66,8 +56,8 @@ export const MetricsExplorerAreaChart = ({ metric, id, series, type, stack }: Pr }; return ( ); }; @@ -87,13 +77,6 @@ export const MetricsExplorerBarChart = ({ metric, id, series, stack }: Props) => colorTransformer(MetricsExplorerColor.color0); const yAccessor = `metric_${id}`; - const specId = getSpecId(yAccessor); - const colors: DataSeriesColorsValues = { - colorValues: [], - specId, - }; - const customColors: CustomSeriesColorsMap = new Map(); - customColors.set(colors, color); const chartId = `series-${series.id}-${yAccessor}`; const seriesBarStyle: RecursivePartial = { @@ -108,8 +91,8 @@ export const MetricsExplorerBarChart = ({ metric, id, series, stack }: Props) => }; return ( data={series.rows} stackAccessors={stack ? ['timestamp'] : void 0} barSeriesStyle={seriesBarStyle} - customSeriesColors={customColors} + customSeriesColors={[color]} /> ); }; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/index.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/index.ts index a737d19a5923d..4c4c317759bb3 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/index.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/index.ts @@ -4,23 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useContext } from 'react'; -import createContainer from 'constate'; -import { ReduxStateContext } from '../../../utils/redux_context'; -import { logFilterSelectors as logFilterReduxSelectors } from '../../../store/local/selectors'; - -export const useLogFilterState = () => { - const { local: state } = useContext(ReduxStateContext); - const filterQuery = logFilterReduxSelectors.selectLogFilterQueryAsJson(state); - return { filterQuery }; -}; - -export interface LogFilterStateParams { - filterQuery: string | null; -} - -export const logFilterInitialState = { - filterQuery: null, -}; - -export const LogFilterState = createContainer(useLogFilterState); +export * from './log_filter_state'; +export * from './with_log_filter_url_state'; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts new file mode 100644 index 0000000000000..2911ee729638a --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useState, useMemo } from 'react'; +import createContainer from 'constate'; +import { IIndexPattern } from 'src/plugins/data/public'; +import { esKuery } from '../../../../../../../../src/plugins/data/public'; +import { convertKueryToElasticSearchQuery } from '../../../utils/kuery'; + +export interface KueryFilterQuery { + kind: 'kuery'; + expression: string; +} + +export interface SerializedFilterQuery { + query: KueryFilterQuery; + serializedQuery: string; +} + +interface LogFilterInternalStateParams { + filterQuery: SerializedFilterQuery | null; + filterQueryDraft: KueryFilterQuery | null; +} + +export const logFilterInitialState: LogFilterInternalStateParams = { + filterQuery: null, + filterQueryDraft: null, +}; + +export type LogFilterStateParams = Omit & { + filterQuery: SerializedFilterQuery['serializedQuery'] | null; + filterQueryAsKuery: SerializedFilterQuery['query'] | null; + isFilterQueryDraftValid: boolean; +}; +export interface LogFilterCallbacks { + setLogFilterQueryDraft: (expression: string) => void; + applyLogFilterQuery: (expression: string) => void; +} + +export const useLogFilterState: (props: { + indexPattern: IIndexPattern; +}) => LogFilterStateParams & LogFilterCallbacks = ({ indexPattern }) => { + const [state, setState] = useState(logFilterInitialState); + const { filterQuery, filterQueryDraft } = state; + + const setLogFilterQueryDraft = useMemo(() => { + const setDraft = (payload: KueryFilterQuery) => + setState(prevState => ({ ...prevState, filterQueryDraft: payload })); + return (expression: string) => + setDraft({ + kind: 'kuery', + expression, + }); + }, []); + const applyLogFilterQuery = useMemo(() => { + const applyQuery = (payload: SerializedFilterQuery) => + setState(prevState => ({ + ...prevState, + filterQueryDraft: payload.query, + filterQuery: payload, + })); + return (expression: string) => + applyQuery({ + query: { + kind: 'kuery', + expression, + }, + serializedQuery: convertKueryToElasticSearchQuery(expression, indexPattern), + }); + }, [indexPattern]); + + const isFilterQueryDraftValid = useMemo(() => { + if (filterQueryDraft?.kind === 'kuery') { + try { + esKuery.fromKueryExpression(filterQueryDraft.expression); + } catch (err) { + return false; + } + } + + return true; + }, [filterQueryDraft]); + + const serializedFilterQuery = useMemo(() => (filterQuery ? filterQuery.serializedQuery : null), [ + filterQuery, + ]); + + return { + ...state, + filterQueryAsKuery: state.filterQuery ? state.filterQuery.query : null, + filterQuery: serializedFilterQuery, + isFilterQueryDraftValid, + setLogFilterQueryDraft, + applyLogFilterQuery, + }; +}; + +export const LogFilterState = createContainer(useLogFilterState); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx new file mode 100644 index 0000000000000..d1da6c715cfc5 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext } from 'react'; +import { LogFilterState, LogFilterStateParams } from './log_filter_state'; +import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state'; + +type LogFilterUrlState = LogFilterStateParams['filterQueryAsKuery']; + +export const WithLogFilterUrlState: React.FC = () => { + const { filterQueryAsKuery, applyLogFilterQuery } = useContext(LogFilterState.Context); + return ( + { + if (urlState) { + applyLogFilterQuery(urlState.expression); + } + }} + onInitialize={urlState => { + if (urlState) { + applyLogFilterQuery(urlState.expression); + } + }} + /> + ); +}; + +const mapToFilterQuery = (value: any): LogFilterUrlState | undefined => + value?.kind === 'kuery' && typeof value.expression === 'string' + ? { + kind: value.kind, + expression: value.expression, + } + : undefined; + +export const replaceLogFilterInQueryString = (expression: string) => + replaceStateKeyInQueryString('logFilter', { + kind: 'kuery', + expression, + }); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/redux_bridges.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/redux_bridges.tsx index 9ea8987d4f326..393caae52511f 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/redux_bridges.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_highlights/redux_bridges.tsx @@ -7,7 +7,6 @@ import React, { useEffect, useContext } from 'react'; import { TimeKey } from '../../../../common/time'; -import { withLogFilter } from '../with_log_filter'; import { withLogPosition } from '../with_log_position'; import { LogHighlightsState } from './log_highlights'; @@ -35,21 +34,8 @@ export const LogHighlightsPositionBridge = withLogPosition( } ); -export const LogHighlightsFilterQueryBridge = withLogFilter( - ({ serializedFilterQuery }: { serializedFilterQuery: string | null }) => { - const { setFilterQuery } = useContext(LogHighlightsState.Context); - - useEffect(() => { - setFilterQuery(serializedFilterQuery); - }, [serializedFilterQuery, setFilterQuery]); - - return null; - } -); - -export const LogHighlightsBridge = ({ indexPattern }: { indexPattern: any }) => ( +export const LogHighlightsBridge = () => ( <> - ); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts index 61c603130df52..7971c12bdfda0 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_summary/with_summary.ts @@ -7,19 +7,18 @@ import { useContext } from 'react'; import { connect } from 'react-redux'; -import { logFilterSelectors, logPositionSelectors, State } from '../../../store'; +import { logPositionSelectors, State } from '../../../store'; import { RendererFunction } from '../../../utils/typed_react'; import { Source } from '../../source'; import { LogViewConfiguration } from '../log_view_configuration'; import { LogSummaryBuckets, useLogSummary } from './log_summary'; +import { LogFilterState } from '../log_filter'; export const WithSummary = connect((state: State) => ({ visibleMidpointTime: logPositionSelectors.selectVisibleMidpointOrTargetTime(state), - filterQuery: logFilterSelectors.selectLogFilterQueryAsJson(state), }))( ({ children, - filterQuery, visibleMidpointTime, }: { children: RendererFunction<{ @@ -27,11 +26,11 @@ export const WithSummary = connect((state: State) => ({ start: number | null; end: number | null; }>; - filterQuery: string | null; visibleMidpointTime: number | null; }) => { const { intervalSize } = useContext(LogViewConfiguration.Context); const { sourceId } = useContext(Source.Context); + const { filterQuery } = useContext(LogFilterState.Context); const { buckets, start, end } = useLogSummary( sourceId, diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/with_log_filter.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/with_log_filter.tsx deleted file mode 100644 index 60261fc728ebb..0000000000000 --- a/x-pack/legacy/plugins/infra/public/containers/logs/with_log_filter.tsx +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { connect } from 'react-redux'; - -import { IIndexPattern } from 'src/plugins/data/public'; -import { logFilterActions, logFilterSelectors, State } from '../../store'; -import { FilterQuery } from '../../store/local/log_filter'; -import { convertKueryToElasticSearchQuery } from '../../utils/kuery'; -import { asChildFunctionRenderer } from '../../utils/typed_react'; -import { bindPlainActionCreators } from '../../utils/typed_redux'; -import { replaceStateKeyInQueryString, UrlStateContainer } from '../../utils/url_state'; - -interface WithLogFilterProps { - indexPattern: IIndexPattern; -} - -export const withLogFilter = connect( - (state: State) => ({ - filterQuery: logFilterSelectors.selectLogFilterQuery(state), - serializedFilterQuery: logFilterSelectors.selectLogFilterQueryAsJson(state), - filterQueryDraft: logFilterSelectors.selectLogFilterQueryDraft(state), - isFilterQueryDraftValid: logFilterSelectors.selectIsLogFilterQueryDraftValid(state), - }), - (dispatch, ownProps: WithLogFilterProps) => - bindPlainActionCreators({ - applyFilterQuery: (query: FilterQuery) => - logFilterActions.applyLogFilterQuery({ - query, - serializedQuery: convertKueryToElasticSearchQuery( - query.expression, - ownProps.indexPattern - ), - }), - applyFilterQueryFromKueryExpression: (expression: string) => - logFilterActions.applyLogFilterQuery({ - query: { - kind: 'kuery', - expression, - }, - serializedQuery: convertKueryToElasticSearchQuery(expression, ownProps.indexPattern), - }), - setFilterQueryDraft: logFilterActions.setLogFilterQueryDraft, - setFilterQueryDraftFromKueryExpression: (expression: string) => - logFilterActions.setLogFilterQueryDraft({ - kind: 'kuery', - expression, - }), - })(dispatch) -); - -export const WithLogFilter = asChildFunctionRenderer(withLogFilter); - -/** - * Url State - */ - -type LogFilterUrlState = ReturnType; - -type WithLogFilterUrlStateProps = WithLogFilterProps; - -export const WithLogFilterUrlState: React.FC = ({ indexPattern }) => ( - - {({ applyFilterQuery, filterQuery }) => ( - { - if (urlState) { - applyFilterQuery(urlState); - } - }} - onInitialize={urlState => { - if (urlState) { - applyFilterQuery(urlState); - } - }} - /> - )} - -); - -const mapToFilterQuery = (value: any): LogFilterUrlState | undefined => - value && value.kind === 'kuery' && typeof value.expression === 'string' - ? { - kind: value.kind, - expression: value.expression, - } - : undefined; - -export const replaceLogFilterInQueryString = (expression: string) => - replaceStateKeyInQueryString('logFilter', { - kind: 'kuery', - expression, - }); diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.tsx b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.tsx index c409470eb24c7..bfa1ede4236aa 100644 --- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_logs.tsx @@ -8,7 +8,7 @@ import compose from 'lodash/fp/compose'; import React from 'react'; import { match as RouteMatch, Redirect, RouteComponentProps } from 'react-router-dom'; -import { replaceLogFilterInQueryString } from '../../containers/logs/with_log_filter'; +import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter'; import { replaceLogPositionInQueryString } from '../../containers/logs/with_log_position'; import { replaceSourceIdInQueryString } from '../../containers/source_id'; import { getFilterFromLocation, getTimeFromLocation } from './query_params'; diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx index 4af50df343859..73d0633cb901b 100644 --- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; import { LoadingPage } from '../../components/loading_page'; -import { replaceLogFilterInQueryString } from '../../containers/logs/with_log_filter'; +import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter'; import { replaceLogPositionInQueryString } from '../../containers/logs/with_log_position'; import { replaceSourceIdInQueryString } from '../../containers/source_id'; import { InfraNodeType, SourceConfigurationFields } from '../../graphql/types'; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/chart.tsx index e4100c6d774b1..a75e6c50ab03f 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/chart.tsx @@ -9,14 +9,11 @@ import { Axis, BarSeries, Chart, - getAxisId, - getSpecId, niceTimeFormatter, Settings, TooltipValue, LIGHT_THEME, DARK_THEME, - getAnnotationId, RectAnnotation, } from '@elastic/charts'; import numeral from '@elastic/numeral'; @@ -44,7 +41,7 @@ export const AnomaliesChart: React.FunctionComponent<{ [timeRange] ); - const logEntryRateSpecId = getSpecId('averageValues'); + const logEntryRateSpecId = 'averageValues'; const tooltipProps = useMemo( () => ({ @@ -67,13 +64,13 @@ export const AnomaliesChart: React.FunctionComponent<{
numeral(value.toPrecision(3)).format('0[.][00]a')} // /~https://github.com/adamwdraper/Numeral-js/issues/194 /> @@ -102,7 +99,7 @@ export const AnomaliesChart: React.FunctionComponent<{ }; interface SeverityConfig { - annotationId: AnnotationId; + id: AnnotationId; style: { fill: string; opacity: number; @@ -111,19 +108,19 @@ interface SeverityConfig { const severityConfigs: Record = { warning: { - annotationId: getAnnotationId(`anomalies-warning`), + id: `anomalies-warning`, style: { fill: 'rgb(125, 180, 226)', opacity: 0.7 }, }, minor: { - annotationId: getAnnotationId(`anomalies-minor`), + id: `anomalies-minor`, style: { fill: 'rgb(255, 221, 0)', opacity: 0.7 }, }, major: { - annotationId: getAnnotationId(`anomalies-major`), + id: `anomalies-major`, style: { fill: 'rgb(229, 113, 0)', opacity: 0.7 }, }, critical: { - annotationId: getAnnotationId(`anomalies-critical`), + id: `anomalies-critical`, style: { fill: 'rgb(228, 72, 72)', opacity: 0.7 }, }, }; @@ -138,7 +135,7 @@ const renderAnnotations = ( diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx index 5055e3fc08239..7969c61bc9f34 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/bar_chart.tsx @@ -8,8 +8,6 @@ import { Axis, BarSeries, Chart, - getAxisId, - getSpecId, niceTimeFormatter, Settings, TooltipValue, @@ -37,8 +35,6 @@ export const LogEntryRateBarChart: React.FunctionComponent<{ [timeRange] ); - const logEntryRateSpecId = getSpecId('averageValues'); - const tooltipProps = useMemo( () => ({ headerFormatter: (tooltipData: TooltipValue) => @@ -61,18 +57,18 @@ export const LogEntryRateBarChart: React.FunctionComponent<{
numeral(value.toPrecision(3)).format('0[.][00]a')} // /~https://github.com/adamwdraper/Numeral-js/issues/194 /> { - const { createDerivedIndexPattern, source, sourceId, version } = useContext(Source.Context); + const { source, sourceId, version } = useContext(Source.Context); const { intervalSize, textScale, textWrap } = useContext(LogViewConfiguration.Context); const { setFlyoutVisibility, @@ -43,37 +43,32 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { isLoading, } = useContext(LogFlyoutState.Context); const { logSummaryHighlights } = useContext(LogHighlightsState.Context); - const derivedIndexPattern = createDerivedIndexPattern('logs'); + const { applyLogFilterQuery } = useContext(LogFilterState.Context); return ( <> - - + - - {({ applyFilterQueryFromKueryExpression }) => ( - - {({ jumpToTargetPosition, stopLiveStreaming }) => - flyoutVisible ? ( - { - jumpToTargetPosition(timeKey); - setSurroundingLogsId(flyoutItemId); - stopLiveStreaming(); - }} - setFlyoutVisibility={setFlyoutVisibility} - flyoutItem={flyoutItem} - loading={isLoading} - /> - ) : null - } - - )} - + + {({ jumpToTargetPosition, stopLiveStreaming }) => + flyoutVisible ? ( + { + jumpToTargetPosition(timeKey); + setSurroundingLogsId(flyoutItemId); + stopLiveStreaming(); + }} + setFlyoutVisibility={setFlyoutVisibility} + flyoutItem={flyoutItem} + loading={isLoading} + /> + ) : null + } + {({ @@ -132,12 +127,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { {({ buckets }) => ( - {({ - isAutoReloading, - jumpToTargetPosition, - visibleMidpointTime, - visibleTimeInterval, - }) => ( + {({ jumpToTargetPosition, visibleMidpointTime, visibleTimeInterval }) => ( {({ isReloading }) => ( { + const { createDerivedIndexPattern } = useContext(Source.Context); + const derivedIndexPattern = createDerivedIndexPattern('logs'); + return ( + + + {children} + + ); +}; + const LogEntriesStateProvider: React.FC = ({ children }) => { const { sourceId } = useContext(Source.Context); const { timeKey, pagesBeforeStart, pagesAfterEnd, isAutoReloading } = useContext( @@ -51,11 +62,11 @@ export const LogsPageProviders: React.FunctionComponent = ({ children }) => { - + {children} - + diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 60c0ebdfc349a..84be3eeaf238d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -19,7 +19,7 @@ import { LogTextWrapControls } from '../../../components/logging/log_text_wrap_c import { LogTimeControls } from '../../../components/logging/log_time_controls'; import { LogFlyout } from '../../../containers/logs/log_flyout'; import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration'; -import { WithLogFilter } from '../../../containers/logs/with_log_filter'; +import { LogFilterState } from '../../../containers/logs/log_filter'; import { WithLogPosition } from '../../../containers/logs/with_log_position'; import { Source } from '../../../containers/source'; import { WithKueryAutocompletion } from '../../../containers/with_kuery_autocompletion'; @@ -37,7 +37,12 @@ export const LogsToolbar = () => { textScale, textWrap, } = useContext(LogViewConfiguration.Context); - + const { + filterQueryDraft, + isFilterQueryDraftValid, + applyLogFilterQuery, + setLogFilterQueryDraft, + } = useContext(LogFilterState.Context); const { setSurroundingLogsId } = useContext(LogFlyout.Context); const { @@ -55,38 +60,28 @@ export const LogsToolbar = () => { {({ isLoadingSuggestions, loadSuggestions, suggestions }) => ( - - {({ - applyFilterQueryFromKueryExpression, - filterQueryDraft, - isFilterQueryDraftValid, - setFilterQueryDraftFromKueryExpression, - }) => ( - { - setSurroundingLogsId(null); - setFilterQueryDraftFromKueryExpression(expression); - }} - onSubmit={(expression: string) => { - setSurroundingLogsId(null); - applyFilterQueryFromKueryExpression(expression); - }} - placeholder={i18n.translate( - 'xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder', - { defaultMessage: 'Search for log entries… (e.g. host.name:host-1)' } - )} - suggestions={suggestions} - value={filterQueryDraft ? filterQueryDraft.expression : ''} - aria-label={i18n.translate( - 'xpack.infra.logsPage.toolbar.kqlSearchFieldAriaLabel', - { defaultMessage: 'Search for log entries' } - )} - /> + { + setSurroundingLogsId(null); + setLogFilterQueryDraft(expression); + }} + onSubmit={(expression: string) => { + setSurroundingLogsId(null); + applyLogFilterQuery(expression); + }} + placeholder={i18n.translate( + 'xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder', + { defaultMessage: 'Search for log entries… (e.g. host.name:host-1)' } )} - + suggestions={suggestions} + value={filterQueryDraft ? filterQueryDraft.expression : ''} + aria-label={i18n.translate('xpack.infra.logsPage.toolbar.kqlSearchFieldAriaLabel', { + defaultMessage: 'Search for log entries', + })} + /> )} diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx index 3d4d656823332..1731e9aa62cda 100644 --- a/x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx @@ -6,15 +6,7 @@ import React, { useCallback, useMemo } from 'react'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import { - Axis, - Chart, - getAxisId, - niceTimeFormatter, - Position, - Settings, - TooltipValue, -} from '@elastic/charts'; +import { Axis, Chart, niceTimeFormatter, Position, Settings, TooltipValue } from '@elastic/charts'; import { EuiPageContentBody } from '@elastic/eui'; import { getChartTheme } from '../../../components/metrics_explorer/helpers/get_chart_theme'; import { SeriesChart } from './series_chart'; @@ -106,12 +98,12 @@ export const ChartSectionVis = ({
- + {metric && metric.series.map(series => ( { visible: true, }, }; - const colors: DataSeriesColorsValues = { - colorValues: [], - specId: getSpecId(id), - }; - const customColors: CustomSeriesColorsMap = new Map(); - customColors.set(colors, color || '#999'); return ( { yAccessors={['value']} data={series.data} areaSeriesStyle={style} - customSeriesColors={color ? customColors : void 0} + customSeriesColors={color ? [color] : void 0} stackAccessors={stack ? ['timestamp'] : void 0} /> ); @@ -79,15 +70,9 @@ export const BarChart = ({ id, color, series, name, type, stack }: Props) => { opacity: 1, }, }; - const colors: DataSeriesColorsValues = { - colorValues: [], - specId: getSpecId(id), - }; - const customColors: CustomSeriesColorsMap = new Map(); - customColors.set(colors, color || '#999'); return ( { yAccessors={['value']} data={series.data} barSeriesStyle={style} - customSeriesColors={customColors} + customSeriesColors={color ? [color] : void 0} stackAccessors={stack ? ['timestamp'] : void 0} /> ); diff --git a/x-pack/legacy/plugins/infra/public/store/actions.ts b/x-pack/legacy/plugins/infra/public/store/actions.ts index e2be0d64b8f1e..ce0c0d8a4e01b 100644 --- a/x-pack/legacy/plugins/infra/public/store/actions.ts +++ b/x-pack/legacy/plugins/infra/public/store/actions.ts @@ -5,7 +5,6 @@ */ export { - logFilterActions, logPositionActions, waffleFilterActions, waffleTimeActions, diff --git a/x-pack/legacy/plugins/infra/public/store/local/actions.ts b/x-pack/legacy/plugins/infra/public/store/local/actions.ts index 1c6918ea4dc12..1827005e5e5e1 100644 --- a/x-pack/legacy/plugins/infra/public/store/local/actions.ts +++ b/x-pack/legacy/plugins/infra/public/store/local/actions.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { logFilterActions } from './log_filter'; export { logPositionActions } from './log_position'; export { waffleFilterActions } from './waffle_filter'; export { waffleTimeActions } from './waffle_time'; diff --git a/x-pack/legacy/plugins/infra/public/store/local/log_filter/actions.ts b/x-pack/legacy/plugins/infra/public/store/local/log_filter/actions.ts deleted file mode 100644 index 32da478c61969..0000000000000 --- a/x-pack/legacy/plugins/infra/public/store/local/log_filter/actions.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import actionCreatorFactory from 'typescript-fsa'; - -import { FilterQuery, SerializedFilterQuery } from './reducer'; - -const actionCreator = actionCreatorFactory('x-pack/infra/local/log_filter'); - -export const setLogFilterQueryDraft = actionCreator('SET_LOG_FILTER_QUERY_DRAFT'); - -export const applyLogFilterQuery = actionCreator('APPLY_LOG_FILTER_QUERY'); diff --git a/x-pack/legacy/plugins/infra/public/store/local/log_filter/reducer.ts b/x-pack/legacy/plugins/infra/public/store/local/log_filter/reducer.ts deleted file mode 100644 index afb77dd9ddc6a..0000000000000 --- a/x-pack/legacy/plugins/infra/public/store/local/log_filter/reducer.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { reducerWithInitialState } from 'typescript-fsa-reducers/dist'; - -import { applyLogFilterQuery, setLogFilterQueryDraft } from './actions'; - -export interface KueryFilterQuery { - kind: 'kuery'; - expression: string; -} - -export type FilterQuery = KueryFilterQuery; - -export interface SerializedFilterQuery { - query: FilterQuery; - serializedQuery: string; -} - -export interface LogFilterState { - filterQuery: SerializedFilterQuery | null; - filterQueryDraft: KueryFilterQuery | null; -} - -export const initialLogFilterState: LogFilterState = { - filterQuery: null, - filterQueryDraft: null, -}; - -export const logFilterReducer = reducerWithInitialState(initialLogFilterState) - .case(setLogFilterQueryDraft, (state, filterQueryDraft) => ({ - ...state, - filterQueryDraft, - })) - .case(applyLogFilterQuery, (state, filterQuery) => ({ - ...state, - filterQuery, - filterQueryDraft: filterQuery.query, - })) - .build(); diff --git a/x-pack/legacy/plugins/infra/public/store/local/log_filter/selectors.ts b/x-pack/legacy/plugins/infra/public/store/local/log_filter/selectors.ts deleted file mode 100644 index f17f7be4defe9..0000000000000 --- a/x-pack/legacy/plugins/infra/public/store/local/log_filter/selectors.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createSelector } from 'reselect'; -import { LogFilterState } from './reducer'; -import { esKuery } from '../../../../../../../../src/plugins/data/public'; - -export const selectLogFilterQuery = (state: LogFilterState) => - state.filterQuery ? state.filterQuery.query : null; - -export const selectLogFilterQueryAsJson = (state: LogFilterState) => - state.filterQuery ? state.filterQuery.serializedQuery : null; - -export const selectLogFilterQueryDraft = (state: LogFilterState) => state.filterQueryDraft; - -export const selectIsLogFilterQueryDraftValid = createSelector( - selectLogFilterQueryDraft, - filterQueryDraft => { - if (filterQueryDraft && filterQueryDraft.kind === 'kuery') { - try { - esKuery.fromKueryExpression(filterQueryDraft.expression); - } catch (err) { - return false; - } - } - - return true; - } -); diff --git a/x-pack/legacy/plugins/infra/public/store/local/reducer.ts b/x-pack/legacy/plugins/infra/public/store/local/reducer.ts index 6308f1bc75427..5cc839af4c7cc 100644 --- a/x-pack/legacy/plugins/infra/public/store/local/reducer.ts +++ b/x-pack/legacy/plugins/infra/public/store/local/reducer.ts @@ -6,7 +6,6 @@ import { combineReducers } from 'redux'; -import { initialLogFilterState, logFilterReducer, LogFilterState } from './log_filter'; import { initialLogPositionState, logPositionReducer, LogPositionState } from './log_position'; import { initialWaffleFilterState, waffleFilterReducer, WaffleFilterState } from './waffle_filter'; import { @@ -17,7 +16,6 @@ import { import { initialWaffleTimeState, waffleTimeReducer, WaffleTimeState } from './waffle_time'; export interface LocalState { - logFilter: LogFilterState; logPosition: LogPositionState; waffleFilter: WaffleFilterState; waffleTime: WaffleTimeState; @@ -25,7 +23,6 @@ export interface LocalState { } export const initialLocalState: LocalState = { - logFilter: initialLogFilterState, logPosition: initialLogPositionState, waffleFilter: initialWaffleFilterState, waffleTime: initialWaffleTimeState, @@ -33,7 +30,6 @@ export const initialLocalState: LocalState = { }; export const localReducer = combineReducers({ - logFilter: logFilterReducer, logPosition: logPositionReducer, waffleFilter: waffleFilterReducer, waffleTime: waffleTimeReducer, diff --git a/x-pack/legacy/plugins/infra/public/store/local/selectors.ts b/x-pack/legacy/plugins/infra/public/store/local/selectors.ts index ef57835496f60..c367901353b3d 100644 --- a/x-pack/legacy/plugins/infra/public/store/local/selectors.ts +++ b/x-pack/legacy/plugins/infra/public/store/local/selectors.ts @@ -5,18 +5,12 @@ */ import { globalizeSelectors } from '../../utils/typed_redux'; -import { logFilterSelectors as innerLogFilterSelectors } from './log_filter'; import { logPositionSelectors as innerLogPositionSelectors } from './log_position'; import { LocalState } from './reducer'; import { waffleFilterSelectors as innerWaffleFilterSelectors } from './waffle_filter'; import { waffleOptionsSelectors as innerWaffleOptionsSelectors } from './waffle_options'; import { waffleTimeSelectors as innerWaffleTimeSelectors } from './waffle_time'; -export const logFilterSelectors = globalizeSelectors( - (state: LocalState) => state.logFilter, - innerLogFilterSelectors -); - export const logPositionSelectors = globalizeSelectors( (state: LocalState) => state.logPosition, innerLogPositionSelectors diff --git a/x-pack/legacy/plugins/infra/public/store/selectors.ts b/x-pack/legacy/plugins/infra/public/store/selectors.ts index aecba1779d036..d98e8ae9a0aae 100644 --- a/x-pack/legacy/plugins/infra/public/store/selectors.ts +++ b/x-pack/legacy/plugins/infra/public/store/selectors.ts @@ -6,7 +6,6 @@ import { globalizeSelectors } from '../utils/typed_redux'; import { - logFilterSelectors as localLogFilterSelectors, logPositionSelectors as localLogPositionSelectors, waffleFilterSelectors as localWaffleFilterSelectors, waffleOptionsSelectors as localWaffleOptionsSelectors, @@ -19,7 +18,6 @@ import { State } from './reducer'; const selectLocal = (state: State) => state.local; -export const logFilterSelectors = globalizeSelectors(selectLocal, localLogFilterSelectors); export const logPositionSelectors = globalizeSelectors(selectLocal, localLogPositionSelectors); export const waffleFilterSelectors = globalizeSelectors(selectLocal, localWaffleFilterSelectors); export const waffleTimeSelectors = globalizeSelectors(selectLocal, localWaffleTimeSelectors); diff --git a/x-pack/legacy/plugins/infra/public/store/store.ts b/x-pack/legacy/plugins/infra/public/store/store.ts index 601db0f56a693..2781b0b2cef3f 100644 --- a/x-pack/legacy/plugins/infra/public/store/store.ts +++ b/x-pack/legacy/plugins/infra/public/store/store.ts @@ -12,7 +12,6 @@ import { map } from 'rxjs/operators'; import { createRootEpic, initialState, - logFilterSelectors, logPositionSelectors, reducer, State, @@ -39,7 +38,6 @@ export function createStore({ apolloClient, observableApi }: StoreDependencies) apolloClient$: apolloClient, selectIsAutoReloadingLogEntries: logPositionSelectors.selectIsAutoReloading, selectIsAutoReloadingScrollLocked: logPositionSelectors.selectAutoReloadScrollLock, - selectLogFilterQueryAsJson: logFilterSelectors.selectLogFilterQueryAsJson, selectLogTargetPosition: logPositionSelectors.selectTargetPosition, selectVisibleLogMidpointOrTarget: logPositionSelectors.selectVisibleMidpointOrTarget, selectWaffleTimeUpdatePolicyInterval: waffleTimeSelectors.selectTimeUpdatePolicyInterval, diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx index 443bcf99a4f09..cb57f2c884e38 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx @@ -152,6 +152,7 @@ export function App({ const isSaveable = lastKnownDoc && + lastKnownDoc.expression && lastKnownDoc.expression.length > 0 && core.application.capabilities.visualize.save; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/expression_wrapper.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/expression_wrapper.tsx index 3dd4373347129..9b85e89cd80eb 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/expression_wrapper.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/embeddable/expression_wrapper.tsx @@ -13,7 +13,7 @@ import { ExpressionRenderer } from 'src/plugins/expressions/public'; export interface ExpressionWrapperProps { ExpressionRenderer: ExpressionRenderer; - expression: string; + expression: string | null; context: { timeRange?: TimeRange; query?: Query; @@ -29,7 +29,7 @@ export function ExpressionWrapper({ }: ExpressionWrapperProps) { return ( - {expression === '' ? ( + {expression === null || expression === '' ? ( diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx index 20505107be122..4a58f0354882a 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx @@ -26,9 +26,6 @@ import { Axis, BarSeries, Chart, - DataSeriesColorsValues, - getAxisId, - getSpecId, niceTimeFormatter, Position, ScaleType, @@ -391,18 +388,11 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { } if (histogram && histogram.buckets.length) { - const specId = getSpecId( - i18n.translate('xpack.lens.indexPattern.fieldStatsCountLabel', { - defaultMessage: 'Count', - }) - ); - const colors: DataSeriesColorsValues = { - colorValues: [], - specId, - }; + const specId = i18n.translate('xpack.lens.indexPattern.fieldStatsCountLabel', { + defaultMessage: 'Count', + }); const expectedColor = getColorForDataType(field.type); - - const seriesColors = new Map([[colors, expectedColor]]); + const seriesColors = expectedColor ? [expectedColor] : undefined; if (field.type === 'date') { return wrapInPopover( @@ -422,7 +412,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { /> formatter.convert(d)} diff --git a/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts b/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts index 4337482663a9c..1d36e18c726ec 100644 --- a/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts +++ b/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts @@ -13,7 +13,7 @@ export interface Document { type?: string; visualizationType: string | null; title: string; - expression: string; + expression: string | null; state: { datasourceMetaData: { filterableIndexPatterns: Array<{ id: string; title: string }>; diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap index 5089a3b8a3a22..8df2d764c0208 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap +++ b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap @@ -4,7 +4,7 @@ exports[`xy_expression XYChart component it renders area 1`] = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - { .find(Axis) .first() .prop('tickFormat'); + + if (!tickFormatter) { + throw new Error('tickFormatter prop not found'); + } + tickFormatter('I'); expect(convertSpy).toHaveBeenCalledWith('I'); diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_expression.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_expression.tsx index 3c113eaca7ed1..32c1ace5b1770 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_expression.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_expression.tsx @@ -12,8 +12,6 @@ import { Settings, Axis, LineSeries, - getAxisId, - getSpecId, AreaSeries, BarSeries, Position, @@ -205,7 +203,7 @@ export function XYChart({ data, args, formatFactory, timeZone }: XYChartRenderPr />
- + {stopInput} {colorInput} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js new file mode 100644 index 0000000000000..5e0f7434b04d0 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import React, { Fragment } from 'react'; +import { FieldSelect } from '../field_select'; +import { ColorRampSelect } from './color_ramp_select'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +export function DynamicColorForm({ + fields, + onDynamicStyleChange, + staticDynamicSelect, + styleProperty, +}) { + const styleOptions = styleProperty.getOptions(); + + const onFieldChange = ({ field }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field }); + }; + + const onColorChange = colorOptions => { + onDynamicStyleChange(styleProperty.getStyleName(), { + ...styleOptions, + ...colorOptions, + }); + }; + + let colorRampSelect; + if (styleOptions.field && styleOptions.field.name) { + colorRampSelect = ( + + ); + } + + return ( + + + {staticDynamicSelect} + + + + + + {colorRampSelect} + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js deleted file mode 100644 index 84327635f2b65..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_selection.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import React, { Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { dynamicColorShape } from '../style_option_shapes'; -import { FieldSelect, fieldShape } from '../field_select'; -import { ColorRampSelect } from './color_ramp_select'; -import { EuiSpacer } from '@elastic/eui'; - -export function DynamicColorSelection({ fields, onChange, styleOptions }) { - const onFieldChange = ({ field }) => { - onChange({ ...styleOptions, field }); - }; - - const onColorChange = colorOptions => { - onChange({ ...styleOptions, ...colorOptions }); - }; - - return ( - - - - - - ); -} - -DynamicColorSelection.propTypes = { - fields: PropTypes.arrayOf(fieldShape).isRequired, - styleOptions: dynamicColorShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js new file mode 100644 index 0000000000000..48befa1ca74c0 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiColorPicker, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function StaticColorForm({ + onStaticStyleChange, + staticDynamicSelect, + styleProperty, + swatches, +}) { + const onColorChange = color => { + onStaticStyleChange(styleProperty.getStyleName(), { color }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_selection.js deleted file mode 100644 index e42b582dc3929..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_selection.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { EuiColorPicker } from '@elastic/eui'; -import { staticColorShape } from '../style_option_shapes'; - -export function StaticColorSelection({ onChange, styleOptions, swatches }) { - const onColorChange = color => { - onChange({ color }); - }; - - return ( - - ); -} - -StaticColorSelection.propTypes = { - styleOptions: staticColorShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js index c7745fa69a82f..43e7050b3d1d2 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js @@ -6,21 +6,29 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; -import { DynamicColorSelection } from './dynamic_color_selection'; -import { StaticColorSelection } from './static_color_selection'; +import { StylePropEditor } from '../style_prop_editor'; +import { DynamicColorForm } from './dynamic_color_form'; +import { StaticColorForm } from './static_color_form'; +import { i18n } from '@kbn/i18n'; export function VectorStyleColorEditor(props) { + const colorForm = props.styleProperty.isDynamic() ? ( + + ) : ( + + ); + return ( - + + {colorForm} + ); } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js new file mode 100644 index 0000000000000..bad13b487cc29 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FieldSelect } from '../field_select'; + +export function DynamicLabelForm({ + fields, + onDynamicStyleChange, + staticDynamicSelect, + styleProperty, +}) { + const styleOptions = styleProperty.getOptions(); + + const onFieldChange = ({ field }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_selector.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_selector.js deleted file mode 100644 index e393341b90696..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_selector.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import React from 'react'; -import { FieldSelect } from '../field_select'; - -export function DynamicLabelSelector({ fields, styleOptions, onChange }) { - const onFieldChange = ({ field }) => { - onChange({ ...styleOptions, field }); - }; - - return ( - - ); -} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js new file mode 100644 index 0000000000000..721487b5d8ff0 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFieldText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function StaticLabelForm({ onStaticStyleChange, staticDynamicSelect, styleProperty }) { + const onValueChange = event => { + onStaticStyleChange(styleProperty.getStyleName(), { value: event.target.value }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_selector.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_selector.js deleted file mode 100644 index ea296a3312799..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_selector.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiFieldText } from '@elastic/eui'; - -export function StaticLabelSelector({ onChange, styleOptions }) { - const onValueChange = event => { - onChange({ value: event.target.value }); - }; - - return ( - - ); -} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js index 6bca56425d38d..aaa21ea315f36 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js @@ -6,16 +6,16 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; -import { DynamicLabelSelector } from './dynamic_label_selector'; -import { StaticLabelSelector } from './static_label_selector'; +import { StylePropEditor } from '../style_prop_editor'; +import { DynamicLabelForm } from './dynamic_label_form'; +import { StaticLabelForm } from './static_label_form'; export function VectorStyleLabelEditor(props) { - return ( - + const labelForm = props.styleProperty.isDynamic() ? ( + + ) : ( + ); + + return {labelForm}; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js new file mode 100644 index 0000000000000..157b863ac4986 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { VectorStyle } from '../../vector_style'; +import { getColorRampCenterColor } from '../../../color_utils'; + +export function extractColorFromStyleProperty(colorStyleProperty, defaultColor) { + if (!colorStyleProperty) { + return defaultColor; + } + + if (colorStyleProperty.type === VectorStyle.STYLE_TYPE.STATIC) { + return colorStyleProperty.options.color; + } + + // Do not use dynamic color unless configuration is complete + if (!colorStyleProperty.options.field || !colorStyleProperty.options.field.name) { + return defaultColor; + } + + // return middle of gradient for dynamic style property + + if (colorStyleProperty.options.useCustomColorRamp) { + if ( + !colorStyleProperty.options.customColorRamp || + !colorStyleProperty.options.customColorRamp.length + ) { + return defaultColor; + } + // favor the lowest color in even arrays + const middleIndex = Math.floor((colorStyleProperty.options.customColorRamp.length - 1) / 2); + return colorStyleProperty.options.customColorRamp[middleIndex].color; + } + + return getColorRampCenterColor(colorStyleProperty.options.color); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js index c9d31b7c22c19..1ce12a5f75bb6 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js @@ -7,13 +7,11 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { dynamicColorShape, staticColorShape } from '../style_option_shapes'; import { CircleIcon } from './circle_icon'; import { LineIcon } from './line_icon'; import { PolygonIcon } from './polygon_icon'; import { SymbolIcon } from './symbol_icon'; -import { VectorStyle } from '../../vector_style'; -import { getColorRampCenterColor } from '../../../color_utils'; +import { VECTOR_STYLES } from '../../vector_style_defaults'; export class VectorIcon extends Component { state = { @@ -48,16 +46,16 @@ export class VectorIcon extends Component { if (this.state.isLinesOnly) { const style = { - stroke: extractColorFromStyleProperty(this.props.lineColor, 'grey'), + stroke: this.props.getColorForProperty(VECTOR_STYLES.LINE_COLOR, true), strokeWidth: '4px', }; return ; } const style = { - stroke: extractColorFromStyleProperty(this.props.lineColor, 'none'), + stroke: this.props.getColorForProperty(VECTOR_STYLES.LINE_COLOR, false), strokeWidth: '1px', - fill: extractColorFromStyleProperty(this.props.fillColor, 'grey'), + fill: this.props.getColorForProperty(VECTOR_STYLES.FILL_COLOR, false), }; if (!this.state.isPointsOnly) { @@ -79,45 +77,8 @@ export class VectorIcon extends Component { } } -function extractColorFromStyleProperty(colorStyleProperty, defaultColor) { - if (!colorStyleProperty) { - return defaultColor; - } - - if (colorStyleProperty.type === VectorStyle.STYLE_TYPE.STATIC) { - return colorStyleProperty.options.color; - } - - // Do not use dynamic color unless configuration is complete - if (!colorStyleProperty.options.field || !colorStyleProperty.options.field.name) { - return defaultColor; - } - - // return middle of gradient for dynamic style property - - if (colorStyleProperty.options.useCustomColorRamp) { - if ( - !colorStyleProperty.options.customColorRamp || - !colorStyleProperty.options.customColorRamp.length - ) { - return defaultColor; - } - // favor the lowest color in even arrays - const middleIndex = Math.floor((colorStyleProperty.options.customColorRamp.length - 1) / 2); - return colorStyleProperty.options.customColorRamp[middleIndex].color; - } - - return getColorRampCenterColor(colorStyleProperty.options.color); -} - -const colorStylePropertyShape = PropTypes.shape({ - type: PropTypes.string.isRequired, - options: PropTypes.oneOfType([dynamicColorShape, staticColorShape]).isRequired, -}); - VectorIcon.propTypes = { - fillColor: colorStylePropertyShape, - lineColor: colorStylePropertyShape, + getColorForProperty: PropTypes.func.isRequired, symbolId: PropTypes.string, loadIsPointsOnly: PropTypes.func.isRequired, loadIsLinesOnly: PropTypes.func.isRequired, diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js index 0341f1f22ca0a..ee0058a6ef1aa 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js @@ -9,16 +9,12 @@ import { shallow } from 'enzyme'; import { VectorIcon } from './vector_icon'; import { VectorStyle } from '../../vector_style'; +import { extractColorFromStyleProperty } from './extract_color_from_style_property'; +import { VECTOR_STYLES } from '../../vector_style_defaults'; let isPointsOnly = false; let isLinesOnly = false; -const defaultProps = { - loadIsPointsOnly: () => { - return isPointsOnly; - }, - loadIsLinesOnly: () => { - return isLinesOnly; - }, +const styles = { fillColor: { type: VectorStyle.STYLE_TYPE.STATIC, options: { @@ -36,6 +32,30 @@ const defaultProps = { }, }; +const defaultProps = { + getColorForProperty: (styleProperty, isLinesOnly) => { + if (isLinesOnly) { + return extractColorFromStyleProperty(styles[VECTOR_STYLES.LINE_COLOR], 'grey'); + } + + if (styleProperty === VECTOR_STYLES.LINE_COLOR) { + return extractColorFromStyleProperty(styles[VECTOR_STYLES.LINE_COLOR], 'none'); + } else if (styleProperty === VECTOR_STYLES.FILL_COLOR) { + return extractColorFromStyleProperty(styles[VECTOR_STYLES.FILL_COLOR], 'grey'); + } else { + //unexpected + console.error('Cannot return color for properties other then line or fill color'); + } + }, + + loadIsPointsOnly: () => { + return isPointsOnly; + }, + loadIsLinesOnly: () => { + return isLinesOnly; + }, +}; + function configureIsLinesOnly() { isLinesOnly = true; isPointsOnly = false; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js index 9e7afb0aa8873..df302c42d48ed 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js @@ -46,7 +46,15 @@ export class VectorStyleLegend extends Component { render() { return this.state.styles.map(style => { - return {style.renderLegendDetailRow()}; + return ( + + {style.renderLegendDetailRow({ + loadIsLinesOnly: this.props.loadIsLinesOnly, + loadIsPointsOnly: this.props.loadIsPointsOnly, + symbolId: this.props.symbolId, + })} + + ); }); } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js new file mode 100644 index 0000000000000..e0b7e7b2865a2 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import React from 'react'; +import { FieldSelect } from '../field_select'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function DynamicOrientationForm({ + fields, + onDynamicStyleChange, + staticDynamicSelect, + styleProperty, +}) { + const styleOptions = styleProperty.getOptions(); + + const onFieldChange = ({ field }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { + ...styleOptions, + field, + }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js deleted file mode 100644 index 8ad3916ac6509..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_selection.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import React from 'react'; -import PropTypes from 'prop-types'; -import { dynamicOrientationShape } from '../style_option_shapes'; -import { FieldSelect, fieldShape } from '../field_select'; - -export function DynamicOrientationSelection({ fields, styleOptions, onChange }) { - const onFieldChange = ({ field }) => { - onChange({ ...styleOptions, field }); - }; - - return ( - - ); -} - -DynamicOrientationSelection.propTypes = { - fields: PropTypes.arrayOf(fieldShape).isRequired, - styleOptions: dynamicOrientationShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js index e97252a5e79da..915fc92c9fb38 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js @@ -6,20 +6,16 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; -import { DynamicOrientationSelection } from './dynamic_orientation_selection'; -import { StaticOrientationSelection } from './static_orientation_selection'; +import { StylePropEditor } from '../style_prop_editor'; +import { DynamicOrientationForm } from './dynamic_orientation_form'; +import { StaticOrientationForm } from './static_orientation_form'; export function OrientationEditor(props) { - return ( - + const orientationForm = props.styleProperty.isDynamic() ? ( + + ) : ( + ); + + return {orientationForm}; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js new file mode 100644 index 0000000000000..8c4418f95e1d2 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ValidatedRange } from '../../../../../components/validated_range'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function StaticOrientationForm({ onStaticStyleChange, staticDynamicSelect, styleProperty }) { + const onOrientationChange = orientation => { + onStaticStyleChange(styleProperty.getStyleName(), { orientation }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_selection.js deleted file mode 100644 index b5529c6987459..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_selection.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { staticOrientationShape } from '../style_option_shapes'; -import { ValidatedRange } from '../../../../../components/validated_range'; - -export function StaticOrientationSelection({ onChange, styleOptions }) { - const onOrientationChange = orientation => { - onChange({ orientation }); - }; - - return ( - - ); -} - -StaticOrientationSelection.propTypes = { - styleOptions: staticOrientationShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js new file mode 100644 index 0000000000000..8b069cd53b731 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import React, { Fragment } from 'react'; +import { FieldSelect } from '../field_select'; +import { SizeRangeSelector } from './size_range_selector'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +export function DynamicSizeForm({ + fields, + onDynamicStyleChange, + staticDynamicSelect, + styleProperty, +}) { + const styleOptions = styleProperty.getOptions(); + + const onFieldChange = ({ field }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field }); + }; + + const onSizeRangeChange = ({ minSize, maxSize }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { + ...styleOptions, + minSize, + maxSize, + }); + }; + + let sizeRange; + if (styleOptions.field && styleOptions.field.name) { + sizeRange = ( + + ); + } + + return ( + + + {staticDynamicSelect} + + + + + + {sizeRange} + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_selection.js deleted file mode 100644 index 76c5b97976bbc..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_selection.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import React, { Fragment } from 'react'; -import PropTypes from 'prop-types'; -import { dynamicSizeShape } from '../style_option_shapes'; -import { FieldSelect, fieldShape } from '../field_select'; -import { SizeRangeSelector } from './size_range_selector'; -import { EuiSpacer } from '@elastic/eui'; - -export function DynamicSizeSelection({ fields, styleOptions, onChange }) { - const onFieldChange = ({ field }) => { - onChange({ ...styleOptions, field }); - }; - - const onSizeRangeChange = ({ minSize, maxSize }) => { - onChange({ ...styleOptions, minSize, maxSize }); - }; - - return ( - - - - - - ); -} - -DynamicSizeSelection.propTypes = { - fields: PropTypes.arrayOf(fieldShape).isRequired, - styleOptions: dynamicSizeShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js new file mode 100644 index 0000000000000..d8fe1322db3e3 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ValidatedRange } from '../../../../../components/validated_range'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +export function StaticSizeForm({ onStaticStyleChange, staticDynamicSelect, styleProperty }) { + const onSizeChange = size => { + onStaticStyleChange(styleProperty.getStyleName(), { size }); + }; + + return ( + + {staticDynamicSelect} + + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_selection.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_selection.js deleted file mode 100644 index 38f8fe53d1748..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_selection.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { staticSizeShape } from '../style_option_shapes'; -import { ValidatedRange } from '../../../../../components/validated_range'; -import { i18n } from '@kbn/i18n'; - -export function StaticSizeSelection({ onChange, styleOptions }) { - const onSizeChange = size => { - onChange({ size }); - }; - - return ( - - ); -} - -StaticSizeSelection.propTypes = { - styleOptions: staticSizeShape.isRequired, - onChange: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js index 6580bfc00e0ad..e344f72bd429a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js @@ -6,20 +6,16 @@ import React from 'react'; -import { StaticDynamicStyleRow } from '../static_dynamic_style_row'; -import { DynamicSizeSelection } from './dynamic_size_selection'; -import { StaticSizeSelection } from './static_size_selection'; +import { StylePropEditor } from '../style_prop_editor'; +import { DynamicSizeForm } from './dynamic_size_form'; +import { StaticSizeForm } from './static_size_form'; export function VectorStyleSizeEditor(props) { - return ( - + const sizeForm = props.styleProperty.isDynamic() ? ( + + ) : ( + ); + + return {sizeForm}; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js deleted file mode 100644 index 625f63a77374d..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/static_dynamic_style_row.js +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Component, Fragment } from 'react'; -import { VectorStyle } from '../vector_style'; -import { i18n } from '@kbn/i18n'; -import { FieldMetaOptionsPopover } from './field_meta_options_popover'; -import { getVectorStyleLabel } from './get_vector_style_label'; - -import { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiFormRow, EuiButtonToggle } from '@elastic/eui'; - -export class StaticDynamicStyleRow extends Component { - // Store previous options locally so when type is toggled, - // previous style options can be used. - prevStaticStyleOptions = this.props.defaultStaticStyleOptions; - prevDynamicStyleOptions = this.props.defaultDynamicStyleOptions; - - _canBeDynamic() { - return this.props.fields.length > 0; - } - - _isDynamic() { - return this.props.styleProperty.isDynamic(); - } - - _getStyleOptions() { - return this.props.styleProperty.getOptions(); - } - - _onFieldMetaOptionsChange = fieldMetaOptions => { - const styleDescriptor = { - type: VectorStyle.STYLE_TYPE.DYNAMIC, - options: { - ...this._getStyleOptions(), - fieldMetaOptions, - }, - }; - this.props.handlePropertyChange(this.props.styleProperty.getStyleName(), styleDescriptor); - }; - - _onStaticStyleChange = options => { - const styleDescriptor = { - type: VectorStyle.STYLE_TYPE.STATIC, - options, - }; - this.props.handlePropertyChange(this.props.styleProperty.getStyleName(), styleDescriptor); - }; - - _onDynamicStyleChange = options => { - const styleDescriptor = { - type: VectorStyle.STYLE_TYPE.DYNAMIC, - options, - }; - this.props.handlePropertyChange(this.props.styleProperty.getStyleName(), styleDescriptor); - }; - - _onTypeToggle = () => { - if (this._isDynamic()) { - // preserve current dynmaic style - this.prevDynamicStyleOptions = this._getStyleOptions(); - // toggle to static style - this._onStaticStyleChange(this.prevStaticStyleOptions); - return; - } - - // preserve current static style - this.prevStaticStyleOptions = this._getStyleOptions(); - // toggle to dynamic style - this._onDynamicStyleChange(this.prevDynamicStyleOptions); - }; - - _renderStyleSelector() { - if (this._isDynamic()) { - const DynamicSelector = this.props.DynamicSelector; - return ( - - - - - ); - } - - const StaticSelector = this.props.StaticSelector; - return ( - - ); - } - - render() { - const isDynamic = this._isDynamic(); - const dynamicTooltipContent = isDynamic - ? i18n.translate('xpack.maps.styles.staticDynamic.staticDescription', { - defaultMessage: 'Use static styling properties to symbolize features.', - }) - : i18n.translate('xpack.maps.styles.staticDynamic.dynamicDescription', { - defaultMessage: 'Use property values to symbolize features.', - }); - - return ( - - - - {this._renderStyleSelector()} - - - {this._canBeDynamic() && ( - - - - - - - - )} - - ); - } -} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js new file mode 100644 index 0000000000000..1ac8edfb2cc69 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Component, Fragment } from 'react'; +import { FieldMetaOptionsPopover } from './field_meta_options_popover'; +import { getVectorStyleLabel } from './get_vector_style_label'; +import { EuiFormRow, EuiSelect } from '@elastic/eui'; +import { VectorStyle } from '../vector_style'; +import { i18n } from '@kbn/i18n'; + +export class StylePropEditor extends Component { + _prevStaticStyleOptions = this.props.defaultStaticStyleOptions; + _prevDynamicStyleOptions = this.props.defaultDynamicStyleOptions; + + _onTypeToggle = () => { + if (this.props.styleProperty.isDynamic()) { + // preserve current dynmaic style + this._prevDynamicStyleOptions = this.props.styleProperty.getOptions(); + // toggle to static style + this.props.onStaticStyleChange( + this.props.styleProperty.getStyleName(), + this._prevStaticStyleOptions + ); + } else { + // preserve current static style + this._prevStaticStyleOptions = this.props.styleProperty.getOptions(); + // toggle to dynamic style + this.props.onDynamicStyleChange( + this.props.styleProperty.getStyleName(), + this._prevDynamicStyleOptions + ); + } + }; + + _onFieldMetaOptionsChange = fieldMetaOptions => { + const options = { + ...this.props.styleProperty.getOptions(), + fieldMetaOptions, + }; + this.props.onDynamicStyleChange(this.props.styleProperty.getStyleName(), options); + }; + + renderStaticDynamicSelect() { + const options = [ + { + value: VectorStyle.STYLE_TYPE.STATIC, + text: this.props.customStaticOptionLabel + ? this.props.customStaticOptionLabel + : i18n.translate('xpack.maps.styles.staticDynamicSelect.staticLabel', { + defaultMessage: 'Fixed', + }), + }, + { + value: VectorStyle.STYLE_TYPE.DYNAMIC, + text: i18n.translate('xpack.maps.styles.staticDynamicSelect.dynamicLabel', { + defaultMessage: 'By value', + }), + }, + ]; + + return ( + + ); + } + + render() { + const fieldMetaOptionsPopover = this.props.styleProperty.isDynamic() ? ( + + ) : null; + + return ( + + + {React.cloneElement(this.props.children, { + staticDynamicSelect: this.renderStaticDynamicSelect(), + })} + {fieldMetaOptionsPopover} + + + ); + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js index 44f630db9d890..8e80e036dbb8b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js @@ -12,8 +12,13 @@ import { VectorStyleColorEditor } from './color/vector_style_color_editor'; import { VectorStyleSizeEditor } from './size/vector_style_size_editor'; import { VectorStyleSymbolEditor } from './vector_style_symbol_editor'; import { VectorStyleLabelEditor } from './label/vector_style_label_editor'; +import { VectorStyle } from '../vector_style'; import { OrientationEditor } from './orientation/orientation_editor'; -import { getDefaultDynamicProperties, getDefaultStaticProperties } from '../vector_style_defaults'; +import { + getDefaultDynamicProperties, + getDefaultStaticProperties, + VECTOR_STYLES, +} from '../vector_style_defaults'; import { DEFAULT_FILL_COLORS, DEFAULT_LINE_COLORS } from '../../color_utils'; import { VECTOR_SHAPE_TYPES } from '../../../sources/vector_feature_types'; import { SYMBOLIZE_AS_ICON } from '../vector_constants'; @@ -128,15 +133,36 @@ export class VectorStyleEditor extends Component { this.props.onIsTimeAwareChange(event.target.checked); }; + _onStaticStyleChange = (propertyName, options) => { + const styleDescriptor = { + type: VectorStyle.STYLE_TYPE.STATIC, + options, + }; + this.props.handlePropertyChange(propertyName, styleDescriptor); + }; + + _onDynamicStyleChange = (propertyName, options) => { + const styleDescriptor = { + type: VectorStyle.STYLE_TYPE.DYNAMIC, + options, + }; + this.props.handlePropertyChange(propertyName, styleDescriptor); + }; + _renderFillColor() { return ( ); } @@ -145,11 +171,16 @@ export class VectorStyleEditor extends Component { return ( ); } @@ -157,11 +188,16 @@ export class VectorStyleEditor extends Component { _renderLineWidth() { return ( ); } @@ -169,11 +205,16 @@ export class VectorStyleEditor extends Component { _renderSymbolSize() { return ( ); } @@ -182,30 +223,45 @@ export class VectorStyleEditor extends Component { return ( @@ -217,11 +273,16 @@ export class VectorStyleEditor extends Component { if (this.props.symbolDescriptor.options.symbolizeAs === SYMBOLIZE_AS_ICON) { iconOrientation = ( ); } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/__snapshots__/dynamic_color_property.test.js.snap b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/__snapshots__/dynamic_color_property.test.js.snap new file mode 100644 index 0000000000000..26e36cb97a791 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/__snapshots__/dynamic_color_property.test.js.snap @@ -0,0 +1,105 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should render categorical legend 1`] = ` +
+ + + + + + + 0_format + + + + + + + + + + + + 10_format + + + + + + + + + + + + + + + foobar_label + + + + + + +
+`; + +exports[`Should render ranged legend 1`] = ` + + } + maxLabel="100_format" + minLabel="0_format" + propertyLabel="Border color" +/> +`; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/categorical_legend.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/categorical_legend.js new file mode 100644 index 0000000000000..8f8ad4d24e715 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/categorical_legend.js @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import _ from 'lodash'; +const EMPTY_VALUE = ''; + +export class CategoricalLegend extends React.Component { + constructor() { + super(); + this._isMounted = false; + this.state = { + label: EMPTY_VALUE, + isPointsOnly: null, + isLinesOnly: null, + }; + } + + async _loadParams() { + const label = await this.props.style.getField().getLabel(); + const isLinesOnly = await this.props.loadIsLinesOnly(); + const isPointsOnly = await this.props.loadIsPointsOnly(); + const newState = { label, isLinesOnly, isPointsOnly }; + if (this._isMounted && !_.isEqual(this.state, newState)) { + this.setState(newState); + } + } + + componentDidUpdate() { + this._loadParams(); + } + + componentWillUnmount() { + this._isMounted = false; + } + + componentDidMount() { + this._isMounted = true; + this._loadParams(); + } + + render() { + if (this.state.label === EMPTY_VALUE) { + return null; + } + return this.props.style.renderBreakedLegend({ + fieldLabel: this.state.label, + isLinesOnly: this.state.isLinesOnly, + isPointsOnly: this.state.isPointsOnly, + symbolId: this.props.symbolId, + }); + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/dynamic_legend_row.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/ordinal_legend.js similarity index 87% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/dynamic_legend_row.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/ordinal_legend.js index 5933bd1575b2a..564bae3ef3f72 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/dynamic_legend_row.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/ordinal_legend.js @@ -7,11 +7,9 @@ import React from 'react'; import _ from 'lodash'; import { RangedStyleLegendRow } from '../../../components/ranged_style_legend_row'; -import { getVectorStyleLabel } from '../../components/get_vector_style_label'; - const EMPTY_VALUE = ''; -export class DynamicLegendRow extends React.Component { +export class OrdinalLegend extends React.Component { constructor() { super(); this._isMounted = false; @@ -28,6 +26,13 @@ export class DynamicLegendRow extends React.Component { } } + _formatValue(value) { + if (value === EMPTY_VALUE) { + return value; + } + return this.props.style.formatField(value); + } + componentDidUpdate() { this._loadParams(); } @@ -40,14 +45,6 @@ export class DynamicLegendRow extends React.Component { this._isMounted = true; this._loadParams(); } - - _formatValue(value) { - if (value === EMPTY_VALUE) { - return value; - } - return this.props.style.formatField(value); - } - render() { const fieldMeta = this.props.style.getFieldMeta(); @@ -70,10 +67,10 @@ export class DynamicLegendRow extends React.Component { return ( ); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js index 3961325c3bd59..200df9e5cc33d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js @@ -10,6 +10,9 @@ import { getComputedFieldName } from '../style_util'; import { getColorRampStops } from '../../color_utils'; import { ColorGradient } from '../../components/color_gradient'; import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiToolTip } from '@elastic/eui'; +import { VectorIcon } from '../components/legend/vector_icon'; +import { VECTOR_STYLES } from '../vector_style_defaults'; export class DynamicColorProperty extends DynamicStyleProperty { syncCircleColorWithMb(mbLayerId, mbMap, alpha) { @@ -64,6 +67,14 @@ export class DynamicColorProperty extends DynamicStyleProperty { return !this.isCustomColorRamp(); } + isRanged() { + return !this.isCustomColorRamp(); + } + + hasBreaks() { + return this.isCustomColorRamp(); + } + _getMbColor() { const isDynamicConfigComplete = _.has(this._options, 'field.name') && _.has(this._options, 'color'); @@ -117,11 +128,95 @@ export class DynamicColorProperty extends DynamicStyleProperty { return getColorRampStops(this._options.color); } - renderLegendHeader() { + renderRangeLegendHeader() { if (this._options.color) { return ; } else { return null; } } + + _renderStopIcon(color, isLinesOnly, isPointsOnly, symbolId) { + if (this.getStyleName() === VECTOR_STYLES.LABEL_COLOR) { + const style = { color: color }; + return ( + + Tx + + ); + } + + const loadIsLinesOnly = () => { + return isLinesOnly; + }; + + const loadIsPointsOnly = () => { + return isPointsOnly; + }; + + const getColorForProperty = (styleProperty, isLinesOnly) => { + if (isLinesOnly) { + return color; + } + + return this.getStyleName() === styleProperty ? color : 'none'; + }; + + return ( + + ); + } + + _renderColorbreaks({ isLinesOnly, isPointsOnly, symbolId }) { + if (!this._options.customColorRamp) { + return null; + } + + return this._options.customColorRamp.map((config, index) => { + const value = this.formatField(config.stop); + return ( + + + + {value} + + + {this._renderStopIcon(config.color, isLinesOnly, isPointsOnly, symbolId)} + + + + ); + }); + } + + renderBreakedLegend({ fieldLabel, isPointsOnly, isLinesOnly, symbolId }) { + return ( +
+ + + {this._renderColorbreaks({ + isPointsOnly, + isLinesOnly, + symbolId, + })} + + + + + + + {fieldLabel} + + + + + +
+ ); + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js new file mode 100644 index 0000000000000..dbf704c9cbe4c --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// eslint-disable-next-line no-unused-vars +import React from 'react'; +import { shallow } from 'enzyme'; + +import { VECTOR_STYLES } from '../vector_style_defaults'; +import { DynamicColorProperty } from './dynamic_color_property'; + +const mockField = { + async getLabel() { + return 'foobar_label'; + }, + + getName() { + return 'foobar'; + }, + supportsFieldMeta() { + return true; + }, +}; + +test('Should render ranged legend', async () => { + const colorStyle = new DynamicColorProperty( + { + color: 'Blues', + }, + VECTOR_STYLES.LINE_COLOR, + mockField, + () => { + return { min: 0, max: 100 }; + }, + () => { + return x => x + '_format'; + } + ); + + const legendRow = colorStyle.renderLegendDetailRow({ + loadIsPointsOnly: () => { + return true; + }, + loadIsLinesOnly: () => { + return false; + }, + }); + + const component = shallow(legendRow); + + // Ensure all promises resolve + await new Promise(resolve => process.nextTick(resolve)); + // Ensure the state changes are reflected + component.update(); + + expect(component).toMatchSnapshot(); +}); + +test('Should render categorical legend', async () => { + const colorStyle = new DynamicColorProperty( + { + useCustomColorRamp: true, + customColorRamp: [ + { + stop: 0, + color: '#FF0000', + }, + { + stop: 10, + color: '#00FF00', + }, + ], + }, + VECTOR_STYLES.LINE_COLOR, + mockField, + () => { + return { min: 0, max: 100 }; + }, + () => { + return x => x + '_format'; + } + ); + + const legendRow = colorStyle.renderLegendDetailRow({ + loadIsPointsOnly: () => { + return true; + }, + loadIsLinesOnly: () => { + return false; + }, + }); + + const component = shallow(legendRow); + + // Ensure all promises resolve + await new Promise(resolve => process.nextTick(resolve)); + // Ensure the state changes are reflected + component.update(); + + expect(component).toMatchSnapshot(); +}); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js index 05e235f6926a6..f2e5672226814 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js @@ -146,7 +146,7 @@ export class DynamicSizeProperty extends DynamicStyleProperty { ); } - renderLegendHeader() { + renderRangeLegendHeader() { let icons; if (this.getStyleName() === VECTOR_STYLES.LINE_WIDTH) { icons = getLineWidthIcons(); @@ -157,7 +157,7 @@ export class DynamicSizeProperty extends DynamicStyleProperty { } return ( - + {icons.map((icon, index) => { const isLast = index === icons.length - 1; let spacer; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js index e6448f365b44a..bac3c96581967 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -8,9 +8,10 @@ import _ from 'lodash'; import { AbstractStyleProperty } from './style_property'; import { DEFAULT_SIGMA } from '../vector_style_defaults'; import { STYLE_TYPE } from '../../../../../common/constants'; -import { DynamicLegendRow } from './components/dynamic_legend_row'; import { scaleValue } from '../style_util'; import React from 'react'; +import { OrdinalLegend } from './components/ordinal_legend'; +import { CategoricalLegend } from './components/categorical_legend'; export class DynamicStyleProperty extends AbstractStyleProperty { static type = STYLE_TYPE.DYNAMIC; @@ -38,6 +39,14 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return true; } + hasBreaks() { + return false; + } + + isRanged() { + return true; + } + isComplete() { return !!this._field; } @@ -75,6 +84,10 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } pluckStyleMetaFromFeatures(features) { + if (!this.isOrdinal()) { + return null; + } + const name = this.getField().getName(); let min = Infinity; let max = -Infinity; @@ -97,6 +110,10 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } pluckStyleMetaFromFieldMetaData(fieldMetaData) { + if (!this.isOrdinal()) { + return null; + } + const realFieldName = this._field.getESDocFieldName ? this._field.getESDocFieldName() : this._field.getName(); @@ -120,13 +137,13 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } formatField(value) { - if (!this.getField()) { + if (this.getField()) { + const fieldName = this.getField().getName(); + const fieldFormatter = this._getFieldFormatter(fieldName); + return fieldFormatter ? fieldFormatter(value) : value; + } else { return value; } - - const fieldName = this.getField().getName(); - const fieldFormatter = this._getFieldFormatter(fieldName); - return fieldFormatter ? fieldFormatter(value) : value; } getMbValue(value) { @@ -144,7 +161,32 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return valueAsFloat; } - renderLegendDetailRow() { - return ; + renderBreakedLegend() { + return null; + } + + _renderCategoricalLegend({ loadIsPointsOnly, loadIsLinesOnly, symbolId }) { + return ( + + ); + } + + _renderRangeLegend() { + return ; + } + + renderLegendDetailRow({ loadIsPointsOnly, loadIsLinesOnly, symbolId }) { + if (this.isRanged()) { + return this._renderRangeLegend(); + } else if (this.hasBreaks()) { + return this._renderCategoricalLegend({ loadIsPointsOnly, loadIsLinesOnly, symbolId }); + } else { + return null; + } } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js index b716030d2f263..fbc4c3af78f98 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js @@ -32,8 +32,4 @@ export class DynamicTextProperty extends DynamicStyleProperty { isScaled() { return false; } - - renderHeader() { - return null; - } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js index 8da76a775229e..52e1a46a18e94 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.js @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { getVectorStyleLabel } from '../components/get_vector_style_label'; export class AbstractStyleProperty { constructor(options, styleName) { this._options = options; @@ -36,11 +37,15 @@ export class AbstractStyleProperty { return this._options || {}; } - renderLegendHeader() { + renderRangeLegendHeader() { return null; } renderLegendDetailRow() { return null; } + + getDisplayStyleName() { + return getVectorStyleLabel(this.getStyleName()); + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index ae434f5002800..ea80b188e1646 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -38,6 +38,7 @@ import { StaticOrientationProperty } from './properties/static_orientation_prope import { DynamicOrientationProperty } from './properties/dynamic_orientation_property'; import { StaticTextProperty } from './properties/static_text_property'; import { DynamicTextProperty } from './properties/dynamic_text_property'; +import { extractColorFromStyleProperty } from './components/legend/extract_color_from_style_property'; const POINTS = [GEO_JSON_TYPE.POINT, GEO_JSON_TYPE.MULTI_POINT]; const LINES = [GEO_JSON_TYPE.LINE_STRING, GEO_JSON_TYPE.MULTI_LINE_STRING]; @@ -386,18 +387,37 @@ export class VectorStyle extends AbstractStyle { return _.get(this._descriptor, '__styleMeta', {}); }; - getIcon = () => { - const styles = this.getRawProperties(); - const symbolId = this.arePointsSymbolizedAsCircles() + _getSymbolId() { + return this.arePointsSymbolizedAsCircles() ? undefined : this._descriptor.properties.symbol.options.symbolId; + } + + _getColorForProperty = (styleProperty, isLinesOnly) => { + const styles = this.getRawProperties(); + if (isLinesOnly) { + return extractColorFromStyleProperty(styles[VECTOR_STYLES.LINE_COLOR], 'grey'); + } + + if (styleProperty === VECTOR_STYLES.LINE_COLOR) { + return extractColorFromStyleProperty(styles[VECTOR_STYLES.LINE_COLOR], 'none'); + } else if (styleProperty === VECTOR_STYLES.FILL_COLOR) { + return extractColorFromStyleProperty(styles[VECTOR_STYLES.FILL_COLOR], 'grey'); + } else { + //unexpected + console.error('Cannot return color for properties other then line or fill color'); + } + }; + + getIcon = () => { + const symbolId = this._getSymbolId(); + return ( ); }; @@ -431,7 +451,12 @@ export class VectorStyle extends AbstractStyle { renderLegendDetails() { return ( - + ); } diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx index dfe9eb3fe17b8..9da1235a6becd 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx @@ -12,8 +12,6 @@ import { Axis, BarSeries, Chart, - getAxisId, - getSpecId, niceTimeFormatter, Position, ScaleType, @@ -76,14 +74,14 @@ export const DocumentCountChart: FC = ({ }} /> - + = ({ width, height, chartData, f tooltip={{ headerFormatter }} /> kibanaFieldFormat(d, fieldFormat)} /> - d.toFixed(3)} - hide={true} - /> + d.toFixed(3)} hide={true} /> = ({ anomalyData }) => { return ( = ({ chartData }) => { return ( = ({ chartData }) => { yScaleToDataExtent={false} curve={CurveType.CURVE_MONOTONE_X} lineSeriesStyle={lineSeriesStyle} - customSeriesColors={getCustomColor(SPEC_ID, LINE_COLOR)} + customSeriesColors={[LINE_COLOR]} /> ); }; diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/anomaly_chart/model_bounds.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/anomaly_chart/model_bounds.tsx index 0d76b50b80b97..588bb58135643 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/anomaly_chart/model_bounds.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/anomaly_chart/model_bounds.tsx @@ -5,9 +5,8 @@ */ import React, { FC } from 'react'; -import { getSpecId, ScaleType, AreaSeries, CurveType } from '@elastic/charts'; +import { ScaleType, AreaSeries, CurveType } from '@elastic/charts'; import { ModelItem } from '../../../../common/results_loader'; -import { getCustomColor } from '../common/utils'; import { seriesStyle, MODEL_COLOR } from '../common/settings'; interface Props { @@ -33,7 +32,7 @@ export const ModelBounds: FC = ({ modelData }) => { const model = modelData === undefined ? [] : modelData; return ( = ({ modelData }) => { yScaleToDataExtent={false} curve={CurveType.CURVE_MONOTONE_X} areaSeriesStyle={areaSeriesStyle} - customSeriesColors={getCustomColor(SPEC_ID, MODEL_COLOR)} + customSeriesColors={[MODEL_COLOR]} /> ); }; diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/anomaly_chart/scatter.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/anomaly_chart/scatter.tsx index 3a8fb9dbfb4d7..30148be62b835 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/anomaly_chart/scatter.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/anomaly_chart/scatter.tsx @@ -5,8 +5,7 @@ */ import React, { FC } from 'react'; -import { LineSeries, getSpecId, ScaleType, CurveType } from '@elastic/charts'; -import { getCustomColor } from '../common/utils'; +import { LineSeries, ScaleType, CurveType } from '@elastic/charts'; import { seriesStyle, LINE_COLOR } from '../common/settings'; interface Props { @@ -30,7 +29,7 @@ const scatterSeriesStyle = { export const Scatter: FC = ({ chartData }) => { return ( = ({ chartData }) => { yScaleToDataExtent={false} curve={CurveType.CURVE_MONOTONE_X} lineSeriesStyle={scatterSeriesStyle} - customSeriesColors={getCustomColor(SPEC_ID, LINE_COLOR)} + customSeriesColors={[LINE_COLOR]} /> ); }; diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/axes.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/axes.tsx index d068609760da8..02e62a0dfcc96 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/axes.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/axes.tsx @@ -5,7 +5,7 @@ */ import React, { FC, Fragment } from 'react'; -import { Axis, getAxisId, Position, timeFormatter, niceTimeFormatByDay } from '@elastic/charts'; +import { Axis, Position, timeFormatter, niceTimeFormatByDay } from '@elastic/charts'; import { getYRange } from './utils'; import { LineChartPoint } from '../../../../common/chart_loader'; @@ -26,17 +26,12 @@ export const Axes: FC = ({ chartData }) => { return ( - + ); }; diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/utils.ts b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/utils.ts index 91f4c08c72f7a..4103c1520b5b1 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/utils.ts +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/utils.ts @@ -3,17 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { getSpecId, CustomSeriesColorsMap, DataSeriesColorsValues } from '@elastic/charts'; - -export function getCustomColor(specId: string, color: string): CustomSeriesColorsMap { - const lineDataSeriesColorValues: DataSeriesColorsValues = { - colorValues: [], - specId: getSpecId(specId), - }; - return new Map([[lineDataSeriesColorValues, color]]); -} - export function getYRange(chartData: any[]) { if (chartData.length === 0) { return { min: 0, max: 0 }; diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx index 7130a24cffb7d..5423e80a82f15 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx @@ -5,9 +5,8 @@ */ import React, { FC } from 'react'; -import { BarSeries, Chart, getSpecId, ScaleType, Settings, TooltipType } from '@elastic/charts'; +import { BarSeries, Chart, ScaleType, Settings, TooltipType } from '@elastic/charts'; import { Axes } from '../common/axes'; -import { getCustomColor } from '../common/utils'; import { LineChartPoint } from '../../../../common/chart_loader'; import { EVENT_RATE_COLOR } from '../common/settings'; import { LoadingWrapper } from '../loading_wrapper'; @@ -20,8 +19,6 @@ interface Props { loading?: boolean; } -const SPEC_ID = 'event_rate'; - export const EventRateChart: FC = ({ eventRateChartData, height, @@ -40,13 +37,13 @@ export const EventRateChart: FC = ({ diff --git a/x-pack/legacy/plugins/security/index.js b/x-pack/legacy/plugins/security/index.js index 6ee8b5f8b2b10..bc403b803b8df 100644 --- a/x-pack/legacy/plugins/security/index.js +++ b/x-pack/legacy/plugins/security/index.js @@ -28,17 +28,10 @@ export const security = kibana => enabled: Joi.boolean().default(true), cookieName: HANDLED_IN_NEW_PLATFORM, encryptionKey: HANDLED_IN_NEW_PLATFORM, - session: Joi.object({ - idleTimeout: HANDLED_IN_NEW_PLATFORM, - lifespan: HANDLED_IN_NEW_PLATFORM, - }).default(), + session: HANDLED_IN_NEW_PLATFORM, secureCookies: HANDLED_IN_NEW_PLATFORM, loginAssistanceMessage: HANDLED_IN_NEW_PLATFORM, - authorization: Joi.object({ - legacyFallback: Joi.object({ - enabled: Joi.boolean().default(true), // deprecated - }).default(), - }).default(), + authorization: HANDLED_IN_NEW_PLATFORM, audit: Joi.object({ enabled: Joi.boolean().default(false), }).default(), @@ -46,13 +39,6 @@ export const security = kibana => }).default(); }, - deprecations: function({ rename, unused }) { - return [ - unused('authorization.legacyFallback.enabled'), - rename('sessionTimeout', 'session.idleTimeout'), - ]; - }, - uiExports: { chromeNavControls: [], managementSections: ['plugins/security/views/management'], diff --git a/x-pack/legacy/plugins/security/public/lib/api.ts b/x-pack/legacy/plugins/security/public/lib/api.ts index ffa08ca44f376..c5c6994bf4be3 100644 --- a/x-pack/legacy/plugins/security/public/lib/api.ts +++ b/x-pack/legacy/plugins/security/public/lib/api.ts @@ -5,16 +5,12 @@ */ import { kfetch } from 'ui/kfetch'; -import { AuthenticatedUser, Role, User, EditUser } from '../../common/model'; +import { Role, User, EditUser } from '../../common/model'; const usersUrl = '/internal/security/users'; const rolesUrl = '/api/security/role'; export class UserAPIClient { - public async getCurrentUser(): Promise { - return await kfetch({ pathname: `/internal/security/me` }); - } - public async getUsers(): Promise { return await kfetch({ pathname: usersUrl }); } diff --git a/x-pack/legacy/plugins/security/public/services/shield_user.js b/x-pack/legacy/plugins/security/public/services/shield_user.js deleted file mode 100644 index 14a79f267ca75..0000000000000 --- a/x-pack/legacy/plugins/security/public/services/shield_user.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import 'angular-resource'; -import angular from 'angular'; -import { uiModules } from 'ui/modules'; - -const module = uiModules.get('security', ['ngResource']); -module.service('ShieldUser', ($resource, chrome) => { - const baseUrl = chrome.addBasePath('/internal/security/users/:username'); - const ShieldUser = $resource( - baseUrl, - { - username: '@username', - }, - { - changePassword: { - method: 'POST', - url: `${baseUrl}/password`, - transformRequest: ({ password, newPassword }) => angular.toJson({ password, newPassword }), - }, - getCurrent: { - method: 'GET', - url: chrome.addBasePath('/internal/security/me'), - }, - } - ); - - return ShieldUser; -}); diff --git a/x-pack/legacy/plugins/security/public/views/account/account.js b/x-pack/legacy/plugins/security/public/views/account/account.js index db971bd97eab7..70a7b8dce727e 100644 --- a/x-pack/legacy/plugins/security/public/views/account/account.js +++ b/x-pack/legacy/plugins/security/public/views/account/account.js @@ -6,22 +6,13 @@ import routes from 'ui/routes'; import template from './account.html'; -import '../../services/shield_user'; import { i18n } from '@kbn/i18n'; import { I18nContext } from 'ui/i18n'; +import { npSetup } from 'ui/new_platform'; import { AccountManagementPage } from './components'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -const renderReact = (elem, user) => { - render( - - - , - elem - ); -}; - routes.when('/account', { template, k7Breadcrumbs: () => [ @@ -31,13 +22,8 @@ routes.when('/account', { }), }, ], - resolve: { - user(ShieldUser) { - return ShieldUser.getCurrent().$promise; - }, - }, controllerAs: 'accountController', - controller($scope, $route) { + controller($scope) { $scope.$on('$destroy', () => { const elem = document.getElementById('userProfileReactRoot'); if (elem) { @@ -45,8 +31,12 @@ routes.when('/account', { } }); $scope.$$postDigest(() => { - const elem = document.getElementById('userProfileReactRoot'); - renderReact(elem, $route.current.locals.user); + render( + + + , + document.getElementById('userProfileReactRoot') + ); }); }, }); diff --git a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.test.tsx b/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.test.tsx index 176b05f455439..366842e58e9e4 100644 --- a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.test.tsx +++ b/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.test.tsx @@ -4,8 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { act } from '@testing-library/react'; +import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; +import { securityMock } from '../../../../../../../plugins/security/public/mocks'; import { AccountManagementPage } from './account_management_page'; +import { AuthenticatedUser } from '../../../../common/model'; jest.mock('ui/kfetch'); @@ -32,10 +35,24 @@ const createUser = ({ withFullName = true, withEmail = true, realm = 'native' }: }; }; +function getSecuritySetupMock({ currentUser }: { currentUser: AuthenticatedUser }) { + const securitySetupMock = securityMock.createSetup(); + securitySetupMock.authc.getCurrentUser.mockResolvedValue(currentUser); + return securitySetupMock; +} + describe('', () => { - it(`displays users full name, username, and email address`, () => { + it(`displays users full name, username, and email address`, async () => { const user = createUser(); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + expect(wrapper.find('EuiText[data-test-subj="userDisplayName"]').text()).toEqual( user.full_name ); @@ -43,28 +60,60 @@ describe('', () => { expect(wrapper.find('[data-test-subj="email"]').text()).toEqual(user.email); }); - it(`displays username when full_name is not provided`, () => { + it(`displays username when full_name is not provided`, async () => { const user = createUser({ withFullName: false }); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + expect(wrapper.find('EuiText[data-test-subj="userDisplayName"]').text()).toEqual(user.username); }); - it(`displays a placeholder when no email address is provided`, () => { + it(`displays a placeholder when no email address is provided`, async () => { const user = createUser({ withEmail: false }); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + expect(wrapper.find('[data-test-subj="email"]').text()).toEqual('no email address'); }); - it(`displays change password form for users in the native realm`, () => { + it(`displays change password form for users in the native realm`, async () => { const user = createUser(); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + expect(wrapper.find('EuiFieldText[data-test-subj="currentPassword"]')).toHaveLength(1); expect(wrapper.find('EuiFieldText[data-test-subj="newPassword"]')).toHaveLength(1); }); - it(`does not display change password form for users in the saml realm`, () => { + it(`does not display change password form for users in the saml realm`, async () => { const user = createUser({ realm: 'saml' }); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + expect(wrapper.find('EuiFieldText[data-test-subj="currentPassword"]')).toHaveLength(0); expect(wrapper.find('EuiFieldText[data-test-subj="newPassword"]')).toHaveLength(0); }); diff --git a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx b/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx index 2ed057ad73a12..6abee73e0b353 100644 --- a/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx +++ b/x-pack/legacy/plugins/security/public/views/account/components/account_management_page.tsx @@ -4,29 +4,41 @@ * you may not use this file except in compliance with the Elastic License. */ import { EuiPage, EuiPageBody, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import { SecurityPluginSetup } from '../../../../../../../plugins/security/public'; import { getUserDisplayName, AuthenticatedUser } from '../../../../common/model'; import { ChangePassword } from './change_password'; import { PersonalInfo } from './personal_info'; interface Props { - user: AuthenticatedUser; + securitySetup: SecurityPluginSetup; } -export const AccountManagementPage: React.FC = props => ( - - - - -

{getUserDisplayName(props.user)}

-
+export const AccountManagementPage = (props: Props) => { + const [currentUser, setCurrentUser] = useState(null); + useEffect(() => { + props.securitySetup.authc.getCurrentUser().then(setCurrentUser); + }, [props]); - + if (!currentUser) { + return null; + } - + return ( + + + + +

{getUserDisplayName(currentUser)}

+
- -
-
-
-); + + + + + +
+
+
+ ); +}; diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js b/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js index 09c612526918f..27c9beb4ba828 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js @@ -11,10 +11,10 @@ import { kfetch } from 'ui/kfetch'; import { fatalError, toastNotifications } from 'ui/notify'; import { npStart } from 'ui/new_platform'; import template from 'plugins/security/views/management/edit_role/edit_role.html'; -import 'plugins/security/services/shield_user'; import 'plugins/security/services/shield_role'; import 'plugins/security/services/shield_indices'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; +import { UserAPIClient } from '../../../lib/api'; import { ROLES_PATH, CLONE_ROLES_PATH, EDIT_ROLES_PATH } from '../management_urls'; import { getEditRoleBreadcrumbs, getCreateRoleBreadcrumbs } from '../breadcrumbs'; @@ -69,9 +69,8 @@ const routeDefinition = action => ({ return role.then(res => res.toJSON()); }, - users(ShieldUser) { - // $promise is used here because the result is an ngResource, not a promise itself - return ShieldUser.query().$promise.then(users => _.map(users, 'username')); + users() { + return new UserAPIClient().getUsers().then(users => _.map(users, 'username')); }, indexPatterns() { return npStart.plugins.data.indexPatterns.getTitles(); diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.test.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.test.tsx index 5c71d0da3954a..639646ce48e22 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.test.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.test.tsx @@ -4,38 +4,42 @@ * you may not use this file except in compliance with the Elastic License. */ -import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { act } from '@testing-library/react'; +import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { EditUserPage } from './edit_user_page'; import React from 'react'; +import { securityMock } from '../../../../../../../../plugins/security/public/mocks'; import { UserAPIClient } from '../../../../lib/api'; import { User, Role } from '../../../../../common/model'; import { ReactWrapper } from 'enzyme'; +import { mockAuthenticatedUser } from '../../../../../../../../plugins/security/common/model/authenticated_user.mock'; jest.mock('ui/kfetch'); -const buildClient = () => { - const apiClient = new UserAPIClient(); +const createUser = (username: string) => { + const user: User = { + username, + full_name: 'my full name', + email: 'foo@bar.com', + roles: ['idk', 'something'], + enabled: true, + }; - const createUser = (username: string) => { - const user: User = { - username, - full_name: 'my full name', - email: 'foo@bar.com', - roles: ['idk', 'something'], - enabled: true, + if (username === 'reserved_user') { + user.metadata = { + _reserved: true, }; + } - if (username === 'reserved_user') { - user.metadata = { - _reserved: true, - }; - } + return user; +}; - return Promise.resolve(user); - }; +const buildClient = () => { + const apiClient = new UserAPIClient(); - apiClient.getUser = jest.fn().mockImplementation(createUser); - apiClient.getCurrentUser = jest.fn().mockImplementation(() => createUser('current_user')); + apiClient.getUser = jest + .fn() + .mockImplementation(async (username: string) => createUser(username)); apiClient.getRoles = jest.fn().mockImplementation(() => { return Promise.resolve([ @@ -63,6 +67,14 @@ const buildClient = () => { return apiClient; }; +function buildSecuritySetup() { + const securitySetupMock = securityMock.createSetup(); + securitySetupMock.authc.getCurrentUser.mockResolvedValue( + mockAuthenticatedUser(createUser('current_user')) + ); + return securitySetupMock; +} + function expectSaveButton(wrapper: ReactWrapper) { expect(wrapper.find('EuiButton[data-test-subj="userFormSaveButton"]')).toHaveLength(1); } @@ -74,10 +86,12 @@ function expectMissingSaveButton(wrapper: ReactWrapper) { describe('EditUserPage', () => { it('allows reserved users to be viewed', async () => { const apiClient = buildClient(); + const securitySetup = buildSecuritySetup(); const wrapper = mountWithIntl( path} intl={null as any} /> @@ -86,17 +100,19 @@ describe('EditUserPage', () => { await waitForRender(wrapper); expect(apiClient.getUser).toBeCalledTimes(1); - expect(apiClient.getCurrentUser).toBeCalledTimes(1); + expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1); expectMissingSaveButton(wrapper); }); it('allows new users to be created', async () => { const apiClient = buildClient(); + const securitySetup = buildSecuritySetup(); const wrapper = mountWithIntl( path} intl={null as any} /> @@ -105,17 +121,19 @@ describe('EditUserPage', () => { await waitForRender(wrapper); expect(apiClient.getUser).toBeCalledTimes(0); - expect(apiClient.getCurrentUser).toBeCalledTimes(0); + expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(0); expectSaveButton(wrapper); }); it('allows existing users to be edited', async () => { const apiClient = buildClient(); + const securitySetup = buildSecuritySetup(); const wrapper = mountWithIntl( path} intl={null as any} /> @@ -124,16 +142,15 @@ describe('EditUserPage', () => { await waitForRender(wrapper); expect(apiClient.getUser).toBeCalledTimes(1); - expect(apiClient.getCurrentUser).toBeCalledTimes(1); + expect(securitySetup.authc.getCurrentUser).toBeCalledTimes(1); expectSaveButton(wrapper); }); }); async function waitForRender(wrapper: ReactWrapper) { - await Promise.resolve(); - await Promise.resolve(); - await Promise.resolve(); - await Promise.resolve(); - wrapper.update(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); } diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.tsx index 91f5f048adc6d..bbffe07485f8d 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_user/components/edit_user_page.tsx @@ -28,6 +28,7 @@ import { } from '@elastic/eui'; import { toastNotifications } from 'ui/notify'; import { FormattedMessage, injectI18n, InjectedIntl } from '@kbn/i18n/react'; +import { SecurityPluginSetup } from '../../../../../../../../plugins/security/public'; import { UserValidator, UserValidationResult } from '../../../../lib/validate_user'; import { User, EditUser, Role } from '../../../../../common/model'; import { USERS_PATH } from '../../../../views/management/management_urls'; @@ -40,6 +41,7 @@ interface Props { intl: InjectedIntl; changeUrl: (path: string) => void; apiClient: UserAPIClient; + securitySetup: SecurityPluginSetup; } interface State { @@ -82,7 +84,7 @@ class EditUserPageUI extends Component { } public async componentDidMount() { - const { username, apiClient } = this.props; + const { username, apiClient, securitySetup } = this.props; let { user, currentUser } = this.state; if (username) { try { @@ -91,7 +93,7 @@ class EditUserPageUI extends Component { password: '', confirmPassword: '', }; - currentUser = await apiClient.getCurrentUser(); + currentUser = await securitySetup.authc.getCurrentUser(); } catch (err) { toastNotifications.addDanger({ title: this.props.intl.formatMessage({ diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.js b/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.js index bd9d6f2b1ca35..ab218022c6ee6 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.js +++ b/x-pack/legacy/plugins/security/public/views/management/edit_user/edit_user.js @@ -7,7 +7,6 @@ import routes from 'ui/routes'; import template from 'plugins/security/views/management/edit_user/edit_user.html'; import 'angular-resource'; import 'ui/angular_ui_select'; -import 'plugins/security/services/shield_user'; import 'plugins/security/services/shield_role'; import { EDIT_USERS_PATH } from '../management_urls'; import { EditUserPage } from './components'; @@ -15,12 +14,18 @@ import { UserAPIClient } from '../../../lib/api'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nContext } from 'ui/i18n'; +import { npSetup } from 'ui/new_platform'; import { getEditUserBreadcrumbs, getCreateUserBreadcrumbs } from '../breadcrumbs'; const renderReact = (elem, changeUrl, username) => { render( - + , elem ); diff --git a/x-pack/legacy/plugins/security/public/views/management/management.js b/x-pack/legacy/plugins/security/public/views/management/management.js index db2175e91c5de..59da63abbb83f 100644 --- a/x-pack/legacy/plugins/security/public/views/management/management.js +++ b/x-pack/legacy/plugins/security/public/views/management/management.js @@ -13,10 +13,10 @@ import 'plugins/security/views/management/edit_user/edit_user'; import 'plugins/security/views/management/edit_role/index'; import routes from 'ui/routes'; import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import '../../services/shield_user'; import { ROLES_PATH, USERS_PATH, API_KEYS_PATH } from './management_urls'; import { management } from 'ui/management'; +import { npSetup } from 'ui/new_platform'; import { i18n } from '@kbn/i18n'; import { toastNotifications } from 'ui/notify'; @@ -36,7 +36,7 @@ routes }) .defaults(/\/management/, { resolve: { - securityManagementSection: function(ShieldUser) { + securityManagementSection: function() { const showSecurityLinks = xpackInfo.get('features.security.showLinks'); function deregisterSecurity() { @@ -93,12 +93,11 @@ routes if (!showSecurityLinks) { deregisterSecurity(); } else { - // getCurrent will reject if there is no authenticated user, so we prevent them from seeing the security - // management screens - // - // $promise is used here because the result is an ngResource, not a promise itself - return ShieldUser.getCurrent() - .$promise.then(ensureSecurityRegistered) + // getCurrentUser will reject if there is no authenticated user, so we prevent them from + // seeing the security management screens. + return npSetup.plugins.security.authc + .getCurrentUser() + .then(ensureSecurityRegistered) .catch(deregisterSecurity); } }, diff --git a/x-pack/legacy/plugins/security/public/views/management/users_grid/users.js b/x-pack/legacy/plugins/security/public/views/management/users_grid/users.js index a7115f449ebfd..8d4e0526251d7 100644 --- a/x-pack/legacy/plugins/security/public/views/management/users_grid/users.js +++ b/x-pack/legacy/plugins/security/public/views/management/users_grid/users.js @@ -8,7 +8,6 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import routes from 'ui/routes'; import template from 'plugins/security/views/management/users_grid/users.html'; -import 'plugins/security/services/shield_user'; import { SECURITY_PATH, USERS_PATH } from '../management_urls'; import { UsersListPage } from './components'; import { UserAPIClient } from '../../../lib/api'; diff --git a/x-pack/legacy/plugins/security/public/views/overwritten_session/overwritten_session.tsx b/x-pack/legacy/plugins/security/public/views/overwritten_session/overwritten_session.tsx index 76088443212b2..fb39c517e1c2c 100644 --- a/x-pack/legacy/plugins/security/public/views/overwritten_session/overwritten_session.tsx +++ b/x-pack/legacy/plugins/security/public/views/overwritten_session/overwritten_session.tsx @@ -10,36 +10,40 @@ import React from 'react'; import { render } from 'react-dom'; import chrome from 'ui/chrome'; import { I18nContext } from 'ui/i18n'; +import { npSetup } from 'ui/new_platform'; +import { SecurityPluginSetup } from '../../../../../../plugins/security/public'; import { AuthenticatedUser } from '../../../common/model'; import { AuthenticationStatePage } from '../../components/authentication_state_page'; chrome .setVisible(false) .setRootTemplate('
') - .setRootController('overwritten_session', ($scope: any, ShieldUser: any) => { + .setRootController('overwritten_session', ($scope: any) => { $scope.$$postDigest(() => { - ShieldUser.getCurrent().$promise.then((user: AuthenticatedUser) => { - const overwrittenSessionPage = ( - - - } - > - - - - - - ); - render(overwrittenSessionPage, document.getElementById('reactOverwrittenSessionRoot')); - }); + ((npSetup.plugins as unknown) as { security: SecurityPluginSetup }).security.authc + .getCurrentUser() + .then((user: AuthenticatedUser) => { + const overwrittenSessionPage = ( + + + } + > + + + + + + ); + render(overwrittenSessionPage, document.getElementById('reactOverwrittenSessionRoot')); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx index 297563c8e31cf..ba07a3f3436d9 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx @@ -9,8 +9,6 @@ import { Axis, AreaSeries, Chart, - getAxisId, - getSpecId, Position, ScaleType, Settings, @@ -26,7 +24,6 @@ import { ChartSeriesData, getChartHeight, getChartWidth, - getSeriesStyle, WrappedByAutoSizer, useTheme, useBrowserTimeZone, @@ -77,8 +74,8 @@ export const AreaChartBaseComponent = ({ const timeZone = useBrowserTimeZone(); const xTickFormatter = get('configs.axis.xTickFormatter', chartConfigs); const yTickFormatter = get('configs.axis.yTickFormatter', chartConfigs); - const xAxisId = getAxisId(`group-${data[0].key}-x`); - const yAxisId = getAxisId(`group-${data[0].key}-y`); + const xAxisId = `group-${data[0].key}-x`; + const yAxisId = `group-${data[0].key}-y`; const settings = { ...chartDefaultSettings, theme, @@ -90,20 +87,19 @@ export const AreaChartBaseComponent = ({ {data.map(series => { const seriesKey = series.key; - const seriesSpecId = getSpecId(seriesKey); return checkIfAllTheDataInTheSeriesAreValid(series) ? ( ) : null; })} diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx index ee8b4eaf6b08c..db84d7dbd2c18 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx @@ -5,16 +5,7 @@ */ import React from 'react'; -import { - Chart, - BarSeries, - Axis, - Position, - getAxisId, - getSpecId, - ScaleType, - Settings, -} from '@elastic/charts'; +import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts'; import { getOr, get, isNumber } from 'lodash/fp'; import { AutoSizer } from '../auto_sizer'; import { ChartPlaceHolder } from './chart_place_holder'; @@ -25,8 +16,6 @@ import { checkIfAllValuesAreZero, getChartHeight, getChartWidth, - getSeriesStyle, - SeriesType, WrappedByAutoSizer, useBrowserTimeZone, useTheme, @@ -59,8 +48,8 @@ export const BarChartBaseComponent = ({ const xTickFormatter = get('configs.axis.xTickFormatter', chartConfigs); const yTickFormatter = get('configs.axis.yTickFormatter', chartConfigs); const tickSize = getOr(0, 'configs.axis.tickSize', chartConfigs); - const xAxisId = getAxisId(`stat-items-barchart-${data[0].key}-x`); - const yAxisId = getAxisId(`stat-items-barchart-${data[0].key}-y`); + const xAxisId = `stat-items-barchart-${data[0].key}-x`; + const yAxisId = `stat-items-barchart-${data[0].key}-y`; const settings = { ...chartDefaultSettings, theme, @@ -72,11 +61,9 @@ export const BarChartBaseComponent = ({ {data.map(series => { const barSeriesKey = series.key; - const barSeriesSpecId = getSpecId(barSeriesKey); - const seriesType = SeriesType.BAR; return checkIfAllTheDataInTheSeriesAreValid ? ( ) : null; })} diff --git a/x-pack/legacy/plugins/siem/public/components/charts/common.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/common.test.tsx index e9df0d3885a18..3748a2505c115 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/common.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/common.test.tsx @@ -13,8 +13,6 @@ import { defaultChartHeight, getChartHeight, getChartWidth, - getSeriesStyle, - SeriesType, WrappedByAutoSizer, ChartSeriesData, useTheme, @@ -41,26 +39,6 @@ describe('WrappedByAutoSizer', () => { }); }); -describe('getSeriesStyle', () => { - it('should not create style mapping if color is not given', () => { - const mockSeriesKey = 'mockSeriesKey'; - const color = ''; - const customSeriesColors = getSeriesStyle(mockSeriesKey, color, SeriesType.BAR); - expect(customSeriesColors).toBeUndefined(); - }); - - it('should create correct style mapping for series of a chart', () => { - const mockSeriesKey = 'mockSeriesKey'; - const color = 'red'; - const customSeriesColors = getSeriesStyle(mockSeriesKey, color, SeriesType.BAR); - const expectedKey = { colorValues: [mockSeriesKey] }; - customSeriesColors!.forEach((value, key) => { - expect(JSON.stringify(key)).toEqual(JSON.stringify(expectedKey)); - expect(value).toEqual(color); - }); - }); -}); - describe('getChartHeight', () => { it('should render customHeight', () => { const height = getChartHeight(10, 100); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx index dfb201fc3d927..a4be390019916 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx @@ -5,17 +5,14 @@ */ import { - CustomSeriesColorsMap, DARK_THEME, - DataSeriesColorsValues, - getSpecId, LIGHT_THEME, mergeWithDefaultTheme, PartialTheme, Rendering, Rotation, ScaleType, - SettingSpecProps, + SettingsSpecProps, TickFormatter, } from '@elastic/charts'; import moment from 'moment-timezone'; @@ -47,7 +44,7 @@ export interface ChartSeriesConfigs { xTickFormatter?: TickFormatter | undefined; yTickFormatter?: TickFormatter | undefined; }; - settings?: Partial; + settings?: Partial; } export interface ChartSeriesData { @@ -76,24 +73,6 @@ export enum SeriesType { LINE = 'line', } -// Customize colors: https://ela.st/custom-colors -export const getSeriesStyle = ( - seriesKey: string, - color: string | undefined, - seriesType?: SeriesType -) => { - if (!color) return undefined; - const customSeriesColors: CustomSeriesColorsMap = new Map(); - const dataSeriesColorValues: DataSeriesColorsValues = { - colorValues: seriesType === SeriesType.BAR ? [seriesKey] : [], - specId: getSpecId(seriesKey), - }; - - customSeriesColors.set(dataSeriesColorValues, color); - - return customSeriesColors; -}; - // Apply margins and paddings: https://ela.st/charts-spacing const theme: PartialTheme = { chartMargins: { diff --git a/x-pack/legacy/plugins/siem/public/components/empty_page/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/empty_page/__snapshots__/index.test.tsx.snap index 9b6bfb1752a20..87409c5fdebe0 100644 --- a/x-pack/legacy/plugins/siem/public/components/empty_page/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/empty_page/__snapshots__/index.test.tsx.snap @@ -3,8 +3,12 @@ exports[`renders correctly 1`] = ` - + + ( title={

{title}

} body={message &&

{message}

} actions={ - - + + ( {actionSecondaryLabel && actionSecondaryUrl && ( - + => { - try { - const text = await response.text(); - return JSON.parse(text); - } catch (error) { - return null; - } -}; - export const tryParseResponse = (response: string): string => { try { return JSON.stringify(JSON.parse(response), null, 2); diff --git a/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx index fa26664930fe5..35fe8a2d90509 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx @@ -9,8 +9,6 @@ import { Chart, HistogramBarSeries, Settings, - getAxisId, - getSpecId, niceTimeFormatByDay, timeFormatter, } from '@elastic/charts'; @@ -62,16 +60,12 @@ export const HistogramSignals = React.memo(() => { theme={npStart.plugins.eui_utils.useChartsTheme()} /> - + - + ({ query, @@ -46,7 +59,7 @@ export const fetchQuerySignals = async ({ * @param query of signals to update * @param status to update to('open' / 'closed') * @param kbnVersion current Kibana Version to use for headers - * @param signal to cancel request + * @param signal AbortSignal for cancelling request */ export const updateSignalStatus = async ({ query, @@ -69,3 +82,90 @@ export const updateSignalStatus = async ({ await throwIfNotOk(response); return response.json(); }; + +/** + * Fetch Signal Index + * + * @param kbnVersion current Kibana Version to use for headers + * @param signal AbortSignal for cancelling request + */ +export const getSignalIndex = async ({ + kbnVersion, + signal, +}: BasicSignals): Promise => { + const response = await fetch(`${chrome.getBasePath()}${DETECTION_ENGINE_INDEX_URL}`, { + method: 'GET', + credentials: 'same-origin', + headers: { + 'content-type': 'application/json', + 'kbn-version': kbnVersion, + 'kbn-xsrf': kbnVersion, + }, + signal, + }); + if (response.ok) { + const signalIndex = await response.json(); + return signalIndex; + } + const error = await parseJsonFromBody(response); + if (error != null) { + throw new SignalIndexError(error); + } + return null; +}; + +/** + * Get User Privileges + * + * @param kbnVersion current Kibana Version to use for headers + * @param signal AbortSignal for cancelling request + */ +export const getUserPrivilege = async ({ + kbnVersion, + signal, +}: BasicSignals): Promise => { + const response = await fetch(`${chrome.getBasePath()}${DETECTION_ENGINE_PRIVILEGES_URL}`, { + method: 'GET', + credentials: 'same-origin', + headers: { + 'content-type': 'application/json', + 'kbn-version': kbnVersion, + 'kbn-xsrf': kbnVersion, + }, + signal, + }); + + await throwIfNotOk(response); + return response.json(); +}; + +/** + * Create Signal Index if needed it + * + * @param kbnVersion current Kibana Version to use for headers + * @param signal AbortSignal for cancelling request + */ +export const createSignalIndex = async ({ + kbnVersion, + signal, +}: BasicSignals): Promise => { + const response = await fetch(`${chrome.getBasePath()}${DETECTION_ENGINE_INDEX_URL}`, { + method: 'POST', + credentials: 'same-origin', + headers: { + 'content-type': 'application/json', + 'kbn-version': kbnVersion, + 'kbn-xsrf': kbnVersion, + }, + signal, + }); + if (response.ok) { + const signalIndex = await response.json(); + return signalIndex; + } + const error = await parseJsonFromBody(response); + if (error != null) { + throw new PostSignalError(error); + } + return null; +}; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/get_index_error.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/get_index_error.ts new file mode 100644 index 0000000000000..e4f2a658b7362 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/get_index_error.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MessageBody } from '../../../../components/ml/api/throw_if_not_ok'; + +export class SignalIndexError extends Error { + message: string = ''; + statusCode: number = -1; + error: string = ''; + + constructor(errObj: MessageBody) { + super(errObj.message); + this.message = errObj.message ?? ''; + this.statusCode = errObj.statusCode ?? -1; + this.error = errObj.error ?? ''; + this.name = 'SignalIndexError'; + + // Set the prototype explicitly. + Object.setPrototypeOf(this, SignalIndexError.prototype); + } +} diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/index.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/index.ts new file mode 100644 index 0000000000000..4ce8e6ba89183 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './get_index_error'; +export * from './post_index_error'; +export * from './privilege_user_error'; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/post_index_error.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/post_index_error.ts new file mode 100644 index 0000000000000..d6d8cccfb4540 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/post_index_error.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MessageBody } from '../../../../components/ml/api/throw_if_not_ok'; + +export class PostSignalError extends Error { + message: string = ''; + statusCode: number = -1; + error: string = ''; + + constructor(errObj: MessageBody) { + super(errObj.message); + this.message = errObj.message ?? ''; + this.statusCode = errObj.statusCode ?? -1; + this.error = errObj.error ?? ''; + this.name = 'PostSignalError'; + + // Set the prototype explicitly. + Object.setPrototypeOf(this, PostSignalError.prototype); + } +} diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/privilege_user_error.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/privilege_user_error.ts new file mode 100644 index 0000000000000..5cd458a7fe9aa --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/privilege_user_error.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MessageBody } from '../../../../components/ml/api/throw_if_not_ok'; + +export class PrivilegeUserError extends Error { + message: string = ''; + statusCode: number = -1; + error: string = ''; + + constructor(errObj: MessageBody) { + super(errObj.message); + this.message = errObj.message ?? ''; + this.statusCode = errObj.statusCode ?? -1; + this.error = errObj.error ?? ''; + this.name = 'PrivilegeUserError'; + + // Set the prototype explicitly. + Object.setPrototypeOf(this, PrivilegeUserError.prototype); + } +} diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/types.ts index 5846f3275c0fd..118c2b367ca5b 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/types.ts @@ -4,11 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface QuerySignals { - query: string; +export * from './errors_types'; + +export interface BasicSignals { kbnVersion: string; signal: AbortSignal; } +export interface QuerySignals extends BasicSignals { + query: string; +} export interface SignalsResponse { took: number; @@ -38,3 +42,60 @@ export interface UpdateSignalStatusProps { kbnVersion: string; signal?: AbortSignal; // TODO: implement cancelling } + +export interface SignalsIndex { + name: string; +} + +export interface Privilege { + username: string; + has_all_requested: boolean; + cluster: { + monitor_ml: boolean; + manage_ccr: boolean; + manage_index_templates: boolean; + monitor_watcher: boolean; + monitor_transform: boolean; + read_ilm: boolean; + manage_api_key: boolean; + manage_security: boolean; + manage_own_api_key: boolean; + manage_saml: boolean; + all: boolean; + manage_ilm: boolean; + manage_ingest_pipelines: boolean; + read_ccr: boolean; + manage_rollup: boolean; + monitor: boolean; + manage_watcher: boolean; + manage: boolean; + manage_transform: boolean; + manage_token: boolean; + manage_ml: boolean; + manage_pipeline: boolean; + monitor_rollup: boolean; + transport_client: boolean; + create_snapshot: boolean; + }; + index: { + [indexName: string]: { + all: boolean; + manage_ilm: boolean; + read: boolean; + create_index: boolean; + read_cross_cluster: boolean; + index: boolean; + monitor: boolean; + delete: boolean; + manage: boolean; + delete_index: boolean; + create_doc: boolean; + view_index_metadata: boolean; + create: boolean; + manage_follow_index: boolean; + manage_leader_index: boolean; + write: boolean; + }; + }; + isAuthenticated: boolean; +} diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx new file mode 100644 index 0000000000000..6f897703059f7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useState } from 'react'; + +import { DEFAULT_KBN_VERSION } from '../../../../common/constants'; +import { useUiSetting$ } from '../../../lib/kibana'; +import { getUserPrivilege } from './api'; + +type Return = [boolean, boolean | null, boolean | null]; + +/** + * Hook to get user privilege from + * + */ +export const usePrivilegeUser = (): Return => { + const [loading, setLoading] = useState(true); + const [isAuthenticated, setAuthenticated] = useState(null); + const [hasWrite, setHasWrite] = useState(null); + const [kbnVersion] = useUiSetting$(DEFAULT_KBN_VERSION); + + useEffect(() => { + let isSubscribed = true; + const abortCtrl = new AbortController(); + setLoading(true); + + async function fetchData() { + try { + const privilege = await getUserPrivilege({ + kbnVersion, + signal: abortCtrl.signal, + }); + + if (isSubscribed && privilege != null) { + setAuthenticated(privilege.isAuthenticated); + if (privilege.index != null && Object.keys(privilege.index).length > 0) { + const indexName = Object.keys(privilege.index)[0]; + setHasWrite(privilege.index[indexName].create_index); + } + } + } catch (error) { + if (isSubscribed) { + setAuthenticated(false); + setHasWrite(false); + } + } + if (isSubscribed) { + setLoading(false); + } + } + + fetchData(); + return () => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, []); + + return [loading, isAuthenticated, hasWrite]; +}; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx index d865625288550..9501f1189a483 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_query.tsx @@ -8,11 +8,8 @@ import { useEffect, useState } from 'react'; import { useUiSetting$ } from '../../../lib/kibana'; import { DEFAULT_KBN_VERSION } from '../../../../common/constants'; -import { errorToToaster } from '../../../components/ml/api/error_to_toaster'; -import { useStateToaster } from '../../../components/toasters'; import { fetchQuerySignals } from './api'; -import * as i18n from './translations'; import { SignalSearchResponse } from './types'; type Return = [boolean, SignalSearchResponse | null]; @@ -27,7 +24,6 @@ export const useQuerySignals = (query: string): Return => const [signals, setSignals] = useState | null>(null); const [loading, setLoading] = useState(true); const [kbnVersion] = useUiSetting$(DEFAULT_KBN_VERSION); - const [, dispatchToaster] = useStateToaster(); useEffect(() => { let isSubscribed = true; @@ -48,7 +44,6 @@ export const useQuerySignals = (query: string): Return => } catch (error) { if (isSubscribed) { setSignals(null); - errorToToaster({ title: i18n.SIGNAL_FETCH_FAILURE, error, dispatchToaster }); } } if (isSubscribed) { diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_signal_index.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_signal_index.tsx new file mode 100644 index 0000000000000..347c90fa3b411 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_signal_index.tsx @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useState, useRef } from 'react'; + +import { DEFAULT_KBN_VERSION } from '../../../../common/constants'; +import { errorToToaster } from '../../../components/ml/api/error_to_toaster'; +import { useStateToaster } from '../../../components/toasters'; +import { useUiSetting$ } from '../../../lib/kibana'; +import { createSignalIndex, getSignalIndex } from './api'; +import * as i18n from './translations'; +import { PostSignalError } from './types'; + +type Func = () => void; + +type Return = [boolean, boolean | null, string | null, Func | null]; + +/** + * Hook for managing signal index + * + * + */ +export const useSignalIndex = (): Return => { + const [loading, setLoading] = useState(true); + const [signalIndexName, setSignalIndexName] = useState(null); + const [signalIndexExists, setSignalIndexExists] = useState(null); + const createDeSignalIndex = useRef(null); + const [kbnVersion] = useUiSetting$(DEFAULT_KBN_VERSION); + const [, dispatchToaster] = useStateToaster(); + + useEffect(() => { + let isSubscribed = true; + const abortCtrl = new AbortController(); + + const fetchData = async () => { + try { + setLoading(true); + const signal = await getSignalIndex({ + kbnVersion, + signal: abortCtrl.signal, + }); + + if (isSubscribed && signal != null) { + setSignalIndexName(signal.name); + setSignalIndexExists(true); + } + } catch (error) { + if (isSubscribed) { + setSignalIndexName(null); + setSignalIndexExists(false); + } + } + if (isSubscribed) { + setLoading(false); + } + }; + + const createIndex = async () => { + let isFetchingData = false; + try { + setLoading(true); + await createSignalIndex({ + kbnVersion, + signal: abortCtrl.signal, + }); + + if (isSubscribed) { + isFetchingData = true; + fetchData(); + } + } catch (error) { + if (isSubscribed) { + if (error instanceof PostSignalError && error.statusCode === 409) { + fetchData(); + } else { + setSignalIndexName(null); + setSignalIndexExists(false); + errorToToaster({ title: i18n.SIGNAL_FETCH_FAILURE, error, dispatchToaster }); + } + } + } + if (isSubscribed && !isFetchingData) { + setLoading(false); + } + }; + + fetchData(); + createDeSignalIndex.current = createIndex; + return () => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, []); + + return [loading, signalIndexExists, signalIndexName, createDeSignalIndex.current]; +}; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx index aeb5e677374fc..f9e80334a8882 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx @@ -26,7 +26,7 @@ import { SignalsTableFilterGroup, } from './signals_filter_group'; import { useKibana, useUiSetting$ } from '../../../../lib/kibana'; -import { DEFAULT_KBN_VERSION, DEFAULT_SIGNALS_INDEX } from '../../../../../common/constants'; +import { DEFAULT_KBN_VERSION } from '../../../../../common/constants'; import { defaultHeaders } from '../../../../components/timeline/body/column_headers/default_headers'; import { ColumnHeader } from '../../../../components/timeline/body/column_headers/column_header'; import { esFilters, esQuery } from '../../../../../../../../../src/plugins/data/common/es_query'; @@ -91,6 +91,7 @@ interface DispatchProps { interface OwnProps { defaultFilters?: esFilters.Filter[]; from: number; + signalsIndex: string; to: number; } @@ -112,15 +113,14 @@ export const SignalsTableComponent = React.memo( selectedEventIds, setEventsDeleted, setEventsLoading, + signalsIndex, to, }) => { const [selectAll, setSelectAll] = useState(false); const [showClearSelectionAction, setShowClearSelectionAction] = useState(false); const [filterGroup, setFilterGroup] = useState(FILTER_OPEN); - const [{ browserFields, indexPatterns }] = useFetchIndexPatterns([ - `${DEFAULT_SIGNALS_INDEX}-default`, - ]); // TODO Get from new FrankInspired XavierHook + const [{ browserFields, indexPatterns }] = useFetchIndexPatterns([signalsIndex]); const [kbnVersion] = useUiSetting$(DEFAULT_KBN_VERSION); const kibana = useKibana(); @@ -266,9 +266,7 @@ export const SignalsTableComponent = React.memo( [createTimelineCallback, filterGroup, kbnVersion] ); - const defaultIndices = useMemo(() => [`${DEFAULT_SIGNALS_INDEX}-default`], [ - `${DEFAULT_SIGNALS_INDEX}-default`, - ]); + const defaultIndices = useMemo(() => [signalsIndex], [signalsIndex]); const defaultFiltersMemo = useMemo( () => [ ...defaultFilters, @@ -292,7 +290,7 @@ export const SignalsTableComponent = React.memo( return ( { const [lastSignals, setLastSignals] = useState( ); - const [totalSignals, setTotalSignals] = useState( + const [totalSignals, setTotalSignals] = useState( ); @@ -33,7 +33,7 @@ export const useSignalInfo = ({ ruleId = null }: SignalInfo): Return => { query = ''; } - const [, signals] = useQuerySignals(query); + const [loading, signals] = useQuerySignals(query); useEffect(() => { if (signals != null) { @@ -46,8 +46,11 @@ export const useSignalInfo = ({ ruleId = null }: SignalInfo): Return => { ) : null ); setTotalSignals(<>{mySignals.hits.total.value}); + } else { + setLastSignals(null); + setTotalSignals(null); } - }, [signals]); + }, [loading, signals]); return [lastSignals, totalSignals]; }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx index 6e6b71729b07e..8e5c3e9f13118 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButton, EuiSpacer } from '@elastic/eui'; +import { EuiButton, EuiSpacer, EuiPanel, EuiLoadingContent } from '@elastic/eui'; import React from 'react'; import { StickyContainer } from 'react-sticky'; @@ -17,59 +17,101 @@ import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../cont import { SpyRoute } from '../../utils/route/spy_routes'; import { SignalsTable } from './components/signals'; +import * as signalsI18n from './components/signals/translations'; import { SignalsCharts } from './components/signals_chart'; import { useSignalInfo } from './components/signals_info'; import { DetectionEngineEmptyPage } from './detection_engine_empty_page'; +import { DetectionEngineNoIndex } from './detection_engine_no_signal_index'; +import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated'; import * as i18n from './translations'; +import { HeaderSection } from '../../components/header_section'; -export const DetectionEngineComponent = React.memo(() => { - const [lastSignals] = useSignalInfo({}); - return ( - <> - - {({ indicesExist, indexPattern }) => { - return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - - - - +interface DetectionEngineComponentProps { + loading: boolean; + isSignalIndexExists: boolean | null; + isUserAuthenticated: boolean | null; + signalsIndex: string | null; +} - - - {i18n.LAST_SIGNAL} - {': '} - {lastSignals} - - ) - } - title={i18n.PAGE_TITLE} - > - - {i18n.BUTTON_MANAGE_RULES} - - +export const DetectionEngineComponent = React.memo( + ({ loading, isSignalIndexExists, isUserAuthenticated, signalsIndex }) => { + const [lastSignals] = useSignalInfo({}); + if (isUserAuthenticated != null && !isUserAuthenticated && !loading) { + return ( + + + + + ); + } + if (isSignalIndexExists != null && !isSignalIndexExists && !loading) { + return ( + + + + + ); + } + return ( + <> + + {({ indicesExist, indexPattern }) => { + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + + + + + + + + {i18n.LAST_SIGNAL} + {': '} + {lastSignals} + + ) + } + title={i18n.PAGE_TITLE} + > + + {i18n.BUTTON_MANAGE_RULES} + + - + - - {({ to, from }) => } + + + {({ to, from }) => + !loading ? ( + isSignalIndexExists && ( + + ) + ) : ( + + + + + ) + } + + + + ) : ( + + + - - ) : ( - - - - - ); - }} - + ); + }} + - - - ); -}); + + + ); + } +); DetectionEngineComponent.displayName = 'DetectionEngineComponent'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_no_signal_index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_no_signal_index.tsx new file mode 100644 index 0000000000000..713bd6239d80e --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_no_signal_index.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { documentationLinks } from 'ui/documentation_links'; + +import { EmptyPage } from '../../components/empty_page'; +import * as i18n from './translations'; + +export const DetectionEngineNoIndex = React.memo(() => ( + +)); + +DetectionEngineNoIndex.displayName = 'DetectionEngineNoIndex'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_user_unauthenticated.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_user_unauthenticated.tsx new file mode 100644 index 0000000000000..bd3876b810a73 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_user_unauthenticated.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { documentationLinks } from 'ui/documentation_links'; + +import { EmptyPage } from '../../components/empty_page'; +import * as i18n from './translations'; + +export const DetectionEngineUserUnauthenticated = React.memo(() => ( + +)); + +DetectionEngineUserUnauthenticated.displayName = 'DetectionEngineUserUnauthenticated'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx index 21ebac2b4d337..e8a2c98a94a56 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx @@ -4,9 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; +import { useSignalIndex } from '../../containers/detection_engine/signals/use_signal_index'; +import { usePrivilegeUser } from '../../containers/detection_engine/signals/use_privilege_user'; + import { CreateRuleComponent } from './rules/create'; import { DetectionEngineComponent } from './detection_engine'; import { EditRuleComponent } from './rules/edit'; @@ -17,29 +20,61 @@ const detectionEnginePath = `/:pageName(detection-engine)`; type Props = Partial> & { url: string }; -export const DetectionEngineContainer = React.memo(() => ( - - - - - - - - - - - - - - - - - ( - +export const DetectionEngineContainer = React.memo(() => { + const [privilegeLoading, isAuthenticated, hasWrite] = usePrivilegeUser(); + const [ + indexNameLoading, + isSignalIndexExists, + signalIndexName, + createSignalIndex, + ] = useSignalIndex(); + + useEffect(() => { + if ( + isAuthenticated && + hasWrite && + isSignalIndexExists != null && + !isSignalIndexExists && + createSignalIndex != null + ) { + createSignalIndex(); + } + }, [createSignalIndex, isAuthenticated, isSignalIndexExists, hasWrite]); + + return ( + + + + + {isSignalIndexExists && isAuthenticated && ( + <> + + + + + + + + + + + + + )} - /> - -)); + + ( + + )} + /> + + ); +}); DetectionEngineContainer.displayName = 'DetectionEngineContainer'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index b0cf183949dd9..1bc2bc24517e3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -40,7 +40,11 @@ import * as ruleI18n from '../translations'; import * as i18n from './translations'; import { GlobalTime } from '../../../../containers/global_time'; -export const RuleDetailsComponent = memo(() => { +interface RuleDetailsComponentProps { + signalsIndex: string | null; +} + +export const RuleDetailsComponent = memo(({ signalsIndex }) => { const { ruleId } = useParams(); const [loading, rule] = useRule(ruleId); const { aboutRuleData, defineRuleData, scheduleRuleData } = getStepsData({ @@ -200,7 +204,12 @@ export const RuleDetailsComponent = memo(() => { {ruleId != null && ( - + )} @@ -220,4 +229,5 @@ export const RuleDetailsComponent = memo(() => { ); }); + RuleDetailsComponent.displayName = 'RuleDetailsComponent'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts index c7a71bf48d7dd..e5f830d3a49b0 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts @@ -51,3 +51,34 @@ export const EMPTY_ACTION_SECONDARY = i18n.translate( defaultMessage: 'Go to documentation', } ); + +export const NO_INDEX_TITLE = i18n.translate('xpack.siem.detectionEngine.noIndexTitle', { + defaultMessage: 'Let’s set up your detection engine', +}); + +export const NO_INDEX_MSG_BODY = i18n.translate('xpack.siem.detectionEngine.noIndexMsgBody', { + defaultMessage: + 'To use the detection engine, a user with the required cluster and index privileges must first access this page. For more help, contact your administrator.', +}); + +export const GO_TO_DOCUMENTATION = i18n.translate( + 'xpack.siem.detectionEngine.goToDocumentationButton', + { + defaultMessage: 'View documentation', + } +); + +export const USER_UNAUTHENTICATED_TITLE = i18n.translate( + 'xpack.siem.detectionEngine.userUnauthenticatedTitle', + { + defaultMessage: 'Detection engine permissions required', + } +); + +export const USER_UNAUTHENTICATED_MSG_BODY = i18n.translate( + 'xpack.siem.detectionEngine.userUnauthenticatedMsgBody', + { + defaultMessage: + 'You do not have the required permissions for viewing the detection engine. For more help, contact your administrator.', + } +); diff --git a/x-pack/legacy/plugins/siem/public/utils/api/index.ts b/x-pack/legacy/plugins/siem/public/utils/api/index.ts new file mode 100644 index 0000000000000..1dc14413b04d2 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/utils/api/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface MessageBody { + error?: string; + message?: string; + statusCode?: number; +} + +export const parseJsonFromBody = async (response: Response): Promise => { + try { + const text = await response.text(); + return JSON.parse(text); + } catch (error) { + return null; + } +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 66e1d29b80852..edf196b96f5d0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -283,9 +283,7 @@ export const getResult = (): RuleAlertType => ({ ], riskScore: 50, maxSignals: 100, - size: 1, severity: 'high', - tags: [], to: 'now', type: 'query', threats: [ @@ -381,4 +379,5 @@ export const getMockPrivileges = () => ({ }, }, application: {}, + isAuthenticated: false, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts index 457de05674f66..240200af8b585 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts @@ -5,6 +5,7 @@ */ import Hapi from 'hapi'; +import { merge } from 'lodash/fp'; import { DETECTION_ENGINE_PRIVILEGES_URL } from '../../../../../common/constants'; import { RulesRequest } from '../../rules/types'; import { ServerFacade } from '../../../../types'; @@ -28,7 +29,9 @@ export const createReadPrivilegesRulesRoute = (server: ServerFacade): Hapi.Serve const callWithRequest = callWithRequestFactory(request, server); const index = getIndex(request, server); const permissions = await readPrivileges(callWithRequest, index); - return permissions; + return merge(permissions, { + isAuthenticated: request?.auth?.isAuthenticated ?? false, + }); } catch (err) { return transformError(err); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts index c1058bd353e8c..5f69082e3fc71 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/find_rules.ts @@ -5,7 +5,7 @@ */ import { SIGNALS_ID } from '../../../../common/constants'; -import { FindRuleParams } from './types'; +import { FindRuleParams, RuleAlertType } from './types'; export const getFilter = (filter: string | null | undefined) => { if (filter == null) { @@ -33,5 +33,10 @@ export const findRules = async ({ sortOrder, sortField, }, - }); + }) as Promise<{ + page: number; + perPage: number; + total: number; + data: RuleAlertType[]; + }>; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts index b0578174e1f65..4f4c0da7127cd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts @@ -35,10 +35,9 @@ export interface BulkUpdateRulesRequest extends RequestFacade { payload: UpdateRuleAlertParamsRest[]; } -export type RuleAlertType = Alert & { - id: string; +export interface RuleAlertType extends Alert { params: RuleTypeParams; -}; +} export interface RulesRequest extends RequestFacade { payload: RuleAlertParamsRest; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap index 1f197294ebc15..78b2bfdecb87a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap @@ -13,7 +13,7 @@ exports[`MonitorBarSeries component renders a series when there are down items 1 - - - "A danger color", - } + Array [ + "A danger color", + ] } data={ Array [ diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/checks_chart.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/checks_chart.tsx index c7ac0a59e4434..a88a9668660f7 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/checks_chart.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/checks_chart.tsx @@ -8,8 +8,6 @@ import { AreaSeries, Axis, Chart, - getAxisId, - getSpecId, Position, Settings, ScaleType, @@ -21,7 +19,6 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { StatusData } from '../../../../common/graphql/types'; import { getChartDateLabel } from '../../../lib/helper'; -import { getColorsMap } from './get_colors_map'; import { useUrlParams } from '../../../hooks'; interface ChecksChartProps { @@ -45,8 +42,8 @@ interface ChecksChartProps { * @param props The props values required by this component. */ export const ChecksChart = ({ dangerColor, status, successColor }: ChecksChartProps) => { - const upSeriesSpecId = getSpecId('Up'); - const downSeriesSpecId = getSpecId('Down'); + const upSeriesSpecId = 'Up'; + const downSeriesSpecId = 'Down'; const [getUrlParams] = useUrlParams(); const { absoluteDateRangeStart: min, absoluteDateRangeEnd: max } = getUrlParams(); @@ -74,7 +71,7 @@ export const ChecksChart = ({ dangerColor, status, successColor }: ChecksChartPr Number(d).toFixed(0)} title={i18n.translate('xpack.uptime.monitorChart.checksChart.leftAxis.title', { @@ -93,7 +90,7 @@ export const ChecksChart = ({ dangerColor, status, successColor }: ChecksChartPr })} /> ({ x, [upString]: up || 0, @@ -107,7 +104,7 @@ export const ChecksChart = ({ dangerColor, status, successColor }: ChecksChartPr yScaleType={ScaleType.Linear} /> ({ x, [downString]: down || 0, diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx index 012d848610940..b5dcfce032724 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Axis, Chart, getAxisId, Position, timeFormatter, Settings } from '@elastic/charts'; +import { Axis, Chart, Position, timeFormatter, Settings } from '@elastic/charts'; import { EuiPanel, EuiTitle } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; @@ -69,7 +69,7 @@ export const DurationChart = ({ getTickFormat(d)} title={i18n.translate('xpack.uptime.monitorCharts.durationChart.leftAxis.title', { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_line_series_list.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_line_series_list.tsx index b6a65f193e913..872c79933d85a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_line_series_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_line_series_list.tsx @@ -5,9 +5,8 @@ */ import React from 'react'; -import { LineSeries, CurveType, getSpecId } from '@elastic/charts'; +import { LineSeries, CurveType } from '@elastic/charts'; import { LocationDurationLine } from '../../../../common/graphql/types'; -import { getColorsMap } from './get_colors_map'; import { convertMicrosecondsToMilliseconds as microsToMillis } from '../../../lib/helper'; interface Props { @@ -21,9 +20,9 @@ export const DurationLineSeriesList = ({ lines, meanColor }: Props) => ( [x, microsToMillis(y || null)])} - id={getSpecId(`loc-avg-${name}`)} + id={`loc-avg-${name}`} key={`locline-${name}`} name={name} xAccessor={0} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/get_colors_map.ts b/x-pack/legacy/plugins/uptime/public/components/functional/charts/get_colors_map.ts deleted file mode 100644 index 83d181d91f8da..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/get_colors_map.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { DataSeriesColorsValues, SpecId } from '@elastic/charts'; - -/** - * This is a helper function used to more easily define a basic map - * for color values for use with elastic charts. Support for multiple - * color values can be added in the future if needed. - * @param color a string containing a valid color value - * @param specId an ID generated by the elastic charts library - */ -export const getColorsMap = ( - color: string, - specId: SpecId -): Map => { - const map = new Map(); - map.set({ colorValues: [], specId }, color); - return map; -}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx index 52b41416bd17b..a0cbdc5922123 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx @@ -8,10 +8,8 @@ import { Axis, BarSeries, Chart, - getSpecId, ScaleType, Settings, - getAxisId, Position, timeFormatter, } from '@elastic/charts'; @@ -20,7 +18,6 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiText, EuiToolTip } from '@elastic/eui'; import { SummaryHistogramPoint } from '../../../../common/graphql/types'; -import { getColorsMap } from './get_colors_map'; import { getChartDateLabel, seriesHasDownValues } from '../../../lib/helper'; export interface MonitorBarSeriesProps { @@ -53,7 +50,7 @@ export const MonitorBarSeries = ({ dangerColor, histogramSeries, }: MonitorBarSeriesProps) => { - const id = getSpecId('downSeries'); + const id = 'downSeries'; return seriesHasDownValues(histogramSeries) ? (
@@ -61,14 +58,14 @@ export const MonitorBarSeries = ({ [timestamp, down])} id={id} + customSeriesColors={[dangerColor]} + data={(histogramSeries || []).map(({ timestamp, down }) => [timestamp, down])} name={i18n.translate('xpack.uptime.monitorList.downLineSeries.downLabel', { defaultMessage: 'Down checks', })} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/snapshot_histogram.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/snapshot_histogram.tsx index bacd06dbe162d..52ba482ce7fc1 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/snapshot_histogram.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/snapshot_histogram.tsx @@ -4,22 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Axis, - BarSeries, - Chart, - getAxisId, - getSpecId, - Position, - timeFormatter, - Settings, -} from '@elastic/charts'; +import { Axis, BarSeries, Chart, Position, timeFormatter, Settings } from '@elastic/charts'; import { EuiEmptyPrompt, EuiTitle, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useContext } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; -import { getColorsMap } from './get_colors_map'; import { getChartDateLabel } from '../../../lib/helper'; import { withUptimeGraphQL, UptimeGraphQLQueryProps } from '../../higher_order'; import { snapshotHistogramQuery } from '../../../queries/snapshot_histogram_query'; @@ -110,12 +100,12 @@ export const SnapshotHistogramComponent: React.FC = ({ const downMonitorsId = i18n.translate('xpack.uptime.snapshotHistogram.downMonitorsId', { defaultMessage: 'Down Monitors', }); - const downSpecId = getSpecId(downMonitorsId); + const downSpecId = downMonitorsId; const upMonitorsId = i18n.translate('xpack.uptime.snapshotHistogram.series.upLabel', { defaultMessage: 'Up', }); - const upSpecId = getSpecId(upMonitorsId); + const upSpecId = upMonitorsId; return ( <> @@ -148,21 +138,17 @@ export const SnapshotHistogramComponent: React.FC = ({ showLegend={false} /> = ({ })} /> [x, downCount || 0])} id={downSpecId} name={i18n.translate('xpack.uptime.snapshotHistogram.series.downLabel', { @@ -185,7 +171,7 @@ export const SnapshotHistogramComponent: React.FC = ({ yScaleType="linear" /> [x, upCount || 0])} id={upSpecId} name={upMonitorsId} diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__mocks__/mock.ts b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/__mocks__/mock.ts similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__mocks__/mock.ts rename to x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/__mocks__/mock.ts index 9b902651690bf..291ab555fbdc6 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__mocks__/mock.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/__mocks__/mock.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import lowPolyLayerFeatures from '../low_poly_layer.json'; +import lowPolyLayerFeatures from '../../low_poly_layer.json'; export const mockDownPointsLayer = { id: 'down_points', diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.test.ts b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/map_config.test.ts similarity index 83% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.test.ts rename to x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/map_config.test.ts index 1e8e5b6012a79..263c3fc787da9 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.test.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/map_config.test.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getLayerList } from './map_config'; +import { getLayerList } from '../map_config'; import { mockLayerList } from './__mocks__/mock'; -import { LocationPoint } from './embedded_map'; +import { LocationPoint } from '../embedded_map'; jest.mock('uuid', () => { return { @@ -33,7 +33,7 @@ describe('map_config', () => { describe('#getLayerList', () => { test('it returns the low poly layer', () => { - const layerList = getLayerList(upPoints, downPoints); + const layerList = getLayerList(upPoints, downPoints, { danger: '#BC261E', gray: '#000' }); expect(layerList).toStrictEqual(mockLayerList); }); }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index 93de1d478fb83..fe8a1a0bad7ec 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useContext } from 'react'; import uuid from 'uuid'; import styled from 'styled-components'; @@ -15,6 +15,7 @@ import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/common/constants'; import { MapEmbeddable } from './types'; import { getLayerList } from './map_config'; +import { UptimeSettingsContext } from '../../../../contexts'; export interface EmbeddedMapProps { upPoints: LocationPoint[]; @@ -45,6 +46,7 @@ const EmbeddedPanel = styled.div` `; export const EmbeddedMap = ({ upPoints, downPoints }: EmbeddedMapProps) => { + const { colors } = useContext(UptimeSettingsContext); const [embeddable, setEmbeddable] = useState(); const embeddableRoot: React.RefObject = React.createRef(); const factory = start.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); @@ -58,16 +60,18 @@ export const EmbeddedMap = ({ upPoints, downPoints }: EmbeddedMapProps) => { viewMode: 'view', isLayerTOCOpen: false, hideFilterActions: true, - mapCenter: { lon: 11, lat: 47, zoom: 0 }, + mapCenter: { lon: 11, lat: 20, zoom: 0 }, disableInteractive: true, disableTooltipControl: true, hideToolbarOverlay: true, + hideLayerControl: true, + hideViewControl: true, }; useEffect(() => { async function setupEmbeddable() { const mapState = { - layerList: getLayerList(upPoints, downPoints), + layerList: getLayerList(upPoints, downPoints, colors), title: i18n.MAP_TITLE, }; // @ts-ignore @@ -82,9 +86,9 @@ export const EmbeddedMap = ({ upPoints, downPoints }: EmbeddedMapProps) => { useEffect(() => { if (embeddable) { - embeddable.setLayerList(getLayerList(upPoints, downPoints)); + embeddable.setLayerList(getLayerList(upPoints, downPoints, colors)); } - }, [upPoints, downPoints, embeddable]); + }, [upPoints, downPoints, embeddable, colors]); useEffect(() => { if (embeddableRoot.current && embeddable) { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts index 608df8b235f00..b423b8baf41bf 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts @@ -12,8 +12,12 @@ import { LocationPoint } from './embedded_map'; * destination, and line layer for each of the provided indexPatterns * */ -export const getLayerList = (upPoints: LocationPoint[], downPoints: LocationPoint[]) => { - return [getLowPolyLayer(), getDownPointsLayer(downPoints), getUpPointsLayer(upPoints)]; +export const getLayerList = ( + upPoints: LocationPoint[], + downPoints: LocationPoint[], + { gray, danger }: { gray: string; danger: string } +) => { + return [getLowPolyLayer(), getDownPointsLayer(downPoints, danger), getUpPointsLayer(upPoints)]; }; export const getLowPolyLayer = () => { @@ -62,7 +66,7 @@ export const getLowPolyLayer = () => { }; }; -export const getDownPointsLayer = (downPoints: LocationPoint[]) => { +export const getDownPointsLayer = (downPoints: LocationPoint[], dangerColor: string) => { const features = downPoints?.map(point => ({ type: 'feature', geometry: { @@ -87,7 +91,7 @@ export const getDownPointsLayer = (downPoints: LocationPoint[]) => { fillColor: { type: 'STATIC', options: { - color: '#BC261E', + color: dangerColor, }, }, lineColor: { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx index b271632cb631f..f70d145ec05c3 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx @@ -6,15 +6,19 @@ import React from 'react'; import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { LocationStatusTags } from './location_status_tags'; import { EmbeddedMap, LocationPoint } from './embeddables/embedded_map'; +import { MonitorLocations } from '../../../../common/runtime_types'; const MapPanel = styled.div` - height: 400px; + height: 240px; width: 520px; + margin-right: 10px; `; interface LocationMapProps { - monitorLocations: any; + monitorLocations: MonitorLocations; } export const LocationMap = ({ monitorLocations }: LocationMapProps) => { @@ -31,8 +35,15 @@ export const LocationMap = ({ monitorLocations }: LocationMapProps) => { }); } return ( - - - + + + + + + + + + + ); }; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx new file mode 100644 index 0000000000000..a10d8e02e6863 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext } from 'react'; +import styled from 'styled-components'; +import { EuiBadge, EuiText } from '@elastic/eui'; +import { UptimeSettingsContext } from '../../../contexts'; +import { MonitorLocation } from '../../../../common/runtime_types'; + +const TextStyle = styled.div` + font-weight: 600; +`; + +const BadgeItem = styled.div` + margin-bottom: 5px; +`; + +const TagContainer = styled.div` + padding: 10px; + max-height: 200px; + overflow: hidden; +`; + +interface Props { + locations: MonitorLocation[]; +} + +export const LocationStatusTags = ({ locations }: Props) => { + const { + colors: { gray, danger }, + } = useContext(UptimeSettingsContext); + + const upLocs: string[] = []; + const downLocs: string[] = []; + + locations.forEach((item: any) => { + if (item.summary.down === 0) { + upLocs.push(item.geo.name); + } else { + downLocs.push(item.geo.name); + } + }); + + return ( + + + {downLocs.map((item, ind) => ( + + + + {item} + + + + ))} + + + {upLocs.map((item, ind) => ( + + + + {item} + + + + ))} + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/np_ready/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx b/x-pack/legacy/plugins/watcher/public/np_ready/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx index a3da7d14c8886..3901f84f506ef 100644 --- a/x-pack/legacy/plugins/watcher/public/np_ready/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx +++ b/x-pack/legacy/plugins/watcher/public/np_ready/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx @@ -8,9 +8,6 @@ import React, { Fragment, useContext, useEffect } from 'react'; import { AnnotationDomainTypes, Axis, - getAnnotationId, - getAxisId, - getSpecId, Chart, LineAnnotation, LineSeries, @@ -224,22 +221,17 @@ export const WatchVisualization = () => { legendPosition={Position.Bottom} /> - + {watchVisualizationDataKeys.map((key: string) => { return ( { return ( diff --git a/x-pack/package.json b/x-pack/package.json index c0e7a7c86cf0b..110db56c5d4ed 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -230,7 +230,7 @@ "file-type": "^10.9.0", "font-awesome": "4.7.0", "formsy-react": "^1.1.5", - "fp-ts": "^2.0.5", + "fp-ts": "^2.3.1", "geojson-rewind": "^0.3.1", "get-port": "4.2.0", "getos": "^3.1.0", diff --git a/x-pack/plugins/security/public/authentication/authentication_service.ts b/x-pack/plugins/security/public/authentication/authentication_service.ts new file mode 100644 index 0000000000000..23c45c88e563a --- /dev/null +++ b/x-pack/plugins/security/public/authentication/authentication_service.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { HttpSetup } from 'src/core/public'; +import { AuthenticatedUser } from '../../common/model'; + +interface SetupParams { + http: HttpSetup; +} + +export interface AuthenticationServiceSetup { + /** + * Returns currently authenticated user and throws if current user isn't authenticated. + */ + getCurrentUser: () => Promise; +} + +export class AuthenticationService { + public setup({ http }: SetupParams): AuthenticationServiceSetup { + return { + async getCurrentUser() { + return (await http.get('/internal/security/me', { + headers: { 'kbn-system-api': true }, + })) as AuthenticatedUser; + }, + }; + } +} diff --git a/x-pack/plugins/security/public/authentication/index.mock.ts b/x-pack/plugins/security/public/authentication/index.mock.ts new file mode 100644 index 0000000000000..c8d77a5b62c6f --- /dev/null +++ b/x-pack/plugins/security/public/authentication/index.mock.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AuthenticationServiceSetup } from './authentication_service'; + +export const authenticationMock = { + createSetup: (): jest.Mocked => ({ + getCurrentUser: jest.fn(), + }), +}; diff --git a/x-pack/plugins/security/public/authentication/index.ts b/x-pack/plugins/security/public/authentication/index.ts new file mode 100644 index 0000000000000..a55f4d7bb95b3 --- /dev/null +++ b/x-pack/plugins/security/public/authentication/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { AuthenticationService, AuthenticationServiceSetup } from './authentication_service'; diff --git a/x-pack/plugins/security/public/index.ts b/x-pack/plugins/security/public/index.ts index dc34fcbbe7d1e..336ec37d76a1b 100644 --- a/x-pack/plugins/security/public/index.ts +++ b/x-pack/plugins/security/public/index.ts @@ -6,7 +6,10 @@ import { PluginInitializer } from 'src/core/public'; import { SecurityPlugin, SecurityPluginSetup, SecurityPluginStart } from './plugin'; + +export { SecurityPluginSetup, SecurityPluginStart }; export { SessionInfo } from './types'; +export { AuthenticatedUser } from '../common/model'; export const plugin: PluginInitializer = () => new SecurityPlugin(); diff --git a/x-pack/plugins/security/public/mocks.ts b/x-pack/plugins/security/public/mocks.ts new file mode 100644 index 0000000000000..3c0c59d10abd1 --- /dev/null +++ b/x-pack/plugins/security/public/mocks.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { authenticationMock } from './authentication/index.mock'; +import { createSessionTimeoutMock } from './session/session_timeout.mock'; + +function createSetupMock() { + return { + authc: authenticationMock.createSetup(), + sessionTimeout: createSessionTimeoutMock(), + }; +} + +export const securityMock = { + createSetup: createSetupMock, +}; diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts index 3879d611d46eb..a9a89ee05f561 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts @@ -10,6 +10,8 @@ import { ILicense } from '../../../licensing/public'; import { SecurityNavControlService } from '.'; import { SecurityLicenseService } from '../../common/licensing'; import { nextTick } from 'test_utils/enzyme_helpers'; +import { securityMock } from '../mocks'; +import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock'; const validLicense = { isAvailable: true, @@ -29,13 +31,17 @@ describe('SecurityNavControlService', () => { const license$ = new BehaviorSubject(validLicense); const navControlService = new SecurityNavControlService(); + const mockSecuritySetup = securityMock.createSetup(); + mockSecuritySetup.authc.getCurrentUser.mockResolvedValue( + mockAuthenticatedUser({ username: 'some-user', full_name: undefined }) + ); navControlService.setup({ securityLicense: new SecurityLicenseService().setup({ license$ }).license, + authc: mockSecuritySetup.authc, }); const coreStart = coreMock.createStart(); coreStart.chrome.navControls.registerRight = jest.fn(); - coreStart.http.get.mockResolvedValue({ username: 'some-user' }); navControlService.start({ core: coreStart }); expect(coreStart.chrome.navControls.registerRight).toHaveBeenCalledTimes(1); @@ -93,6 +99,7 @@ describe('SecurityNavControlService', () => { const navControlService = new SecurityNavControlService(); navControlService.setup({ securityLicense: new SecurityLicenseService().setup({ license$ }).license, + authc: securityMock.createSetup().authc, }); const coreStart = coreMock.createStart(); @@ -111,6 +118,7 @@ describe('SecurityNavControlService', () => { const navControlService = new SecurityNavControlService(); navControlService.setup({ securityLicense: new SecurityLicenseService().setup({ license$ }).license, + authc: securityMock.createSetup().authc, }); const coreStart = coreMock.createStart(); @@ -126,6 +134,7 @@ describe('SecurityNavControlService', () => { const navControlService = new SecurityNavControlService(); navControlService.setup({ securityLicense: new SecurityLicenseService().setup({ license$ }).license, + authc: securityMock.createSetup().authc, }); const coreStart = coreMock.createStart(); @@ -146,6 +155,7 @@ describe('SecurityNavControlService', () => { const navControlService = new SecurityNavControlService(); navControlService.setup({ securityLicense: new SecurityLicenseService().setup({ license$ }).license, + authc: securityMock.createSetup().authc, }); const coreStart = coreMock.createStart(); diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx index aeeb84219c937..153e7112dc95b 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx @@ -9,11 +9,12 @@ import { CoreStart } from 'src/core/public'; import ReactDOM from 'react-dom'; import React from 'react'; import { SecurityLicense } from '../../common/licensing'; -import { AuthenticatedUser } from '../../common/model'; import { SecurityNavControl } from './nav_control_component'; +import { AuthenticationServiceSetup } from '../authentication'; interface SetupDeps { securityLicense: SecurityLicense; + authc: AuthenticationServiceSetup; } interface StartDeps { @@ -22,13 +23,15 @@ interface StartDeps { export class SecurityNavControlService { private securityLicense!: SecurityLicense; + private authc!: AuthenticationServiceSetup; private navControlRegistered!: boolean; private securityFeaturesSubscription?: Subscription; - public setup({ securityLicense }: SetupDeps) { + public setup({ securityLicense, authc }: SetupDeps) { this.securityLicense = securityLicense; + this.authc = authc; } public start({ core }: StartDeps) { @@ -38,14 +41,8 @@ export class SecurityNavControlService { const shouldRegisterNavControl = !isAnonymousPath && showLinks && !this.navControlRegistered; - if (shouldRegisterNavControl) { - const user = core.http.get('/internal/security/me', { - headers: { - 'kbn-system-api': true, - }, - }) as Promise; - this.registerSecurityNavControl(core, user); + this.registerSecurityNavControl(core); } } ); @@ -60,16 +57,16 @@ export class SecurityNavControlService { } private registerSecurityNavControl( - core: Pick, - user: Promise + core: Pick ) { + const currentUserPromise = this.authc.getCurrentUser(); core.chrome.navControls.registerRight({ order: 2000, mount: (el: HTMLElement) => { const I18nContext = core.i18n.Context; const props = { - user, + user: currentUserPromise, editProfileUrl: core.http.basePath.prepend('/app/kibana#/account'), logoutUrl: core.http.basePath.prepend(`/logout`), }; diff --git a/x-pack/plugins/security/public/plugin.ts b/x-pack/plugins/security/public/plugin.ts index 0f10f9d89f25a..50e0b838c750f 100644 --- a/x-pack/plugins/security/public/plugin.ts +++ b/x-pack/plugins/security/public/plugin.ts @@ -9,18 +9,20 @@ import { LicensingPluginSetup } from '../../licensing/public'; import { SessionExpired, SessionTimeout, + ISessionTimeout, SessionTimeoutHttpInterceptor, UnauthorizedResponseHttpInterceptor, } from './session'; import { SecurityLicenseService } from '../common/licensing'; import { SecurityNavControlService } from './nav_control'; +import { AuthenticationService } from './authentication'; export interface PluginSetupDependencies { licensing: LicensingPluginSetup; } export class SecurityPlugin implements Plugin { - private sessionTimeout!: SessionTimeout; + private sessionTimeout!: ISessionTimeout; private navControlService!: SecurityNavControlService; @@ -43,12 +45,15 @@ export class SecurityPlugin implements Plugin; private sessionInfo?: SessionInfo; private fetchTimer?: number; diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index b3f96497b0538..4f1c25702ae97 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -8,7 +8,6 @@ import crypto from 'crypto'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { schema, Type, TypeOf } from '@kbn/config-schema'; -import { duration } from 'moment'; import { PluginInitializerContext } from '../../../../src/core/server'; export type ConfigType = ReturnType extends Observable @@ -35,7 +34,6 @@ export const ConfigSchema = schema.object( schema.maybe(schema.string({ minLength: 32 })), schema.string({ minLength: 32, defaultValue: 'a'.repeat(32) }) ), - sessionTimeout: schema.maybe(schema.nullable(schema.number())), // DEPRECATED session: schema.object({ idleTimeout: schema.nullable(schema.duration()), lifespan: schema.nullable(schema.duration()), @@ -88,22 +86,11 @@ export function createConfig$(context: PluginInitializerContext, isTLSEnabled: b secureCookies = true; } - // "sessionTimeout" is deprecated and replaced with "session.idleTimeout" - // however, NP does not yet have a mechanism to automatically rename deprecated keys - // for the time being, we'll do it manually: - const deprecatedSessionTimeout = - typeof config.sessionTimeout === 'number' ? duration(config.sessionTimeout) : null; - const val = { + return { ...config, encryptionKey, secureCookies, - session: { - ...config.session, - idleTimeout: config.session.idleTimeout || deprecatedSessionTimeout, - }, }; - delete val.sessionTimeout; // DEPRECATED - return val; }) ); } diff --git a/x-pack/plugins/security/server/index.ts b/x-pack/plugins/security/server/index.ts index e189b71345ffc..33f554be5caa3 100644 --- a/x-pack/plugins/security/server/index.ts +++ b/x-pack/plugins/security/server/index.ts @@ -4,9 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext } from '../../../../src/core/server'; +import { TypeOf } from '@kbn/config-schema'; +import { + PluginConfigDescriptor, + PluginInitializer, + PluginInitializerContext, + RecursiveReadonly, +} from '../../../../src/core/server'; import { ConfigSchema } from './config'; -import { Plugin } from './plugin'; +import { Plugin, PluginSetupContract, PluginSetupDependencies } from './plugin'; // These exports are part of public Security plugin contract, any change in signature of exported // functions or removal of exports should be considered as a breaking change. @@ -17,8 +23,17 @@ export { InvalidateAPIKeyParams, InvalidateAPIKeyResult, } from './authentication'; -export { PluginSetupContract } from './plugin'; +export { PluginSetupContract }; -export const config = { schema: ConfigSchema }; -export const plugin = (initializerContext: PluginInitializerContext) => - new Plugin(initializerContext); +export const config: PluginConfigDescriptor> = { + schema: ConfigSchema, + deprecations: ({ rename, unused }) => [ + rename('sessionTimeout', 'session.idleTimeout'), + unused('authorization.legacyFallback.enabled'), + ], +}; +export const plugin: PluginInitializer< + RecursiveReadonly, + void, + PluginSetupDependencies +> = (initializerContext: PluginInitializerContext) => new Plugin(initializerContext); diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index cdd2a024310bb..9c4b01f94ef4d 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -110,10 +110,7 @@ export class Plugin { this.logger = this.initializerContext.logger.get(); } - public async setup( - core: CoreSetup, - { features, licensing }: PluginSetupDependencies - ): Promise> { + public async setup(core: CoreSetup, { features, licensing }: PluginSetupDependencies) { const [config, legacyConfig] = await combineLatest([ createConfig$(this.initializerContext, core.http.isTlsEnabled), this.initializerContext.config.legacy.globalConfig$, @@ -169,7 +166,7 @@ export class Plugin { csp: core.http.csp, }); - return deepFreeze({ + return deepFreeze({ authc, authz: { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index d957e451fdb74..af43110a8ba5e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1773,10 +1773,7 @@ "kbn.context.reloadPageDescription.selectValidAnchorDocumentTextMessage": "にアクセスして有効な別のドキュメントを選択してください。", "kbn.context.unableToLoadAnchorDocumentDescription": "別のドキュメントが読み込めません", "kbn.context.unableToLoadDocumentDescription": "ドキュメントが読み込めません", - "kbn.dashboard.addVisualizationDescription1": "上のメニューバーの ", - "kbn.dashboard.addVisualizationDescription2": " ボタンをクリックして、ダッシュボードにビジュアライゼーションを追加します。", "kbn.dashboard.addVisualizationLinkAriaLabel": "ビジュアライゼーションを追加", - "kbn.dashboard.addVisualizationLinkText": "追加", "kbn.dashboard.badge.readOnly.text": "読み込み専用", "kbn.dashboard.badge.readOnly.tooltip": "ダッシュボードを保存できません", "kbn.dashboard.changeViewModeConfirmModal.cancelButtonLabel": "編集を続行", @@ -6591,8 +6588,6 @@ "xpack.maps.source.wmsTitle": "ウェブマップサービス", "xpack.maps.style.heatmap.displayNameLabel": "ヒートマップスタイル", "xpack.maps.style.heatmap.resolutionStyleErrorMessage": "解像度パラメーターが認識されません: {resolution}", - "xpack.maps.styles.staticDynamic.dynamicDescription": "プロパティ値で特徴をシンボル化します。", - "xpack.maps.styles.staticDynamic.staticDescription": "静的スタイルプロパティで特徴をシンボル化します。", "xpack.maps.styles.vector.borderColorLabel": "境界線の色", "xpack.maps.styles.vector.borderWidthLabel": "境界線の幅", "xpack.maps.styles.vector.fillColorLabel": "塗りつぶす色", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 2e47c7a615e36..0306edcabd67d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1774,10 +1774,7 @@ "kbn.context.reloadPageDescription.selectValidAnchorDocumentTextMessage": "以选择有效地定位点文档。", "kbn.context.unableToLoadAnchorDocumentDescription": "无法加载该定位点文档", "kbn.context.unableToLoadDocumentDescription": "无法加载文档", - "kbn.dashboard.addVisualizationDescription1": "单击上述菜单栏中的 ", - "kbn.dashboard.addVisualizationDescription2": " 按钮,以将可视化添加到仪表板。", "kbn.dashboard.addVisualizationLinkAriaLabel": "添加可视化", - "kbn.dashboard.addVisualizationLinkText": "添加", "kbn.dashboard.badge.readOnly.text": "只读", "kbn.dashboard.badge.readOnly.tooltip": "无法保存仪表板", "kbn.dashboard.changeViewModeConfirmModal.cancelButtonLabel": "继续编辑", @@ -6593,8 +6590,6 @@ "xpack.maps.source.wmsTitle": "Web 地图服务", "xpack.maps.style.heatmap.displayNameLabel": "热图样式", "xpack.maps.style.heatmap.resolutionStyleErrorMessage": "无法识别分辨率参数:{resolution}", - "xpack.maps.styles.staticDynamic.dynamicDescription": "使用属性值代表功能。", - "xpack.maps.styles.staticDynamic.staticDescription": "使用静态样式属性代表功能。", "xpack.maps.styles.vector.borderColorLabel": "边框颜色", "xpack.maps.styles.vector.borderWidthLabel": "边框宽度", "xpack.maps.styles.vector.fillColorLabel": "填充颜色", diff --git a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts index 487f396d7a3dc..c47649544f9a7 100644 --- a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts +++ b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts @@ -17,7 +17,7 @@ export interface AlertUtilsOpts { objectRemover?: ObjectRemover; } -export interface CreateAlwaysFiringActionOpts { +export interface CreateAlertWithActionOpts { indexRecordActionId?: string; objectRemover?: ObjectRemover; overwrites?: Record; @@ -159,7 +159,7 @@ export class AlertUtils { overwrites = {}, indexRecordActionId, reference, - }: CreateAlwaysFiringActionOpts) { + }: CreateAlertWithActionOpts) { const objRemover = objectRemover || this.objectRemover; const actionId = indexRecordActionId || this.indexRecordActionId; @@ -207,4 +207,47 @@ export class AlertUtils { } return response; } + + public async createAlwaysFailingAction({ + objectRemover, + overwrites = {}, + indexRecordActionId, + reference, + }: CreateAlertWithActionOpts) { + const objRemover = objectRemover || this.objectRemover; + const actionId = indexRecordActionId || this.indexRecordActionId; + + if (!objRemover) { + throw new Error('objectRemover is required'); + } + if (!actionId) { + throw new Error('indexRecordActionId is required '); + } + + let request = this.supertestWithoutAuth + .post(`${getUrlPrefix(this.space.id)}/api/alert`) + .set('kbn-xsrf', 'foo'); + if (this.user) { + request = request.auth(this.user.username, this.user.password); + } + const response = await request.send({ + enabled: true, + name: 'fail', + schedule: { interval: '30s' }, + throttle: '30s', + tags: [], + alertTypeId: 'test.failing', + consumer: 'bar', + params: { + index: ES_TEST_INDEX_NAME, + reference, + }, + actions: [], + ...overwrites, + }); + if (response.statusCode === 200) { + objRemover.add(this.space.id, response.body.id, 'alert'); + } + return response; + } } diff --git a/x-pack/test/alerting_api_integration/common/lib/index.ts b/x-pack/test/alerting_api_integration/common/lib/index.ts index a2f21264634f8..c1e59664f9ce2 100644 --- a/x-pack/test/alerting_api_integration/common/lib/index.ts +++ b/x-pack/test/alerting_api_integration/common/lib/index.ts @@ -10,4 +10,5 @@ export { ES_TEST_INDEX_NAME, ESTestIndexTool } from './es_test_index_tool'; export { getTestAlertData } from './get_test_alert_data'; export { AlertUtils } from './alert_utils'; export { TaskManagerUtils } from './task_manager_utils'; +export * from './test_assertions'; export { checkAAD } from './check_aad'; diff --git a/x-pack/test/alerting_api_integration/common/lib/test_assertions.ts b/x-pack/test/alerting_api_integration/common/lib/test_assertions.ts new file mode 100644 index 0000000000000..9495dd4cfae82 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/lib/test_assertions.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; + +export function ensureDatetimeIsWithinRange( + date: number, + expectedDiff: number, + buffer: number = 10000 +) { + const diff = date - Date.now(); + expect(diff).to.be.greaterThan(expectedDiff - buffer); + expect(diff).to.be.lessThan(expectedDiff + buffer); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts index e89b54b1caa55..2a7e0b2203824 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts @@ -7,7 +7,13 @@ import expect from '@kbn/expect'; import { Response as SupertestResponse } from 'supertest'; import { UserAtSpaceScenarios } from '../../scenarios'; -import { checkAAD, getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { + checkAAD, + getUrlPrefix, + getTestAlertData, + ObjectRemover, + ensureDatetimeIsWithinRange, +} from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -406,10 +412,3 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { } }); } - -function ensureDatetimeIsWithinRange(scheduledRunTime: number, expectedDiff: number) { - const buffer = 10000; - const diff = scheduledRunTime - Date.now(); - expect(diff).to.be.greaterThan(expectedDiff - buffer); - expect(diff).to.be.lessThan(expectedDiff + buffer); -} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts.ts index 03e973194b4e2..032fee15882cf 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { Response as SupertestResponse } from 'supertest'; import { Spaces } from '../../scenarios'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { @@ -14,6 +15,7 @@ import { getTestAlertData, ObjectRemover, AlertUtils, + ensureDatetimeIsWithinRange, } from '../../../common/lib'; // eslint-disable-next-line import/no-default-export @@ -23,6 +25,13 @@ export default function alertTests({ getService }: FtrProviderContext) { const retry = getService('retry'); const esTestIndexTool = new ESTestIndexTool(es, retry); + function getAlertingTaskById(taskId: string) { + return supertestWithoutAuth + .get(`/api/alerting_tasks/${taskId}`) + .expect(200) + .then((response: SupertestResponse) => response.body); + } + describe('alerts', () => { let alertUtils: AlertUtils; let indexRecordActionId: string; @@ -100,6 +109,37 @@ export default function alertTests({ getService }: FtrProviderContext) { }); }); + it('should reschedule failing alerts using the alerting interval and not the Task Manager retry logic', async () => { + /* + Alerting does not use the Task Manager schedule and instead implements its own due to a current limitation + in TaskManager's ability to update an existing Task. + For this reason we need to handle the retry when Alert executors fail, as TaskManager doesn't understand that + alerting tasks are recurring tasks. + */ + const alertIntervalInSeconds = 30; + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFailingAction({ + reference, + overwrites: { schedule: { interval: `${alertIntervalInSeconds}s` } }, + }); + + expect(response.statusCode).to.eql(200); + + // wait for executor Alert Executor to be run, which means the underlying task is running + await esTestIndexTool.waitForDocs('alert:test.failing', reference); + + await retry.try(async () => { + const alertTask = (await getAlertingTaskById(response.body.scheduledTaskId)).docs[0]; + expect(alertTask.status).to.eql('idle'); + // ensure the alert is rescheduled to a minute from now + ensureDatetimeIsWithinRange( + Date.parse(alertTask.runAt), + alertIntervalInSeconds * 1000, + 5000 + ); + }); + }); + it('should handle custom retry logic', async () => { // We'll use this start time to query tasks created after this point const testStart = new Date(); diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index c7a9764c6fb58..aa6860b35763f 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -107,7 +107,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { shouldLoginIfPrompted: false, } ); - await testSubjects.existOrFail('emptyDashboardAddPanelButton', { timeout: 10000 }); + await testSubjects.existOrFail('emptyDashboardWidget', { timeout: 10000 }); }); it(`can view existing Dashboard`, async () => { diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts index 127141b156cd8..c1197fa7023c5 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts @@ -73,7 +73,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { shouldLoginIfPrompted: false, } ); - await testSubjects.existOrFail('emptyDashboardAddPanelButton', { timeout: 10000 }); + await testSubjects.existOrFail('emptyDashboardWidget', { timeout: 10000 }); }); it(`can view existing Dashboard`, async () => { diff --git a/x-pack/test/functional/apps/infra/link_to.ts b/x-pack/test/functional/apps/infra/link_to.ts index a68637600be8b..d1ae7138ecc65 100644 --- a/x-pack/test/functional/apps/infra/link_to.ts +++ b/x-pack/test/functional/apps/infra/link_to.ts @@ -23,7 +23,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }; const expectedSearchString = "logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)&logPosition=(position:(tiebreaker:0,time:1565707203194))&sourceId=default"; - const expectedRedirect = `/logs/stream?${expectedSearchString}`; + const expectedRedirectPath = '/logs/stream?'; await pageObjects.common.navigateToActualUrl( 'infraOps', @@ -32,7 +32,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await retry.tryForTime(5000, async () => { const currentUrl = await browser.getCurrentUrl(); const [, currentHash] = decodeURIComponent(currentUrl).split('#'); - expect(currentHash).to.contain(expectedRedirect); + // Account for unpredictable location of the g parameter in the search string + expect(currentHash.slice(0, expectedRedirectPath.length)).to.be(expectedRedirectPath); + expect(currentHash.slice(expectedRedirectPath.length)).to.contain(expectedSearchString); }); }); }); diff --git a/x-pack/test/licensing_plugin/public/updates.ts b/x-pack/test/licensing_plugin/public/updates.ts index 6d2253fe83868..2e750dcea1a59 100644 --- a/x-pack/test/licensing_plugin/public/updates.ts +++ b/x-pack/test/licensing_plugin/public/updates.ts @@ -32,7 +32,7 @@ export default function(ftrContext: FtrProviderContext) { // this call enforces signature check to detect license update // and causes license re-fetch await setup.core.http.get('/'); - await testUtils.delay(100); + await testUtils.delay(500); const licensing: LicensingPluginSetup = setup.plugins.licensing; licensing.license$.subscribe(license => cb(license.type)); @@ -48,7 +48,7 @@ export default function(ftrContext: FtrProviderContext) { // this call enforces signature check to detect license update // and causes license re-fetch await setup.core.http.get('/'); - await testUtils.delay(100); + await testUtils.delay(500); const licensing: LicensingPluginSetup = setup.plugins.licensing; licensing.license$.subscribe(license => cb(license.type)); @@ -64,7 +64,7 @@ export default function(ftrContext: FtrProviderContext) { // this call enforces signature check to detect license update // and causes license re-fetch await setup.core.http.get('/'); - await testUtils.delay(100); + await testUtils.delay(500); const licensing: LicensingPluginSetup = setup.plugins.licensing; licensing.license$.subscribe(license => cb(license.type)); @@ -80,7 +80,7 @@ export default function(ftrContext: FtrProviderContext) { // this call enforces signature check to detect license update // and causes license re-fetch await setup.core.http.get('/'); - await testUtils.delay(100); + await testUtils.delay(500); const licensing: LicensingPluginSetup = setup.plugins.licensing; licensing.license$.subscribe(license => cb(license.type)); diff --git a/yarn.lock b/yarn.lock index 58578bf7890e4..0026370927fe1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1361,10 +1361,10 @@ dependencies: "@elastic/apm-rum-core" "^4.7.0" -"@elastic/charts@^14.0.0": - version "14.0.0" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-14.0.0.tgz#410c87e9ae53df5848aae09a210fa7d08510b376" - integrity sha512-cskrD5Yq6yTTqGOKV2/7dw/eRON1GbWkIgSuWXPIBa/TQMUwiWqxFkxSMUJSbu9xXq07KMblDgXLf73yMc0AyQ== +"@elastic/charts@^16.0.2": + version "16.0.2" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-16.0.2.tgz#35068a08a19534da62e9bcad700cc7b2a15bc55a" + integrity sha512-0tVyltAmAPOAfiRU1iKYk3b9++4oTn6IXvyM4SSj7Ukh5Y90XXmOtGEUPnZTiRPmup9MJi4srrm9ra9k/Kq4UQ== dependencies: "@types/d3-shape" "^1.3.1" classnames "^2.2.6" @@ -1372,18 +1372,18 @@ d3-collection "^1.0.7" d3-scale "^1.0.7" d3-shape "^1.3.4" - fp-ts "^1.14.2" - konva "^2.6.0" - mobx "^4.9.2" - mobx-react "^5.4.3" + konva "^4.0.18" newtype-ts "^0.2.4" prop-types "^15.7.2" - react "^16.8.3" - react-dom "^16.8.3" - react-konva "16.8.3" + re-reselect "^3.4.0" + react-konva "16.10.1-0" + react-redux "^7.1.0" react-spring "^8.0.8" + redux "^4.0.4" + reselect "^4.0.0" resize-observer-polyfill "^1.5.1" ts-debounce "^1.0.0" + utility-types "^3.9.0" uuid "^3.3.2" "@elastic/elasticsearch@^7.4.0": @@ -2246,64 +2246,61 @@ utils-error-reviver "^1.0.0" utils-error-to-json "^1.0.0" -"@microsoft/api-documenter@7.4.3": - version "7.4.3" - resolved "https://registry.yarnpkg.com/@microsoft/api-documenter/-/api-documenter-7.4.3.tgz#af5e69891c4b62e963a697127b69a25396d0123d" - integrity sha512-+RdqNNt9ssUCBAsLvKVPzbRD5RYd0HRgmnhRuEr+eZg2tj2tqfP9AhLWK5SBVn68CX5hyeWHEnQJ4HikDTZN8A== +"@microsoft/api-documenter@7.7.2": + version "7.7.2" + resolved "https://registry.yarnpkg.com/@microsoft/api-documenter/-/api-documenter-7.7.2.tgz#b6897f052ad447d6bb74f806287e8846c64691da" + integrity sha512-4mWE5G3grYd4PX5D6awiKa3B3GOXumkyGspgeTwlOBxrmj0FuVFRNPVZxGU0NqYnaw/bW4cg4ftUnSDzycrW+A== dependencies: - "@microsoft/api-extractor-model" "7.4.1" - "@microsoft/node-core-library" "3.14.2" - "@microsoft/ts-command-line" "4.2.8" + "@microsoft/api-extractor-model" "7.7.0" + "@microsoft/node-core-library" "3.18.0" + "@microsoft/ts-command-line" "4.3.5" "@microsoft/tsdoc" "0.12.14" colors "~1.2.1" js-yaml "~3.13.1" resolve "1.8.1" -"@microsoft/api-extractor-model@7.4.1": - version "7.4.1" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.4.1.tgz#3376f72570d336960c9b7b0dd44c8a0dbbe34604" - integrity sha512-rBO/QbrOMCdL8e9qwhIu1aH4C5sKOnUO1YhEh3+kVieFzTjiRnync7ghyQOtCaCVl2VXtp4LuOIv02e82oRqUg== +"@microsoft/api-extractor-model@7.7.0": + version "7.7.0" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.7.0.tgz#a5e86a638fa3fea283aeebc4785d8150652f30c6" + integrity sha512-9yrSr9LpdNnx7X8bXVb/YbcQopizsr43McAG7Xno5CMNFzbSkmIr8FJL0L+WGfrSWSTms9Bngfz7d1ScP6zbWQ== dependencies: - "@microsoft/node-core-library" "3.14.2" + "@microsoft/node-core-library" "3.18.0" "@microsoft/tsdoc" "0.12.14" - "@types/node" "8.5.8" -"@microsoft/api-extractor@7.4.2": - version "7.4.2" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.4.2.tgz#440023cf05c69840e054cdb5f85cab9680227a40" - integrity sha512-O8OEaFvsvWEuwkOcVyWegIAFDY6TBZBvSIoOKLsSQYiQZtryGf13e2ym83iewhbUN7RmuOJtyQUKlBvcJbpgQA== +"@microsoft/api-extractor@7.7.0": + version "7.7.0" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.7.0.tgz#1550a5b88ca927d57e9c9698356a2f9375c5984c" + integrity sha512-1ngy95VA1s7GTE+bkS7QoYTg/TZs54CdJ46uAhl6HlyDJut4p/aH46W70g2XQs9VniIymW1Qe6fqNmcQUx5CVg== dependencies: - "@microsoft/api-extractor-model" "7.4.1" - "@microsoft/node-core-library" "3.14.2" - "@microsoft/ts-command-line" "4.2.8" + "@microsoft/api-extractor-model" "7.7.0" + "@microsoft/node-core-library" "3.18.0" + "@microsoft/ts-command-line" "4.3.5" "@microsoft/tsdoc" "0.12.14" colors "~1.2.1" lodash "~4.17.15" resolve "1.8.1" source-map "~0.6.1" - typescript "~3.5.3" + typescript "~3.7.2" -"@microsoft/node-core-library@3.14.2": - version "3.14.2" - resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.14.2.tgz#255d421963f2d447a19f935e3c8eb3053e8e381b" - integrity sha512-bd8XhqhIvXsWg/SSNsZJdJxkN8Ucj7XKQkRe4cdYiKqpVdAREvW/shw8AoZIdgvjLI53029I/MO2Wn/AjGD3Jw== +"@microsoft/node-core-library@3.18.0": + version "3.18.0" + resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.18.0.tgz#9a9123354b3e067bb8a975ba791959ffee1322ed" + integrity sha512-VzzSHtcwgHVW1xbHqpngfn+OS1trAZ1Tw3XXBlMsEKe7Wz7FF2gLr0hZa6x9Pemk5pkd4tu4+GTSOJjCKGjrgg== dependencies: - "@types/fs-extra" "5.0.4" - "@types/jju" "~1.4.0" - "@types/node" "8.5.8" - "@types/z-schema" "3.16.31" + "@types/node" "8.10.54" colors "~1.2.1" fs-extra "~7.0.1" jju "~1.4.0" + semver "~5.3.0" + timsort "~0.3.0" z-schema "~3.18.3" -"@microsoft/ts-command-line@4.2.8": - version "4.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/ts-command-line/-/ts-command-line-4.2.8.tgz#92f4c85d0a4b893090fe6605f255e272b270495e" - integrity sha512-K4sc8/OJ/y5uQPWJFACMExS2UIqF+t3vdQ2A9Mhl9tMsp70CXf0sp6Y9ENYju1K7XWwR5Clh8dkP2jO1Ntlg1g== +"@microsoft/ts-command-line@4.3.5": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@microsoft/ts-command-line/-/ts-command-line-4.3.5.tgz#78026d20244f39978d3397849ac8c40c0c2d4079" + integrity sha512-CN3j86apNOmllUmeJ0AyRfTYA2BP2xlnfgmnyp1HWLqcJmR/zLe/fk/+gohGnNt7o5/qHta3681LQhO2Yy3GFw== dependencies: "@types/argparse" "1.0.33" - "@types/node" "8.5.8" argparse "~1.0.9" colors "~1.2.1" @@ -3498,13 +3495,6 @@ resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.0.tgz#cbb49815a5e1129d5f23836a98d65d93822409af" integrity sha512-dxdRrUov2HVTbSRFX+7xwUPlbGYVEZK6PrSqClg2QPos3PNe0bCajkDDkDeeC1znjSH03KOEqVbXpnJuWa2wgQ== -"@types/fs-extra@5.0.4": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.4.tgz#b971134d162cc0497d221adde3dbb67502225599" - integrity sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g== - dependencies: - "@types/node" "*" - "@types/geojson@*": version "7946.0.7" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.7.tgz#c8fa532b60a0042219cdf173ca21a975ef0666ad" @@ -3703,11 +3693,6 @@ dependencies: "@types/jest-diff" "*" -"@types/jju@~1.4.0": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@types/jju/-/jju-1.4.1.tgz#0a39f5f8e84fec46150a7b9ca985c3f89ad98e9f" - integrity sha512-LFt+YA7Lv2IZROMwokZKiPNORAV5N3huMs3IKnzlE430HWhWYZ8b+78HiwJXJJP1V2IEjinyJURuRJfGoaFSIA== - "@types/joi@*", "@types/joi@^13.4.2": version "13.6.1" resolved "https://registry.yarnpkg.com/@types/joi/-/joi-13.6.1.tgz#325486a397504f8e22c8c551dc8b0e1d41d5d5ae" @@ -3904,7 +3889,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@10.12.27", "@types/node@8.5.8", "@types/node@>=8.9.0", "@types/node@^10.12.27", "@types/node@^12.0.2": +"@types/node@*", "@types/node@10.12.27", "@types/node@8.10.54", "@types/node@>=8.9.0", "@types/node@^10.12.27", "@types/node@^12.0.2": version "10.12.27" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.27.tgz#eb3843f15d0ba0986cc7e4d734d2ee8b50709ef8" integrity sha512-e9wgeY6gaY21on3ve0xAjgBVjGDWq/xUteK0ujsE53bUoxycMkqfnkUgMt6ffZtykZ5X12Mg3T7Pw4TRCObDKg== @@ -4444,11 +4429,6 @@ dependencies: "@types/yargs-parser" "*" -"@types/z-schema@3.16.31": - version "3.16.31" - resolved "https://registry.yarnpkg.com/@types/z-schema/-/z-schema-3.16.31.tgz#2eb1d00a5e4ec3fa58c76afde12e182b66dc5c1c" - integrity sha1-LrHQCl5Ow/pYx2r94S4YK2bcXBw= - "@types/zen-observable@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" @@ -4843,7 +4823,7 @@ acorn-walk@^7.0.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.0.0.tgz#c8ba6f0f1aac4b0a9e32d1f0af12be769528f36b" integrity sha512-7Bv1We7ZGuU79zZbb6rRqcpxo3OY+zrdtloZWoyD8fmGX+FeXRjE+iuGkZjSXLVovLzrsvMGMy0EkwA0E0umxg== -acorn@5.X, acorn@^5.0.0, acorn@^5.0.3, acorn@^5.1.2, acorn@^5.2.1, acorn@^5.5.0: +acorn@5.X, acorn@^5.0.0, acorn@^5.0.3, acorn@^5.1.2, acorn@^5.5.0: version "5.7.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== @@ -5600,14 +5580,7 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" -argparse@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" - integrity sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY= - dependencies: - sprintf-js "~1.0.2" - -argparse@~1.0.9: +argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -6690,11 +6663,6 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base62@^1.1.0: - version "1.2.8" - resolved "https://registry.yarnpkg.com/base62/-/base62-1.2.8.tgz#1264cb0fb848d875792877479dbe8bae6bae3428" - integrity sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA== - base64-arraybuffer@0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" @@ -8553,12 +8521,12 @@ commander@3.0.2: resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== -commander@^2.13.0, commander@^2.15.1, commander@^2.16.0, commander@^2.19.0, commander@^2.20.0, commander@^2.7.1: +commander@^2.13.0, commander@^2.15.1, commander@^2.16.0, commander@^2.19.0, commander@^2.20.0: version "2.20.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== -commander@^2.5.0: +commander@^2.7.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -8590,21 +8558,6 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -commoner@^0.10.1: - version "0.10.8" - resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5" - integrity sha1-NPw2cs0kOT6LtH5wyqApOBH08sU= - dependencies: - commander "^2.5.0" - detective "^4.3.1" - glob "^5.0.15" - graceful-fs "^4.1.2" - iconv-lite "^0.4.5" - mkdirp "^0.5.0" - private "^0.1.6" - q "^1.1.2" - recast "^0.11.17" - compare-versions@3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.5.1.tgz#26e1f5cf0d48a77eced5046b9f67b6b61075a393" @@ -10174,7 +10127,7 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -defined@^1.0.0, defined@~1.0.0: +defined@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= @@ -10423,14 +10376,6 @@ detective-typescript@^5.1.1: node-source-walk "^4.2.0" typescript "^3.4.5" -detective@^4.3.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" - integrity sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig== - dependencies: - acorn "^5.2.1" - defined "^1.0.0" - dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -11137,14 +11082,6 @@ env-variable@0.0.x: resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.5.tgz#913dd830bef11e96a039c038d4130604eba37f88" integrity sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA== -envify@^3.0.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/envify/-/envify-3.4.1.tgz#d7122329e8df1688ba771b12501917c9ce5cbce8" - integrity sha1-1xIjKejfFoi6dxsSUBkXyc5cvOg= - dependencies: - jstransform "^11.0.3" - through "~2.3.4" - enzyme-adapter-react-16@^1.15.1: version "1.15.1" resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.1.tgz#8ad55332be7091dc53a25d7d38b3485fc2ba50d5" @@ -11822,11 +11759,6 @@ espree@^6.1.1: acorn-jsx "^5.0.2" eslint-visitor-keys "^1.1.0" -esprima-fb@^15001.1.0-dev-harmony-fb: - version "15001.1.0-dev-harmony-fb" - resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz#30a947303c6b8d5e955bee2b99b1d233206a6901" - integrity sha1-MKlHMDxrjV6VW+4rmbHSMyBqaQE= - esprima@2.7.x, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -11837,21 +11769,16 @@ esprima@^3.1.3, esprima@~3.1.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= -esprima@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" - integrity sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw== +esprima@^4.0.0, esprima@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esprima@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" integrity sha1-n1V+CPw7TSbs6d00+Pv0drYlha0= -esprima@~4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - esquery@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" @@ -12518,17 +12445,6 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -fbjs@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.6.1.tgz#9636b7705f5ba9684d44b72f78321254afc860f7" - integrity sha1-lja3cF9bqWhNRLcveDISVK/IYPc= - dependencies: - core-js "^1.0.0" - loose-envify "^1.0.0" - promise "^7.0.3" - ua-parser-js "^0.7.9" - whatwg-fetch "^0.9.0" - fbjs@^0.8.0, fbjs@^0.8.1, fbjs@^0.8.16: version "0.8.17" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" @@ -13183,15 +13099,10 @@ fp-ts@^1.0.0: resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.12.0.tgz#d333310e4ac104cdcb6bea47908e381bb09978e7" integrity sha512-fWwnAgVlTsV26Ruo9nx+fxNHIm6l1puE1VJ/C0XJ3nRQJJJIgRHYw6sigB3MuNFZL1o4fpGlhwFhcbxHK0RsOA== -fp-ts@^1.14.2: - version "1.17.3" - resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.17.3.tgz#5064afc4bee8ddcaea567479bfc62d527e015825" - integrity sha512-r4gHfAWaRrYPsmdzRl1U9CkpbdOi8fPg5F5KiazAadENz5DKdWEaCDPl2Tf92fvkZGD/ekZ3EHu3gtXIVcsXtA== - -fp-ts@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.0.5.tgz#9560d8a6a4f53cbda9f9b31ed8d1458e41939e07" - integrity sha512-opI5r+rVlpZE7Rhk0YtqsrmxGkbIw0dRNqGca8FEAMMnjomXotG+R9QkLQg20onx7R8qhepAn4CCOP8usma/Xw== +fp-ts@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.3.1.tgz#8068bfcca118227932941101e062134d7ecd9119" + integrity sha512-KevPBnYt0aaJiuUzmU9YIxjrhC9AgJ8CLtLlXmwArovlNTeYM5NtEoKd86B0wHd7FIbzeE8sNXzCoYIOr7e6Iw== fragment-cache@^0.2.1: version "0.2.1" @@ -14180,7 +14091,7 @@ got@^8.3.1, got@^8.3.2: url-parse-lax "^3.0.0" url-to-options "^1.0.1" -graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6: +graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.4: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= @@ -14190,6 +14101,11 @@ graceful-fs@^4.1.15, graceful-fs@^4.1.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== +graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== + graceful-fs@^4.2.0, graceful-fs@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" @@ -15077,13 +14993,6 @@ hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react- resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" - integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== - dependencies: - react-is "^16.7.0" - hoist-non-react-statics@^3.1.0: version "3.3.1" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#101685d3aff3b23ea213163f6e8e12f4f111e19f" @@ -15091,6 +15000,13 @@ hoist-non-react-statics@^3.1.0: dependencies: react-is "^16.7.0" +hoist-non-react-statics@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" + integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== + dependencies: + react-is "^16.7.0" + homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" @@ -15394,7 +15310,7 @@ icalendar@0.7.1: resolved "https://registry.yarnpkg.com/icalendar/-/icalendar-0.7.1.tgz#d0d3486795f8f1c5cf4f8cafac081b4b4e7a32ae" integrity sha1-0NNIZ5X48cXPT4yvrAgbS056Mq4= -iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.22, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@^0.4.5, iconv-lite@~0.4.13: +iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.22, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -17654,17 +17570,6 @@ jssha@^2.1.0: resolved "https://registry.yarnpkg.com/jssha/-/jssha-2.3.1.tgz#147b2125369035ca4b2f7d210dc539f009b3de9a" integrity sha1-FHshJTaQNcpLL30hDcU58Amz3po= -jstransform@^11.0.3: - version "11.0.3" - resolved "https://registry.yarnpkg.com/jstransform/-/jstransform-11.0.3.tgz#09a78993e0ae4d4ef4487f6155a91f6190cb4223" - integrity sha1-CaeJk+CuTU70SH9hVakfYZDLQiM= - dependencies: - base62 "^1.1.0" - commoner "^0.10.1" - esprima-fb "^15001.1.0-dev-harmony-fb" - object-assign "^2.0.0" - source-map "^0.4.2" - jstransformer-ejs@^0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/jstransformer-ejs/-/jstransformer-ejs-0.0.3.tgz#04d9201469274fcf260f1e7efd732d487fa234b6" @@ -17933,10 +17838,10 @@ known-css-properties@^0.3.0: resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.3.0.tgz#a3d135bbfc60ee8c6eacf2f7e7e6f2d4755e49a4" integrity sha512-QMQcnKAiQccfQTqtBh/qwquGZ2XK/DXND1jrcN9M8gMMy99Gwla7GQjndVUsEqIaRyP6bsFRuhwRj5poafBGJQ== -konva@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/konva/-/konva-2.6.0.tgz#43165b95e32a4378ce532d9113c914f4998409c3" - integrity sha512-LCOoavICTD9PYoAqtWo8sbxYtCiXdgEeY7vj/Sq8b2bwFmrQr9Ak0RkD4/jxAf5fcUQRL5e1zPLyfRpVndp20A== +konva@^4.0.18: + version "4.0.18" + resolved "https://registry.yarnpkg.com/konva/-/konva-4.0.18.tgz#43e614c9b22827183506d4a6b3b474f90187b469" + integrity sha512-Tlq0v7QHr8q73xr1cKjHdQl41oHC06IOldPO+ukjt99G74NgoU0TVouvPIFpW2whA9t3xNk/+/VJcc3XPcboOw== kopy@^8.2.0: version "8.2.5" @@ -19784,19 +19689,6 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi dependencies: minimist "0.0.8" -mobx-react@^5.4.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-5.4.3.tgz#6709b7dd89670c40e9815914ac2ca49cc02bfb47" - integrity sha512-WC8yFlwvJ91hy8j6CrydAuFteUafcuvdITFQeHl3LRIf5ayfT/4W3M/byhEYD2BcJWejeXr8y4Rh2H26RunCRQ== - dependencies: - hoist-non-react-statics "^3.0.0" - react-lifecycles-compat "^3.0.2" - -mobx@^4.9.2: - version "4.9.4" - resolved "https://registry.yarnpkg.com/mobx/-/mobx-4.9.4.tgz#bb37a0e4e05f0b02be89ced9d23445cad73377ad" - integrity sha512-RaEpydw7D1ebp1pdFHrEMZcLk4nALAZyHAroCPQpqLzuIXIxJpLmMIe5PUZwYHqvlcWL6DVqDYCANZpPOi9iXA== - mocha-junit-reporter@^1.23.1: version "1.23.1" resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.1.tgz#ba11519c0b967f404e4123dd69bc4ba022ab0f12" @@ -21785,12 +21677,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.0.tgz#99a10d870a803bdd5ee6f0470e58dfcd2f9a54d3" integrity sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg== -path-parse@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" - integrity sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME= - -path-parse@^1.0.6: +path-parse@^1.0.5, path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== @@ -22469,7 +22356,7 @@ promise.prototype.finally@^3.1.0: es-abstract "^1.9.0" function-bind "^1.1.1" -promise@^7.0.1, promise@^7.0.3, promise@^7.1.1: +promise@^7.0.1, promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== @@ -23063,6 +22950,11 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +re-reselect@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/re-reselect/-/re-reselect-3.4.0.tgz#0f2303f3c84394f57f0cd31fea08a1ca4840a7cd" + integrity sha512-JsecfN+JlckncVXTWFWjn0Vk6uInl8GSf4eEd9tTk5qXHlgqkPdILpnYpgZcISXNYAzvfvsCZviaDk8AxyS5sg== + react-ace@^5.5.0: version "5.10.0" resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-5.10.0.tgz#e328b37ac52759f700be5afdb86ada2f5ec84c5e" @@ -23433,20 +23325,20 @@ react-is@~16.3.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22" integrity sha512-ybEM7YOr4yBgFd6w8dJqwxegqZGJNBZl6U27HnGKuTZmDvVrD5quWOK/wAnMywiZzW+Qsk+l4X2c70+thp/A8Q== -react-konva@16.8.3: - version "16.8.3" - resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-16.8.3.tgz#e55390040ea54675a0ef0d40b4fa93731e6d7b03" - integrity sha512-gU36TBxcPZANQOV5prAFnpRSNp2ikAT7zCICHTBJvOzAfa8Yhcyaey6EIrD+NTT/4y0PyGFBIkmWq6zdrlNrQg== +react-konva@16.10.1-0: + version "16.10.1-0" + resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-16.10.1-0.tgz#f8cc2c95374933069e891a6c714c70d0fdc77e68" + integrity sha512-N0Zi3TcWmUxb2d7y1DUDQhRA+WIcqk54DQmmUmJSadj+fS0bg6iZDebQSEQC8dMbjnLHc/338xRT4a4718PEiw== dependencies: - react-reconciler "^0.20.1" - scheduler "^0.13.3" + react-reconciler "^0.22.1" + scheduler "^0.16.1" react-lib-adler32@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/react-lib-adler32/-/react-lib-adler32-1.0.3.tgz#63df1aed274eabcc1c5067077ea281ec30888ba7" integrity sha512-AqFqdt4cP0RPffHNjVHZ7tyIgnoSzNxgFhG8XKMXCtA1dZ72gTPO4iYFwWDKHqvE8sHS14rhltQTdbXU5G4BFA== -react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -23555,15 +23447,15 @@ react-portal@^3.2.0: dependencies: prop-types "^15.5.8" -react-reconciler@^0.20.1: - version "0.20.4" - resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.20.4.tgz#3da6a95841592f849cb4edd3d38676c86fd920b2" - integrity sha512-kxERc4H32zV2lXMg/iMiwQHOtyqf15qojvkcZ5Ja2CPkjVohHw9k70pdDBwrnQhLVetUJBSYyqU3yqrlVTOajA== +react-reconciler@^0.22.1: + version "0.22.2" + resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.22.2.tgz#e8a10374fec8fee7c5cd0cf3cd05626f1b134d3e" + integrity sha512-MLX5Y2pNLsdXzWz/GLNhhYkdLOvxEtw2IGqVCzkiRdSFSHRjujI9gfTOQ3rV5z8toTBxSZ2qrRkRUo97mmEdhA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.6" + scheduler "^0.16.2" react-redux@^5.0.7: version "5.0.7" @@ -23590,7 +23482,7 @@ react-redux@^5.1.2: react-is "^16.6.0" react-lifecycles-compat "^3.0.0" -react-redux@^7.1.1: +react-redux@^7.1.0, react-redux@^7.1.1: version "7.1.3" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.3.tgz#717a3d7bbe3a1b2d535c94885ce04cdc5a33fc79" integrity sha512-uI1wca+ECG9RoVkWQFF4jDMqmaw0/qnvaSvOoL/GA4dNxf6LoV8sUAcNDvE5NWKs4hFpn0t6wswNQnY3f7HT3w== @@ -23852,15 +23744,7 @@ react-visibility-sensor@^5.1.1: dependencies: prop-types "^15.7.2" -react@^0.14.0: - version "0.14.9" - resolved "https://registry.yarnpkg.com/react/-/react-0.14.9.tgz#9110a6497c49d44ba1c0edd317aec29c2e0d91d1" - integrity sha1-kRCmSXxJ1EuhwO3TF67CnC4NkdE= - dependencies: - envify "^3.0.0" - fbjs "^0.6.1" - -react@^16.12.0: +react@^0.14.0, react@^16.12.0, react@^16.8.3, react@^16.8.5: version "16.12.0" resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA== @@ -23869,16 +23753,6 @@ react@^16.12.0: object-assign "^4.1.1" prop-types "^15.6.2" -react@^16.8.3, react@^16.8.5: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" - integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.13.6" - reactcss@1.2.3, reactcss@^1.2.0: version "1.2.3" resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd" @@ -24111,16 +23985,6 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" -recast@^0.11.17, recast@~0.11.12: - version "0.11.23" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" - integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM= - dependencies: - ast-types "0.9.6" - esprima "~3.1.0" - private "~0.1.5" - source-map "~0.5.0" - recast@^0.14.7: version "0.14.7" resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" @@ -24141,6 +24005,16 @@ recast@^0.17.3: private "^0.1.8" source-map "~0.6.1" +recast@~0.11.12: + version "0.11.23" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" + integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM= + dependencies: + ast-types "0.9.6" + esprima "~3.1.0" + private "~0.1.5" + source-map "~0.5.0" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -24831,6 +24705,11 @@ reselect@3.0.1, reselect@^3.0.1: resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc= +reselect@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" + integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA== + resize-observer-polyfill@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.0.tgz#660ff1d9712a2382baa2cad450a4716209f9ca69" @@ -25399,10 +25278,10 @@ saxes@^3.1.9: dependencies: xmlchars "^2.1.1" -scheduler@^0.13.3, scheduler@^0.13.6: - version "0.13.6" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" - integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ== +scheduler@^0.16.1, scheduler@^0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.16.2.tgz#f74cd9d33eff6fc554edfb79864868e4819132c1" + integrity sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -27663,6 +27542,11 @@ timm@^1.6.1: resolved "https://registry.yarnpkg.com/timm/-/timm-1.6.1.tgz#5f8aafc932248c76caf2c6af60542a32d3c30701" integrity sha512-hqDTYi/bWuDxL2i6T3v6nrvkAQ/1Bc060GSkVEQZp02zTSTB4CHSKsOkliequCftQaNRcjRqUZmpGWs5FfhrNg== +timsort@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + tiny-emitter@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c" @@ -28657,7 +28541,7 @@ typescript-fsa@^2.0.0, typescript-fsa@^2.5.0: resolved "https://registry.yarnpkg.com/typescript-fsa/-/typescript-fsa-2.5.0.tgz#1baec01b5e8f5f34c322679d1327016e9e294faf" integrity sha1-G67AG16PXzTDImedEycBbp4pT68= -typescript@3.5.3, typescript@3.7.2, typescript@^3.0.1, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.5.3: +typescript@3.5.3, typescript@3.7.2, typescript@^3.0.1, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== @@ -28956,9 +28840,9 @@ universal-user-agent@^2.0.0, universal-user-agent@^2.0.1: os-name "^3.0.0" universalify@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" - integrity sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc= + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== unlazy-loader@^0.1.3: version "0.1.3" @@ -29227,7 +29111,7 @@ utila@^0.4.0, utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= -utility-types@^3.10.0: +utility-types@^3.10.0, utility-types@^3.9.0: version "3.10.0" resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== @@ -30324,11 +30208,6 @@ whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0: resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== -whatwg-fetch@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz#0e3684c6cb9995b43efc9df03e4c365d95fd9cc0" - integrity sha1-DjaExsuZlbQ+/J3wPkw2XZX9nMA= - whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"