diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index 020ec8018b85b4..56bd4abbc80236 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -23,7 +23,7 @@ experimental[] Import dashboards and corresponding saved objects. [[dashboard-api-import-request-body]] ==== Request body -Use the complete response body from the <> as the request body. Do not manually construct a payload to the endpoint. +Use the complete response body from the <> as the request body. Do not manually construct a payload to the endpoint. The max payload size is determined by the `savedObjects.maxImportPayloadBytes` configuration key. [[dashboard-api-import-response-body]] ==== Response body diff --git a/docs/development/core/public/kibana-plugin-core-public.environmentmode.dev.md b/docs/development/core/public/kibana-plugin-core-public.environmentmode.dev.md deleted file mode 100644 index 68ea11c62cf96d..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.environmentmode.dev.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) > [dev](./kibana-plugin-core-public.environmentmode.dev.md) - -## EnvironmentMode.dev property - -Signature: - -```typescript -dev: boolean; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.environmentmode.md b/docs/development/core/public/kibana-plugin-core-public.environmentmode.md deleted file mode 100644 index 34ca3a00005638..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.environmentmode.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) - -## EnvironmentMode interface - - -Signature: - -```typescript -export interface EnvironmentMode -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [dev](./kibana-plugin-core-public.environmentmode.dev.md) | boolean | | -| [name](./kibana-plugin-core-public.environmentmode.name.md) | 'development' | 'production' | | -| [prod](./kibana-plugin-core-public.environmentmode.prod.md) | boolean | | - diff --git a/docs/development/core/public/kibana-plugin-core-public.environmentmode.name.md b/docs/development/core/public/kibana-plugin-core-public.environmentmode.name.md deleted file mode 100644 index 2d1722a82417c0..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.environmentmode.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) > [name](./kibana-plugin-core-public.environmentmode.name.md) - -## EnvironmentMode.name property - -Signature: - -```typescript -name: 'development' | 'production'; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.environmentmode.prod.md b/docs/development/core/public/kibana-plugin-core-public.environmentmode.prod.md deleted file mode 100644 index 5145f8f6887e50..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.environmentmode.prod.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) > [prod](./kibana-plugin-core-public.environmentmode.prod.md) - -## EnvironmentMode.prod property - -Signature: - -```typescript -prod: boolean; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index f2bf72a597656c..85ef00d2714159 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -55,7 +55,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [CoreSetup](./kibana-plugin-core-public.coresetup.md) | Core services exposed to the Plugin setup lifecycle | | [CoreStart](./kibana-plugin-core-public.corestart.md) | Core services exposed to the Plugin start lifecycle | | [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) | | -| [EnvironmentMode](./kibana-plugin-core-public.environmentmode.md) | | | [ErrorToastOptions](./kibana-plugin-core-public.errortoastoptions.md) | Options available for [IToasts](./kibana-plugin-core-public.itoasts.md) error APIs. | | [FatalErrorInfo](./kibana-plugin-core-public.fatalerrorinfo.md) | Represents the message and stack of a fatal Error | | [FatalErrorsSetup](./kibana-plugin-core-public.fatalerrorssetup.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. | @@ -85,7 +84,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [OverlayBannersStart](./kibana-plugin-core-public.overlaybannersstart.md) | | | [OverlayRef](./kibana-plugin-core-public.overlayref.md) | Returned by [OverlayStart](./kibana-plugin-core-public.overlaystart.md) methods for closing a mounted overlay. | | [OverlayStart](./kibana-plugin-core-public.overlaystart.md) | | -| [PackageInfo](./kibana-plugin-core-public.packageinfo.md) | | | [Plugin](./kibana-plugin-core-public.plugin.md) | The interface that should be returned by a PluginInitializer. | | [PluginInitializerContext](./kibana-plugin-core-public.plugininitializercontext.md) | The available core services passed to a PluginInitializer | | [SavedObject](./kibana-plugin-core-public.savedobject.md) | | diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.branch.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.branch.md deleted file mode 100644 index 24fd8a74c84f11..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.branch.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [branch](./kibana-plugin-core-public.packageinfo.branch.md) - -## PackageInfo.branch property - -Signature: - -```typescript -branch: string; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildnum.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildnum.md deleted file mode 100644 index b849b7bdc2a54e..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildnum.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [buildNum](./kibana-plugin-core-public.packageinfo.buildnum.md) - -## PackageInfo.buildNum property - -Signature: - -```typescript -buildNum: number; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildsha.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildsha.md deleted file mode 100644 index 42e5a21ab2f540..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.buildsha.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [buildSha](./kibana-plugin-core-public.packageinfo.buildsha.md) - -## PackageInfo.buildSha property - -Signature: - -```typescript -buildSha: string; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.dist.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.dist.md deleted file mode 100644 index ac6e39e07172b4..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.dist.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [dist](./kibana-plugin-core-public.packageinfo.dist.md) - -## PackageInfo.dist property - -Signature: - -```typescript -dist: boolean; -``` diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.md deleted file mode 100644 index e819fb7990e5d0..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) - -## PackageInfo interface - - -Signature: - -```typescript -export interface PackageInfo -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [branch](./kibana-plugin-core-public.packageinfo.branch.md) | string | | -| [buildNum](./kibana-plugin-core-public.packageinfo.buildnum.md) | number | | -| [buildSha](./kibana-plugin-core-public.packageinfo.buildsha.md) | string | | -| [dist](./kibana-plugin-core-public.packageinfo.dist.md) | boolean | | -| [version](./kibana-plugin-core-public.packageinfo.version.md) | string | | - diff --git a/docs/development/core/public/kibana-plugin-core-public.packageinfo.version.md b/docs/development/core/public/kibana-plugin-core-public.packageinfo.version.md deleted file mode 100644 index 5a7649a1445f6c..00000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.packageinfo.version.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [PackageInfo](./kibana-plugin-core-public.packageinfo.md) > [version](./kibana-plugin-core-public.packageinfo.version.md) - -## PackageInfo.version property - -Signature: - -```typescript -version: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecation.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecation.md deleted file mode 100644 index 6fa74bccb4fc54..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecation.md +++ /dev/null @@ -1,18 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) - -## ConfigDeprecation type - -Configuration deprecation returned from [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md) that handles a single deprecation from the configuration. - -Signature: - -```typescript -export declare type ConfigDeprecation = (config: Record, fromPath: string, logger: ConfigDeprecationLogger) => Record; -``` - -## Remarks - -This should only be manually implemented if [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) does not provide the proper helpers for a specific deprecation need. - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.md deleted file mode 100644 index 8b34b379af67e9..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.md +++ /dev/null @@ -1,36 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) - -## ConfigDeprecationFactory interface - -Provides helpers to generates the most commonly used [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) when invoking a [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md). - -See methods documentation for more detailed examples. - -Signature: - -```typescript -export interface ConfigDeprecationFactory -``` - -## Example - - -```typescript -const provider: ConfigDeprecationProvider = ({ rename, unused }) => [ - rename('oldKey', 'newKey'), - unused('deprecatedKey'), -] - -``` - -## Methods - -| Method | Description | -| --- | --- | -| [rename(oldKey, newKey)](./kibana-plugin-core-server.configdeprecationfactory.rename.md) | Rename a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the oldKey was found and deprecation applied. | -| [renameFromRoot(oldKey, newKey, silent)](./kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md) | Rename a configuration property from the root configuration. Will log a deprecation warning if the oldKey was found and deprecation applied.This should be only used when renaming properties from different configuration's path. To rename properties from inside a plugin's configuration, use 'rename' instead. | -| [unused(unusedKey)](./kibana-plugin-core-server.configdeprecationfactory.unused.md) | Remove a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the unused key was found and deprecation applied. | -| [unusedFromRoot(unusedKey)](./kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md) | Remove a configuration property from the root configuration. Will log a deprecation warning if the unused key was found and deprecation applied.This should be only used when removing properties from outside of a plugin's configuration. To remove properties from inside a plugin's configuration, use 'unused' instead. | - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.rename.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.rename.md deleted file mode 100644 index 91bf1b86fe52ec..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.rename.md +++ /dev/null @@ -1,36 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) > [rename](./kibana-plugin-core-server.configdeprecationfactory.rename.md) - -## ConfigDeprecationFactory.rename() method - -Rename a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the oldKey was found and deprecation applied. - -Signature: - -```typescript -rename(oldKey: string, newKey: string): ConfigDeprecation; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| oldKey | string | | -| newKey | string | | - -Returns: - -`ConfigDeprecation` - -## Example - -Rename 'myplugin.oldKey' to 'myplugin.newKey' - -```typescript -const provider: ConfigDeprecationProvider = ({ rename }) => [ - rename('oldKey', 'newKey'), -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md deleted file mode 100644 index cc8cde9a5ed79f..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md +++ /dev/null @@ -1,39 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) > [renameFromRoot](./kibana-plugin-core-server.configdeprecationfactory.renamefromroot.md) - -## ConfigDeprecationFactory.renameFromRoot() method - -Rename a configuration property from the root configuration. Will log a deprecation warning if the oldKey was found and deprecation applied. - -This should be only used when renaming properties from different configuration's path. To rename properties from inside a plugin's configuration, use 'rename' instead. - -Signature: - -```typescript -renameFromRoot(oldKey: string, newKey: string, silent?: boolean): ConfigDeprecation; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| oldKey | string | | -| newKey | string | | -| silent | boolean | | - -Returns: - -`ConfigDeprecation` - -## Example - -Rename 'oldplugin.key' to 'newplugin.key' - -```typescript -const provider: ConfigDeprecationProvider = ({ renameFromRoot }) => [ - renameFromRoot('oldplugin.key', 'newplugin.key'), -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unused.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unused.md deleted file mode 100644 index e7443b135984fc..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unused.md +++ /dev/null @@ -1,35 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) > [unused](./kibana-plugin-core-server.configdeprecationfactory.unused.md) - -## ConfigDeprecationFactory.unused() method - -Remove a configuration property from inside a plugin's configuration path. Will log a deprecation warning if the unused key was found and deprecation applied. - -Signature: - -```typescript -unused(unusedKey: string): ConfigDeprecation; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| unusedKey | string | | - -Returns: - -`ConfigDeprecation` - -## Example - -Flags 'myplugin.deprecatedKey' as unused - -```typescript -const provider: ConfigDeprecationProvider = ({ unused }) => [ - unused('deprecatedKey'), -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md deleted file mode 100644 index 8d5ed349e60e96..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md +++ /dev/null @@ -1,37 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) > [unusedFromRoot](./kibana-plugin-core-server.configdeprecationfactory.unusedfromroot.md) - -## ConfigDeprecationFactory.unusedFromRoot() method - -Remove a configuration property from the root configuration. Will log a deprecation warning if the unused key was found and deprecation applied. - -This should be only used when removing properties from outside of a plugin's configuration. To remove properties from inside a plugin's configuration, use 'unused' instead. - -Signature: - -```typescript -unusedFromRoot(unusedKey: string): ConfigDeprecation; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| unusedKey | string | | - -Returns: - -`ConfigDeprecation` - -## Example - -Flags 'somepath.deprecatedProperty' as unused - -```typescript -const provider: ConfigDeprecationProvider = ({ unusedFromRoot }) => [ - unusedFromRoot('somepath.deprecatedProperty'), -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationlogger.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationlogger.md deleted file mode 100644 index 15da7d24b4a7ca..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationlogger.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationLogger](./kibana-plugin-core-server.configdeprecationlogger.md) - -## ConfigDeprecationLogger type - -Logger interface used when invoking a [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) - -Signature: - -```typescript -export declare type ConfigDeprecationLogger = (message: string) => void; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationprovider.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationprovider.md deleted file mode 100644 index 64351cbdb4a71c..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configdeprecationprovider.md +++ /dev/null @@ -1,28 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md) - -## ConfigDeprecationProvider type - -A provider that should returns a list of [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md). - -See [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) for more usage examples. - -Signature: - -```typescript -export declare type ConfigDeprecationProvider = (factory: ConfigDeprecationFactory) => ConfigDeprecation[]; -``` - -## Example - - -```typescript -const provider: ConfigDeprecationProvider = ({ rename, unused }) => [ - rename('oldKey', 'newKey'), - unused('deprecatedKey'), - myCustomDeprecation, -] - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.configpath.md b/docs/development/core/server/kibana-plugin-core-server.configpath.md deleted file mode 100644 index 0b15d3ca727f2c..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.configpath.md +++ /dev/null @@ -1,12 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigPath](./kibana-plugin-core-server.configpath.md) - -## ConfigPath type - - -Signature: - -```typescript -export declare type ConfigPath = string | string[]; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.environmentmode.dev.md b/docs/development/core/server/kibana-plugin-core-server.environmentmode.dev.md deleted file mode 100644 index c1b9d5b7e19b1b..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.environmentmode.dev.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) > [dev](./kibana-plugin-core-server.environmentmode.dev.md) - -## EnvironmentMode.dev property - -Signature: - -```typescript -dev: boolean; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.environmentmode.md b/docs/development/core/server/kibana-plugin-core-server.environmentmode.md deleted file mode 100644 index 94795754739abf..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.environmentmode.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) - -## EnvironmentMode interface - - -Signature: - -```typescript -export interface EnvironmentMode -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [dev](./kibana-plugin-core-server.environmentmode.dev.md) | boolean | | -| [name](./kibana-plugin-core-server.environmentmode.name.md) | 'development' | 'production' | | -| [prod](./kibana-plugin-core-server.environmentmode.prod.md) | boolean | | - diff --git a/docs/development/core/server/kibana-plugin-core-server.environmentmode.name.md b/docs/development/core/server/kibana-plugin-core-server.environmentmode.name.md deleted file mode 100644 index 5627deab9e83ec..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.environmentmode.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) > [name](./kibana-plugin-core-server.environmentmode.name.md) - -## EnvironmentMode.name property - -Signature: - -```typescript -name: 'development' | 'production'; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.environmentmode.prod.md b/docs/development/core/server/kibana-plugin-core-server.environmentmode.prod.md deleted file mode 100644 index 1ed3fa33b89979..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.environmentmode.prod.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) > [prod](./kibana-plugin-core-server.environmentmode.prod.md) - -## EnvironmentMode.prod property - -Signature: - -```typescript -prod: boolean; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.debug.md b/docs/development/core/server/kibana-plugin-core-server.logger.debug.md deleted file mode 100644 index c2b95d92b21d14..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.debug.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [debug](./kibana-plugin-core-server.logger.debug.md) - -## Logger.debug() method - -Log messages useful for debugging and interactive investigation - -Signature: - -```typescript -debug(message: string, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| message | string | The log message | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.error.md b/docs/development/core/server/kibana-plugin-core-server.logger.error.md deleted file mode 100644 index 68e7499fabc320..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.error.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [error](./kibana-plugin-core-server.logger.error.md) - -## Logger.error() method - -Logs abnormal or unexpected errors or messages that caused a failure in the application flow - -Signature: - -```typescript -error(errorOrMessage: string | Error, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| errorOrMessage | string | Error | An Error object or message string to log | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.fatal.md b/docs/development/core/server/kibana-plugin-core-server.logger.fatal.md deleted file mode 100644 index d39194d2126b73..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.fatal.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [fatal](./kibana-plugin-core-server.logger.fatal.md) - -## Logger.fatal() method - -Logs abnormal or unexpected errors or messages that caused an unrecoverable failure - -Signature: - -```typescript -fatal(errorOrMessage: string | Error, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| errorOrMessage | string | Error | An Error object or message string to log | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.get.md b/docs/development/core/server/kibana-plugin-core-server.logger.get.md deleted file mode 100644 index 5f69831a738082..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.get.md +++ /dev/null @@ -1,33 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [get](./kibana-plugin-core-server.logger.get.md) - -## Logger.get() method - -Returns a new [Logger](./kibana-plugin-core-server.logger.md) instance extending the current logger context. - -Signature: - -```typescript -get(...childContextPaths: string[]): Logger; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| childContextPaths | string[] | | - -Returns: - -`Logger` - -## Example - - -```typescript -const logger = loggerFactory.get('plugin', 'service'); // 'plugin.service' context -const subLogger = logger.get('feature'); // 'plugin.service.feature' context - -``` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.info.md b/docs/development/core/server/kibana-plugin-core-server.logger.info.md deleted file mode 100644 index 7e786ed0e3f9b9..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.info.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [info](./kibana-plugin-core-server.logger.info.md) - -## Logger.info() method - -Logs messages related to general application flow - -Signature: - -```typescript -info(message: string, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| message | string | The log message | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.md b/docs/development/core/server/kibana-plugin-core-server.logger.md deleted file mode 100644 index 7012358524c359..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.md +++ /dev/null @@ -1,26 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) - -## Logger interface - -Logger exposes all the necessary methods to log any type of information and this is the interface used by the logging consumers including plugins. - -Signature: - -```typescript -export interface Logger -``` - -## Methods - -| Method | Description | -| --- | --- | -| [debug(message, meta)](./kibana-plugin-core-server.logger.debug.md) | Log messages useful for debugging and interactive investigation | -| [error(errorOrMessage, meta)](./kibana-plugin-core-server.logger.error.md) | Logs abnormal or unexpected errors or messages that caused a failure in the application flow | -| [fatal(errorOrMessage, meta)](./kibana-plugin-core-server.logger.fatal.md) | Logs abnormal or unexpected errors or messages that caused an unrecoverable failure | -| [get(childContextPaths)](./kibana-plugin-core-server.logger.get.md) | Returns a new [Logger](./kibana-plugin-core-server.logger.md) instance extending the current logger context. | -| [info(message, meta)](./kibana-plugin-core-server.logger.info.md) | Logs messages related to general application flow | -| [trace(message, meta)](./kibana-plugin-core-server.logger.trace.md) | Log messages at the most detailed log level | -| [warn(errorOrMessage, meta)](./kibana-plugin-core-server.logger.warn.md) | Logs abnormal or unexpected errors or messages | - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.trace.md b/docs/development/core/server/kibana-plugin-core-server.logger.trace.md deleted file mode 100644 index b8d761f69541e3..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.trace.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [trace](./kibana-plugin-core-server.logger.trace.md) - -## Logger.trace() method - -Log messages at the most detailed log level - -Signature: - -```typescript -trace(message: string, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| message | string | The log message | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.logger.warn.md b/docs/development/core/server/kibana-plugin-core-server.logger.warn.md deleted file mode 100644 index 21cefe4f748dd5..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logger.warn.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [Logger](./kibana-plugin-core-server.logger.md) > [warn](./kibana-plugin-core-server.logger.warn.md) - -## Logger.warn() method - -Logs abnormal or unexpected errors or messages - -Signature: - -```typescript -warn(errorOrMessage: string | Error, meta?: LogMeta): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| errorOrMessage | string | Error | An Error object or message string to log | -| meta | LogMeta | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-core-server.loggerfactory.get.md b/docs/development/core/server/kibana-plugin-core-server.loggerfactory.get.md deleted file mode 100644 index d081d359b94809..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.loggerfactory.get.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LoggerFactory](./kibana-plugin-core-server.loggerfactory.md) > [get](./kibana-plugin-core-server.loggerfactory.get.md) - -## LoggerFactory.get() method - -Returns a `Logger` instance for the specified context. - -Signature: - -```typescript -get(...contextParts: string[]): Logger; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| contextParts | string[] | Parts of the context to return logger for. For example get('plugins', 'pid') will return a logger for the plugins.pid context. | - -Returns: - -`Logger` - diff --git a/docs/development/core/server/kibana-plugin-core-server.loggerfactory.md b/docs/development/core/server/kibana-plugin-core-server.loggerfactory.md deleted file mode 100644 index 8795413bf675a2..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.loggerfactory.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LoggerFactory](./kibana-plugin-core-server.loggerfactory.md) - -## LoggerFactory interface - -The single purpose of `LoggerFactory` interface is to define a way to retrieve a context-based logger instance. - -Signature: - -```typescript -export interface LoggerFactory -``` - -## Methods - -| Method | Description | -| --- | --- | -| [get(contextParts)](./kibana-plugin-core-server.loggerfactory.get.md) | Returns a Logger instance for the specified context. | - diff --git a/docs/development/core/server/kibana-plugin-core-server.logmeta.md b/docs/development/core/server/kibana-plugin-core-server.logmeta.md deleted file mode 100644 index 56a2af7e826c65..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.logmeta.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LogMeta](./kibana-plugin-core-server.logmeta.md) - -## LogMeta interface - -Contextual metadata - -Signature: - -```typescript -export interface LogMeta -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index 071e776ca0c644..b83c091846f047 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -65,7 +65,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [Capabilities](./kibana-plugin-core-server.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. | | [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | APIs to manage the [Capabilities](./kibana-plugin-core-server.capabilities.md) that will be used by the application.Plugins relying on capabilities to toggle some of their features should register them during the setup phase using the registerProvider method.Plugins having the responsibility to restrict capabilities depending on a given context should register their capabilities switcher using the registerSwitcher method.Refers to the methods documentation for complete description and examples. | | [CapabilitiesStart](./kibana-plugin-core-server.capabilitiesstart.md) | APIs to access the application [Capabilities](./kibana-plugin-core-server.capabilities.md). | -| [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) | Provides helpers to generates the most commonly used [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) when invoking a [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md).See methods documentation for more detailed examples. | | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. | | [CoreSetup](./kibana-plugin-core-server.coresetup.md) | Context passed to the plugins setup method. | | [CoreStart](./kibana-plugin-core-server.corestart.md) | Context passed to the plugins start method. | @@ -81,7 +80,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) | | | [ElasticsearchServiceStart](./kibana-plugin-core-server.elasticsearchservicestart.md) | | | [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) | | -| [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) | | | [ErrorHttpResponseOptions](./kibana-plugin-core-server.errorhttpresponseoptions.md) | HTTP response parameters | | [Explanation](./kibana-plugin-core-server.explanation.md) | | | [FakeRequest](./kibana-plugin-core-server.fakerequest.md) | Fake request object created manually by Kibana plugins. | @@ -114,11 +112,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | [LegacyRequest](./kibana-plugin-core-server.legacyrequest.md) | | | [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md) | | | [LegacyServiceStartDeps](./kibana-plugin-core-server.legacyservicestartdeps.md) | | -| [Logger](./kibana-plugin-core-server.logger.md) | Logger exposes all the necessary methods to log any type of information and this is the interface used by the logging consumers including plugins. | | [LoggerContextConfigInput](./kibana-plugin-core-server.loggercontextconfiginput.md) | | -| [LoggerFactory](./kibana-plugin-core-server.loggerfactory.md) | The single purpose of LoggerFactory interface is to define a way to retrieve a context-based logger instance. | | [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md) | Provides APIs to plugins for customizing the plugin's logger. | -| [LogMeta](./kibana-plugin-core-server.logmeta.md) | Contextual metadata | | [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) | APIs to retrieves metrics gathered and exposed by the core platform. | | [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) | | | [OnPostAuthToolkit](./kibana-plugin-core-server.onpostauthtoolkit.md) | A tool set defining an outcome of OnPostAuth interceptor for incoming request. | @@ -131,7 +126,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [OpsOsMetrics](./kibana-plugin-core-server.opsosmetrics.md) | OS related metrics | | [OpsProcessMetrics](./kibana-plugin-core-server.opsprocessmetrics.md) | Process related metrics | | [OpsServerMetrics](./kibana-plugin-core-server.opsservermetrics.md) | server related metrics | -| [PackageInfo](./kibana-plugin-core-server.packageinfo.md) | | | [Plugin](./kibana-plugin-core-server.plugin.md) | The interface that should be returned by a PluginInitializer. | | [PluginConfigDescriptor](./kibana-plugin-core-server.pluginconfigdescriptor.md) | Describes a plugin configuration properties. | | [PluginInitializerContext](./kibana-plugin-core-server.plugininitializercontext.md) | Context that's available to plugins during initialization stage. | @@ -233,10 +227,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AuthResult](./kibana-plugin-core-server.authresult.md) | | | [CapabilitiesProvider](./kibana-plugin-core-server.capabilitiesprovider.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | | [CapabilitiesSwitcher](./kibana-plugin-core-server.capabilitiesswitcher.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | -| [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) | Configuration deprecation returned from [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md) that handles a single deprecation from the configuration. | -| [ConfigDeprecationLogger](./kibana-plugin-core-server.configdeprecationlogger.md) | Logger interface used when invoking a [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) | -| [ConfigDeprecationProvider](./kibana-plugin-core-server.configdeprecationprovider.md) | A provider that should returns a list of [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md).See [ConfigDeprecationFactory](./kibana-plugin-core-server.configdeprecationfactory.md) for more usage examples. | -| [ConfigPath](./kibana-plugin-core-server.configpath.md) | | | [DestructiveRouteMethod](./kibana-plugin-core-server.destructiveroutemethod.md) | Set of HTTP methods changing the state of the server. | | [ElasticsearchClient](./kibana-plugin-core-server.elasticsearchclient.md) | Client used to query the elasticsearch cluster. | | [ElasticsearchClientConfig](./kibana-plugin-core-server.elasticsearchclientconfig.md) | Configuration options to be used to create a [cluster client](./kibana-plugin-core-server.iclusterclient.md) using the [createClient API](./kibana-plugin-core-server.elasticsearchservicestart.createclient.md) | diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.branch.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.branch.md deleted file mode 100644 index 34fbd56045921a..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.branch.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [branch](./kibana-plugin-core-server.packageinfo.branch.md) - -## PackageInfo.branch property - -Signature: - -```typescript -branch: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildnum.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildnum.md deleted file mode 100644 index acc33fae18fbcd..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildnum.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [buildNum](./kibana-plugin-core-server.packageinfo.buildnum.md) - -## PackageInfo.buildNum property - -Signature: - -```typescript -buildNum: number; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildsha.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildsha.md deleted file mode 100644 index 14ad75168fd859..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.buildsha.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [buildSha](./kibana-plugin-core-server.packageinfo.buildsha.md) - -## PackageInfo.buildSha property - -Signature: - -```typescript -buildSha: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.dist.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.dist.md deleted file mode 100644 index d7567010ddd53f..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.dist.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [dist](./kibana-plugin-core-server.packageinfo.dist.md) - -## PackageInfo.dist property - -Signature: - -```typescript -dist: boolean; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.md deleted file mode 100644 index 3e1789d8df896c..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) - -## PackageInfo interface - - -Signature: - -```typescript -export interface PackageInfo -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [branch](./kibana-plugin-core-server.packageinfo.branch.md) | string | | -| [buildNum](./kibana-plugin-core-server.packageinfo.buildnum.md) | number | | -| [buildSha](./kibana-plugin-core-server.packageinfo.buildsha.md) | string | | -| [dist](./kibana-plugin-core-server.packageinfo.dist.md) | boolean | | -| [version](./kibana-plugin-core-server.packageinfo.version.md) | string | | - diff --git a/docs/development/core/server/kibana-plugin-core-server.packageinfo.version.md b/docs/development/core/server/kibana-plugin-core-server.packageinfo.version.md deleted file mode 100644 index 1606ab5901941b..00000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.packageinfo.version.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [PackageInfo](./kibana-plugin-core-server.packageinfo.md) > [version](./kibana-plugin-core-server.packageinfo.version.md) - -## PackageInfo.version property - -Signature: - -```typescript -version: string; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md b/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md index d539b5a0501c15..d06b3cb9fa64ae 100644 --- a/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md +++ b/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md @@ -4,7 +4,7 @@ ## PluginConfigDescriptor.deprecations property -Provider for the [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) to apply to the plugin configuration. +Provider for the to apply to the plugin configuration. Signature: diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md b/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md index 964d45f155948e..5708c4f9a3f88a 100644 --- a/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md +++ b/docs/development/core/server/kibana-plugin-core-server.pluginconfigdescriptor.md @@ -44,7 +44,7 @@ export const config: PluginConfigDescriptor = { | Property | Type | Description | | --- | --- | --- | -| [deprecations](./kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md) | ConfigDeprecationProvider | Provider for the [ConfigDeprecation](./kibana-plugin-core-server.configdeprecation.md) to apply to the plugin configuration. | +| [deprecations](./kibana-plugin-core-server.pluginconfigdescriptor.deprecations.md) | ConfigDeprecationProvider | Provider for the to apply to the plugin configuration. | | [exposeToBrowser](./kibana-plugin-core-server.pluginconfigdescriptor.exposetobrowser.md) | {
[P in keyof T]?: boolean;
} | List of configuration properties that will be available on the client-side plugin. | | [schema](./kibana-plugin-core-server.pluginconfigdescriptor.schema.md) | PluginConfigSchema<T> | Schema to use to validate the plugin configuration.[PluginConfigSchema](./kibana-plugin-core-server.pluginconfigschema.md) | diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.configpath.md b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.configpath.md index c391431e12b1cf..8aa603242df8e1 100644 --- a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.configpath.md +++ b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.configpath.md @@ -4,7 +4,7 @@ ## PluginManifest.configPath property -Root [configuration path](./kibana-plugin-core-server.configpath.md) used by the plugin, defaults to "id" in snake\_case format. +Root used by the plugin, defaults to "id" in snake\_case format. Signature: diff --git a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md index 6db2f89590149f..b0182a7c48e16c 100644 --- a/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md +++ b/docs/development/core/server/kibana-plugin-core-server.pluginmanifest.md @@ -20,7 +20,7 @@ Should never be used in code outside of Core but is exported for documentation p | Property | Type | Description | | --- | --- | --- | -| [configPath](./kibana-plugin-core-server.pluginmanifest.configpath.md) | ConfigPath | Root [configuration path](./kibana-plugin-core-server.configpath.md) used by the plugin, defaults to "id" in snake\_case format. | +| [configPath](./kibana-plugin-core-server.pluginmanifest.configpath.md) | ConfigPath | Root used by the plugin, defaults to "id" in snake\_case format. | | [extraPublicDirs](./kibana-plugin-core-server.pluginmanifest.extrapublicdirs.md) | string[] | Specifies directory names that can be imported by other ui-plugins built using the same instance of the @kbn/optimizer. A temporary measure we plan to replace with better mechanisms for sharing static code between plugins | | [id](./kibana-plugin-core-server.pluginmanifest.id.md) | PluginName | Identifier of the plugin. Must be a string in camelCase. Part of a plugin public contract. Other plugins leverage it to access plugin API, navigate to the plugin, etc. | | [kibanaVersion](./kibana-plugin-core-server.pluginmanifest.kibanaversion.md) | string | The version of Kibana the plugin is compatible with, defaults to "version". | diff --git a/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md b/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md index 7f306919101efb..ec2e1b227a2d7b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md +++ b/docs/development/core/server/kibana-plugin-core-server.sharedglobalconfig.md @@ -12,5 +12,6 @@ export declare type SharedGlobalConfig = RecursiveReadonly<{ kibana: Pick; elasticsearch: Pick; path: Pick; + savedObjects: Pick; }>; ``` diff --git a/package.json b/package.json index 8994f327d3e653..5008bc0bcebc59 100644 --- a/package.json +++ b/package.json @@ -137,9 +137,11 @@ "@hapi/wreck": "^15.0.2", "@kbn/analytics": "1.0.0", "@kbn/babel-preset": "1.0.0", + "@kbn/config": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", + "@kbn/logging": "1.0.0", "@kbn/pm": "1.0.0", "@kbn/std": "1.0.0", "@kbn/telemetry-tools": "1.0.0", diff --git a/packages/kbn-config/README.md b/packages/kbn-config/README.md new file mode 100644 index 00000000000000..343748cb0d817e --- /dev/null +++ b/packages/kbn-config/README.md @@ -0,0 +1,3 @@ +# `@kbn/config` — Kibana configuration file loader + +This package contains the configuration service used to load and read the kibana configuration file diff --git a/src/core/server/config/__fixtures__/config.yml b/packages/kbn-config/__fixtures__/config.yml similarity index 100% rename from src/core/server/config/__fixtures__/config.yml rename to packages/kbn-config/__fixtures__/config.yml diff --git a/src/core/server/config/__fixtures__/config_flat.yml b/packages/kbn-config/__fixtures__/config_flat.yml similarity index 100% rename from src/core/server/config/__fixtures__/config_flat.yml rename to packages/kbn-config/__fixtures__/config_flat.yml diff --git a/src/core/server/config/__fixtures__/en_var_ref_config.yml b/packages/kbn-config/__fixtures__/en_var_ref_config.yml similarity index 100% rename from src/core/server/config/__fixtures__/en_var_ref_config.yml rename to packages/kbn-config/__fixtures__/en_var_ref_config.yml diff --git a/src/core/server/config/__fixtures__/one.yml b/packages/kbn-config/__fixtures__/one.yml similarity index 100% rename from src/core/server/config/__fixtures__/one.yml rename to packages/kbn-config/__fixtures__/one.yml diff --git a/src/core/server/config/__fixtures__/two.yml b/packages/kbn-config/__fixtures__/two.yml similarity index 100% rename from src/core/server/config/__fixtures__/two.yml rename to packages/kbn-config/__fixtures__/two.yml diff --git a/packages/kbn-config/package.json b/packages/kbn-config/package.json new file mode 100644 index 00000000000000..2d9dbc3b7ab8f7 --- /dev/null +++ b/packages/kbn-config/package.json @@ -0,0 +1,30 @@ +{ + "name": "@kbn/config", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "version": "1.0.0", + "license": "Apache-2.0", + "private": true, + "scripts": { + "build": "tsc", + "kbn:bootstrap": "yarn build" + }, + "dependencies": { + "@elastic/safer-lodash-set": "0.0.0", + "@kbn/config-schema": "1.0.0", + "@kbn/dev-utils": "1.0.0", + "@kbn/logging": "1.0.0", + "@kbn/std": "1.0.0", + "@kbn/utility-types": "1.0.0", + "js-yaml": "3.13.1", + "load-json-file": "^6.2.0", + "lodash": "^4.17.20", + "moment": "^2.24.0", + "rxjs": "^6.5.5", + "type-detect": "^4.0.8" + }, + "devDependencies": { + "typescript": "4.0.2", + "tsd": "^0.7.4" + } +} diff --git a/src/core/server/config/__mocks__/env.ts b/packages/kbn-config/src/__mocks__/env.ts similarity index 100% rename from src/core/server/config/__mocks__/env.ts rename to packages/kbn-config/src/__mocks__/env.ts diff --git a/src/core/server/config/__snapshots__/config_service.test.ts.snap b/packages/kbn-config/src/__snapshots__/config_service.test.ts.snap similarity index 100% rename from src/core/server/config/__snapshots__/config_service.test.ts.snap rename to packages/kbn-config/src/__snapshots__/config_service.test.ts.snap diff --git a/src/core/server/config/__snapshots__/env.test.ts.snap b/packages/kbn-config/src/__snapshots__/env.test.ts.snap similarity index 100% rename from src/core/server/config/__snapshots__/env.test.ts.snap rename to packages/kbn-config/src/__snapshots__/env.test.ts.snap diff --git a/src/core/server/config/apply_argv.test.ts b/packages/kbn-config/src/apply_argv.test.ts similarity index 100% rename from src/core/server/config/apply_argv.test.ts rename to packages/kbn-config/src/apply_argv.test.ts diff --git a/src/core/server/config/config.mock.ts b/packages/kbn-config/src/config.mock.ts similarity index 100% rename from src/core/server/config/config.mock.ts rename to packages/kbn-config/src/config.mock.ts diff --git a/src/core/server/config/config.test.ts b/packages/kbn-config/src/config.test.ts similarity index 100% rename from src/core/server/config/config.test.ts rename to packages/kbn-config/src/config.test.ts diff --git a/src/core/server/config/config.ts b/packages/kbn-config/src/config.ts similarity index 99% rename from src/core/server/config/config.ts rename to packages/kbn-config/src/config.ts index a4026b1d88ac37..3d012dd5f38856 100644 --- a/src/core/server/config/config.ts +++ b/packages/kbn-config/src/config.ts @@ -23,7 +23,7 @@ export type ConfigPath = string | string[]; /** * Checks whether specified value can be considered as config path. * @param value Value to check. - * @internal + * @public */ export function isConfigPath(value: unknown): value is ConfigPath { if (!value) { diff --git a/src/core/server/config/config_service.mock.ts b/packages/kbn-config/src/config_service.mock.ts similarity index 100% rename from src/core/server/config/config_service.mock.ts rename to packages/kbn-config/src/config_service.mock.ts diff --git a/src/core/server/config/config_service.test.mocks.ts b/packages/kbn-config/src/config_service.test.mocks.ts similarity index 94% rename from src/core/server/config/config_service.test.mocks.ts rename to packages/kbn-config/src/config_service.test.mocks.ts index 1299c4c0b4eb1a..1b70802e9805e6 100644 --- a/src/core/server/config/config_service.test.mocks.ts +++ b/packages/kbn-config/src/config_service.test.mocks.ts @@ -18,7 +18,7 @@ */ export const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] }); -jest.mock('../../../../package.json', () => mockPackage); +jest.mock('../../../package.json', () => mockPackage); export const mockApplyDeprecations = jest.fn((config, deprecations, log) => config); jest.mock('./deprecation/apply_deprecations', () => ({ diff --git a/src/core/server/config/config_service.test.ts b/packages/kbn-config/src/config_service.test.ts similarity index 95% rename from src/core/server/config/config_service.test.ts rename to packages/kbn-config/src/config_service.test.ts index 95153447bd4a90..b32b4054557001 100644 --- a/src/core/server/config/config_service.test.ts +++ b/packages/kbn-config/src/config_service.test.ts @@ -20,22 +20,36 @@ import { BehaviorSubject, Observable } from 'rxjs'; import { first, take } from 'rxjs/operators'; -import { mockPackage, mockApplyDeprecations } from './config_service.test.mocks'; -import { rawConfigServiceMock } from './raw_config_service.mock'; +import { mockApplyDeprecations } from './config_service.test.mocks'; +import { rawConfigServiceMock } from './raw/raw_config_service.mock'; import { schema } from '@kbn/config-schema'; +import { MockedLogger, loggerMock } from '@kbn/logging/target/mocks'; + +import { ConfigService, Env, RawPackageInfo } from '.'; -import { ConfigService, Env } from '.'; -import { loggingSystemMock } from '../logging/logging_system.mock'; import { getEnvOptions } from './__mocks__/env'; +const packageInfos: RawPackageInfo = { + branch: 'master', + version: '8.0.0', + build: { + number: 42, + sha: 'one', + }, +}; const emptyArgv = getEnvOptions(); -const defaultEnv = new Env('/kibana', emptyArgv); -const logger = loggingSystemMock.create(); +const defaultEnv = new Env('/kibana', packageInfos, emptyArgv); + +let logger: MockedLogger; const getRawConfigProvider = (rawConfig: Record) => rawConfigServiceMock.create({ rawConfig }); +beforeEach(() => { + logger = loggerMock.create(); +}); + test('returns config at path as observable', async () => { const rawConfig = getRawConfigProvider({ key: 'foo' }); const configService = new ConfigService(rawConfig, defaultEnv, logger); @@ -237,7 +251,7 @@ test('tracks unhandled paths', async () => { }); test('correctly passes context', async () => { - mockPackage.raw = { + const mockPackage = { branch: 'feature-v1', version: 'v1', build: { @@ -247,7 +261,7 @@ test('correctly passes context', async () => { }, }; - const env = new Env('/kibana', getEnvOptions()); + const env = new Env('/kibana', mockPackage, getEnvOptions()); const rawConfigProvider = rawConfigServiceMock.create({ rawConfig: { foo: {} } }); const schemaDefinition = schema.object({ @@ -441,9 +455,9 @@ test('logs deprecation warning during validation', async () => { return config; }); - loggingSystemMock.clear(logger); + loggerMock.clear(logger); await configService.validate(); - expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(` + expect(loggerMock.collect(logger).warn).toMatchInlineSnapshot(` Array [ Array [ "some deprecation message", diff --git a/src/core/server/config/config_service.ts b/packages/kbn-config/src/config_service.ts similarity index 97% rename from src/core/server/config/config_service.ts rename to packages/kbn-config/src/config_service.ts index d77ee980b0491d..faa8397cd6d6ef 100644 --- a/src/core/server/config/config_service.ts +++ b/packages/kbn-config/src/config_service.ts @@ -16,23 +16,24 @@ * specific language governing permissions and limitations * under the License. */ + import type { PublicMethodsOf } from '@kbn/utility-types'; import { Type } from '@kbn/config-schema'; import { isEqual } from 'lodash'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { distinctUntilChanged, first, map, shareReplay, take } from 'rxjs/operators'; +import { Logger, LoggerFactory } from '@kbn/logging'; import { Config, ConfigPath, Env } from '.'; -import { Logger, LoggerFactory } from '../logging'; import { hasConfigPathIntersection } from './config'; -import { RawConfigurationProvider } from './raw_config_service'; +import { RawConfigurationProvider } from './raw/raw_config_service'; import { applyDeprecations, ConfigDeprecationWithContext, ConfigDeprecationProvider, configDeprecationFactory, } from './deprecation'; -import { LegacyObjectToConfigAdapter } from '../legacy/config'; +import { LegacyObjectToConfigAdapter } from './legacy'; /** @internal */ export type IConfigService = PublicMethodsOf; diff --git a/src/core/server/config/deprecation/apply_deprecations.test.ts b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts similarity index 100% rename from src/core/server/config/deprecation/apply_deprecations.test.ts rename to packages/kbn-config/src/deprecation/apply_deprecations.test.ts diff --git a/src/core/server/config/deprecation/apply_deprecations.ts b/packages/kbn-config/src/deprecation/apply_deprecations.ts similarity index 100% rename from src/core/server/config/deprecation/apply_deprecations.ts rename to packages/kbn-config/src/deprecation/apply_deprecations.ts diff --git a/src/core/server/config/deprecation/deprecation_factory.test.ts b/packages/kbn-config/src/deprecation/deprecation_factory.test.ts similarity index 100% rename from src/core/server/config/deprecation/deprecation_factory.test.ts rename to packages/kbn-config/src/deprecation/deprecation_factory.test.ts diff --git a/src/core/server/config/deprecation/deprecation_factory.ts b/packages/kbn-config/src/deprecation/deprecation_factory.ts similarity index 100% rename from src/core/server/config/deprecation/deprecation_factory.ts rename to packages/kbn-config/src/deprecation/deprecation_factory.ts diff --git a/packages/kbn-config/src/deprecation/index.ts b/packages/kbn-config/src/deprecation/index.ts new file mode 100644 index 00000000000000..504dbfeeb001a4 --- /dev/null +++ b/packages/kbn-config/src/deprecation/index.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { + ConfigDeprecation, + ConfigDeprecationWithContext, + ConfigDeprecationLogger, + ConfigDeprecationFactory, + ConfigDeprecationProvider, +} from './types'; +export { configDeprecationFactory } from './deprecation_factory'; +export { applyDeprecations } from './apply_deprecations'; diff --git a/src/core/server/config/deprecation/types.ts b/packages/kbn-config/src/deprecation/types.ts similarity index 100% rename from src/core/server/config/deprecation/types.ts rename to packages/kbn-config/src/deprecation/types.ts diff --git a/src/core/server/config/env.test.mocks.ts b/packages/kbn-config/src/env.test.mocks.ts similarity index 81% rename from src/core/server/config/env.test.mocks.ts rename to packages/kbn-config/src/env.test.mocks.ts index d35afdf0b66e52..3976b1c2016fbb 100644 --- a/src/core/server/config/env.test.mocks.ts +++ b/packages/kbn-config/src/env.test.mocks.ts @@ -17,7 +17,10 @@ * under the License. */ -jest.mock('path', () => ({ +const realPath = jest.requireActual('path'); + +jest.doMock('path', () => ({ + ...realPath, resolve(...pathSegments: string[]) { return pathSegments.join('/'); }, @@ -26,5 +29,10 @@ jest.mock('path', () => ({ }, })); -export const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] }); -jest.mock('../../../../package.json', () => mockPackage); +export const mockPackage = { + raw: {}, +}; + +jest.doMock('load-json-file', () => ({ + sync: () => mockPackage.raw, +})); diff --git a/src/core/server/config/env.test.ts b/packages/kbn-config/src/env.test.ts similarity index 89% rename from src/core/server/config/env.test.ts rename to packages/kbn-config/src/env.test.ts index 0fffcc44781d9c..f3d51a021246e5 100644 --- a/src/core/server/config/env.test.ts +++ b/packages/kbn-config/src/env.test.ts @@ -19,9 +19,24 @@ import { mockPackage } from './env.test.mocks'; -import { Env } from '.'; +import { Env, RawPackageInfo } from './env'; import { getEnvOptions } from './__mocks__/env'; +const REPO_ROOT = '/test/kibanaRoot'; + +const packageInfos: RawPackageInfo = { + branch: 'master', + version: '8.0.0', + build: { + number: 42, + sha: 'one', + }, +}; + +beforeEach(() => { + mockPackage.raw = {}; +}); + test('correctly creates default environment in dev mode.', () => { mockPackage.raw = { branch: 'some-branch', @@ -29,6 +44,7 @@ test('correctly creates default environment in dev mode.', () => { }; const defaultEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ configs: ['/test/cwd/config/kibana.yml'], isDevClusterMaster: true, @@ -50,6 +66,7 @@ test('correctly creates default environment in prod distributable mode.', () => }; const defaultEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false }, configs: ['/some/other/path/some-kibana.yml'], @@ -71,6 +88,7 @@ test('correctly creates default environment in prod non-distributable mode.', () }; const defaultEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false }, configs: ['/some/other/path/some-kibana.yml'], @@ -92,6 +110,7 @@ test('correctly creates default environment if `--env.name` is supplied.', () => }; const defaultDevEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { envName: 'development' }, configs: ['/some/other/path/some-kibana.yml'], @@ -99,6 +118,7 @@ test('correctly creates default environment if `--env.name` is supplied.', () => ); const defaultProdEnv = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false, envName: 'production' }, configs: ['/some/other/path/some-kibana.yml'], @@ -110,18 +130,17 @@ test('correctly creates default environment if `--env.name` is supplied.', () => }); test('correctly creates environment with constructor.', () => { - mockPackage.raw = { - branch: 'feature-v1', - version: 'v1', - build: { - distributable: true, - number: 100, - sha: 'feature-v1-build-sha', - }, - }; - const env = new Env( '/some/home/dir', + { + branch: 'feature-v1', + version: 'v1', + build: { + distributable: true, + number: 100, + sha: 'feature-v1-build-sha', + }, + }, getEnvOptions({ cliArgs: { dev: false }, configs: ['/some/other/path/some-kibana.yml'], @@ -134,6 +153,7 @@ test('correctly creates environment with constructor.', () => { test('pluginSearchPaths contains x-pack plugins path if --oss flag is false', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { oss: false }, }) @@ -145,6 +165,7 @@ test('pluginSearchPaths contains x-pack plugins path if --oss flag is false', () test('pluginSearchPaths does not contains x-pack plugins path if --oss flag is true', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { oss: true }, }) @@ -156,6 +177,7 @@ test('pluginSearchPaths does not contains x-pack plugins path if --oss flag is t test('pluginSearchPaths contains examples plugins path if --run-examples flag is true', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { runExamples: true }, }) @@ -167,6 +189,7 @@ test('pluginSearchPaths contains examples plugins path if --run-examples flag is test('pluginSearchPaths contains x-pack/examples plugins path if --run-examples flag is true', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { runExamples: true }, }) @@ -178,6 +201,7 @@ test('pluginSearchPaths contains x-pack/examples plugins path if --run-examples test('pluginSearchPaths does not contains examples plugins path if --run-examples flag is false', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { runExamples: false }, }) @@ -189,6 +213,7 @@ test('pluginSearchPaths does not contains examples plugins path if --run-example test('pluginSearchPaths does not contains x-pack/examples plugins path if --run-examples flag is false', () => { const env = new Env( '/some/home/dir', + packageInfos, getEnvOptions({ cliArgs: { runExamples: false }, }) diff --git a/src/core/server/config/env.ts b/packages/kbn-config/src/env.ts similarity index 87% rename from src/core/server/config/env.ts rename to packages/kbn-config/src/env.ts index d8b056996315b8..250c7b72d47a9c 100644 --- a/src/core/server/config/env.ts +++ b/packages/kbn-config/src/env.ts @@ -17,13 +17,10 @@ * under the License. */ -import { resolve, dirname } from 'path'; +import { resolve, join } from 'path'; +import loadJsonFile from 'load-json-file'; import { PackageInfo, EnvironmentMode } from './types'; -// `require` is necessary for this to work inside x-pack code as well -// eslint-disable-next-line @typescript-eslint/no-var-requires -const pkg = require('../../../../package.json'); - /** @internal */ export interface EnvOptions { configs: string[]; @@ -50,13 +47,26 @@ export interface CliArgs { dist: boolean; } +/** @internal */ +export interface RawPackageInfo { + branch: string; + version: string; + build: { + distributable?: boolean; + number: number; + sha: string; + }; +} + export class Env { /** * @internal */ - public static createDefault(options: EnvOptions): Env { - const repoRoot = dirname(require.resolve('../../../../package.json')); - return new Env(repoRoot, options); + public static createDefault(repoRoot: string, options: EnvOptions, pkg?: RawPackageInfo): Env { + if (!pkg) { + pkg = loadJsonFile.sync(join(repoRoot, 'package.json')) as RawPackageInfo; + } + return new Env(repoRoot, pkg, options); } /** @internal */ @@ -99,7 +109,7 @@ export class Env { /** * @internal */ - constructor(public readonly homeDir: string, options: EnvOptions) { + constructor(public readonly homeDir: string, pkg: RawPackageInfo, options: EnvOptions) { this.configDir = resolve(this.homeDir, 'config'); this.binDir = resolve(this.homeDir, 'bin'); this.logDir = resolve(this.homeDir, 'log'); diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts new file mode 100644 index 00000000000000..f02514a92e606e --- /dev/null +++ b/packages/kbn-config/src/index.ts @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { + applyDeprecations, + ConfigDeprecation, + ConfigDeprecationFactory, + configDeprecationFactory, + ConfigDeprecationLogger, + ConfigDeprecationProvider, + ConfigDeprecationWithContext, +} from './deprecation'; + +export { RawConfigurationProvider, RawConfigService, getConfigFromFiles } from './raw'; + +export { ConfigService, IConfigService } from './config_service'; +export { Config, ConfigPath, isConfigPath, hasConfigPathIntersection } from './config'; +export { ObjectToConfigAdapter } from './object_to_config_adapter'; +export { CliArgs, Env, RawPackageInfo } from './env'; +export { EnvironmentMode, PackageInfo } from './types'; +export { LegacyObjectToConfigAdapter, LegacyLoggingConfig } from './legacy'; diff --git a/src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap b/packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap similarity index 100% rename from src/core/server/legacy/config/__snapshots__/legacy_object_to_config_adapter.test.ts.snap rename to packages/kbn-config/src/legacy/__snapshots__/legacy_object_to_config_adapter.test.ts.snap diff --git a/packages/kbn-config/src/legacy/index.ts b/packages/kbn-config/src/legacy/index.ts new file mode 100644 index 00000000000000..cad6b754e956cf --- /dev/null +++ b/packages/kbn-config/src/legacy/index.ts @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { + LegacyObjectToConfigAdapter, + LegacyLoggingConfig, +} from './legacy_object_to_config_adapter'; diff --git a/src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts similarity index 100% rename from src/core/server/legacy/config/legacy_object_to_config_adapter.test.ts rename to packages/kbn-config/src/legacy/legacy_object_to_config_adapter.test.ts diff --git a/src/core/server/legacy/config/legacy_object_to_config_adapter.ts b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.ts similarity index 92% rename from src/core/server/legacy/config/legacy_object_to_config_adapter.ts rename to packages/kbn-config/src/legacy/legacy_object_to_config_adapter.ts index 3e496648c3af90..e8fca8735a6d93 100644 --- a/src/core/server/legacy/config/legacy_object_to_config_adapter.ts +++ b/packages/kbn-config/src/legacy/legacy_object_to_config_adapter.ts @@ -17,10 +17,17 @@ * under the License. */ -import { ConfigPath } from '../../config'; -import { ObjectToConfigAdapter } from '../../config/object_to_config_adapter'; -import { LoggingConfigType } from '../../logging/logging_config'; -import { LegacyVars } from '../types'; +import { ConfigPath } from '../config'; +import { ObjectToConfigAdapter } from '../object_to_config_adapter'; + +// TODO: fix once core schemas are moved to this package +type LoggingConfigType = any; + +/** + * @internal + * @deprecated + */ +export type LegacyVars = Record; /** * Represents logging config supported by the legacy platform. diff --git a/packages/kbn-config/src/mocks.ts b/packages/kbn-config/src/mocks.ts new file mode 100644 index 00000000000000..5ca5de3a328a2b --- /dev/null +++ b/packages/kbn-config/src/mocks.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// these CANT be exported by the main entrypoint, as it cause ts check failures +// in `src/test` and `src/xpack/test` projects due to definition conflicts between +// mocha and jest declaring the same globals such as `it` or `beforeAll`, as the test +// files imports types from `core` that is importing the main `@kbn/config` entrypoint. +// For now, these should be imported using `import {} from '@kbn/config/target/mocks'` +export { configMock } from './config.mock'; +export { configServiceMock } from './config_service.mock'; +export { rawConfigServiceMock } from './raw/raw_config_service.mock'; +export { getEnvOptions } from './__mocks__/env'; diff --git a/src/core/server/config/object_to_config_adapter.test.ts b/packages/kbn-config/src/object_to_config_adapter.test.ts similarity index 100% rename from src/core/server/config/object_to_config_adapter.test.ts rename to packages/kbn-config/src/object_to_config_adapter.test.ts diff --git a/src/core/server/config/object_to_config_adapter.ts b/packages/kbn-config/src/object_to_config_adapter.ts similarity index 100% rename from src/core/server/config/object_to_config_adapter.ts rename to packages/kbn-config/src/object_to_config_adapter.ts diff --git a/src/core/server/config/__snapshots__/read_config.test.ts.snap b/packages/kbn-config/src/raw/__snapshots__/read_config.test.ts.snap similarity index 100% rename from src/core/server/config/__snapshots__/read_config.test.ts.snap rename to packages/kbn-config/src/raw/__snapshots__/read_config.test.ts.snap diff --git a/src/core/server/config/ensure_deep_object.test.ts b/packages/kbn-config/src/raw/ensure_deep_object.test.ts similarity index 100% rename from src/core/server/config/ensure_deep_object.test.ts rename to packages/kbn-config/src/raw/ensure_deep_object.test.ts diff --git a/src/core/server/config/ensure_deep_object.ts b/packages/kbn-config/src/raw/ensure_deep_object.ts similarity index 100% rename from src/core/server/config/ensure_deep_object.ts rename to packages/kbn-config/src/raw/ensure_deep_object.ts diff --git a/packages/kbn-config/src/raw/index.ts b/packages/kbn-config/src/raw/index.ts new file mode 100644 index 00000000000000..e32920fe64d672 --- /dev/null +++ b/packages/kbn-config/src/raw/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { RawConfigService, RawConfigurationProvider } from './raw_config_service'; +export { getConfigFromFiles } from './read_config'; diff --git a/src/core/server/config/raw_config_service.mock.ts b/packages/kbn-config/src/raw/raw_config_service.mock.ts similarity index 99% rename from src/core/server/config/raw_config_service.mock.ts rename to packages/kbn-config/src/raw/raw_config_service.mock.ts index 73a3b5cc9e4d0c..53ea797dac9e51 100644 --- a/src/core/server/config/raw_config_service.mock.ts +++ b/packages/kbn-config/src/raw/raw_config_service.mock.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ + import type { PublicMethodsOf } from '@kbn/utility-types'; import { RawConfigService } from './raw_config_service'; import { Observable, of } from 'rxjs'; diff --git a/src/core/server/config/raw_config_service.test.mocks.ts b/packages/kbn-config/src/raw/raw_config_service.test.mocks.ts similarity index 100% rename from src/core/server/config/raw_config_service.test.mocks.ts rename to packages/kbn-config/src/raw/raw_config_service.test.mocks.ts diff --git a/src/core/server/config/raw_config_service.test.ts b/packages/kbn-config/src/raw/raw_config_service.test.ts similarity index 98% rename from src/core/server/config/raw_config_service.test.ts rename to packages/kbn-config/src/raw/raw_config_service.test.ts index 8846ea3847f799..444d67985250bc 100644 --- a/src/core/server/config/raw_config_service.test.ts +++ b/packages/kbn-config/src/raw/raw_config_service.test.ts @@ -20,7 +20,7 @@ import { mockGetConfigFromFiles } from './raw_config_service.test.mocks'; import { first } from 'rxjs/operators'; -import { RawConfigService } from '.'; +import { RawConfigService } from './raw_config_service'; const configFile = '/config/kibana.yml'; const anotherConfigFile = '/config/kibana.dev.yml'; diff --git a/src/core/server/config/raw_config_service.ts b/packages/kbn-config/src/raw/raw_config_service.ts similarity index 100% rename from src/core/server/config/raw_config_service.ts rename to packages/kbn-config/src/raw/raw_config_service.ts diff --git a/src/core/server/config/read_config.test.ts b/packages/kbn-config/src/raw/read_config.test.ts similarity index 96% rename from src/core/server/config/read_config.test.ts rename to packages/kbn-config/src/raw/read_config.test.ts index 46b75f28eb987b..89b73c5d4e26ae 100644 --- a/src/core/server/config/read_config.test.ts +++ b/packages/kbn-config/src/raw/read_config.test.ts @@ -20,7 +20,7 @@ import { relative, resolve } from 'path'; import { getConfigFromFiles } from './read_config'; -const fixtureFile = (name: string) => `${__dirname}/__fixtures__/${name}`; +const fixtureFile = (name: string) => resolve(`${__dirname}/../../__fixtures__/${name}`); test('reads single yaml from file system and parses to json', () => { const config = getConfigFromFiles([fixtureFile('config.yml')]); diff --git a/src/core/server/config/read_config.ts b/packages/kbn-config/src/raw/read_config.ts similarity index 100% rename from src/core/server/config/read_config.ts rename to packages/kbn-config/src/raw/read_config.ts diff --git a/src/core/server/config/types.ts b/packages/kbn-config/src/types.ts similarity index 100% rename from src/core/server/config/types.ts rename to packages/kbn-config/src/types.ts diff --git a/packages/kbn-config/tsconfig.json b/packages/kbn-config/tsconfig.json new file mode 100644 index 00000000000000..ba00ddfa6adb6f --- /dev/null +++ b/packages/kbn-config/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "outDir": "./target", + "stripInternal": false, + "declarationMap": true, + "types": ["jest", "node"] + }, + "include": ["./src/**/*.ts"], + "exclude": ["target"] +} diff --git a/packages/kbn-config/yarn.lock b/packages/kbn-config/yarn.lock new file mode 120000 index 00000000000000..3f82ebc9cdbae3 --- /dev/null +++ b/packages/kbn-config/yarn.lock @@ -0,0 +1 @@ +../../yarn.lock \ No newline at end of file diff --git a/packages/kbn-logging/README.md b/packages/kbn-logging/README.md new file mode 100644 index 00000000000000..8bde73722deb00 --- /dev/null +++ b/packages/kbn-logging/README.md @@ -0,0 +1,61 @@ +# kbn-logging + +Base types for the kibana platform logging system. + +Note that this package currently only contains logging types. The only concrete implementation +is still in `core` for now. + +- [Loggers, Appenders and Layouts](#loggers-appenders-and-layouts) +- [Logger hierarchy](#logger-hierarchy) +- [Log level](#log-level) +- [Layouts](#layouts) + +The way logging works in Kibana is inspired by `log4j 2` logging framework used by [Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html#logging). +The main idea is to have consistent logging behaviour (configuration, log format etc.) across the entire Elastic Stack +where possible. + +## Loggers, Appenders and Layouts + +Kibana logging system has three main components: _loggers_, _appenders_ and _layouts_. These components allow us to log +messages according to message type and level, and to control how these messages are formatted and where the final logs +will be displayed or stored. + +__Loggers__ define what logging settings should be applied at the particular context. + +__Appenders__ define where log messages are displayed (eg. stdout or console) and stored (eg. file on the disk). + +__Layouts__ define how log messages are formatted and what type of information they include. + + +## Logger hierarchy + +Every logger has its unique name or context that follows hierarchical naming rule. The logger is considered to be an +ancestor of another logger if its name followed by a `.` is a prefix of the descendant logger name. For example logger +with `a.b` context is an ancestor of logger with `a.b.c` context. All top-level loggers are descendants of special +logger with `root` context that resides at the top of the logger hierarchy. This logger always exists and +fully configured. + +Developer can configure _log level_ and _appenders_ that should be used within particular context. If logger configuration +specifies only _log level_ then _appenders_ configuration will be inherited from the ancestor logger. + +__Note:__ in the current implementation log messages are only forwarded to appenders configured for a particular logger +context or to appenders of the closest ancestor if current logger doesn't have any appenders configured. That means that +we __don't support__ so called _appender additivity_ when log messages are forwarded to _every_ distinct appender within +ancestor chain including `root`. + +## Log level + +Currently we support the following log levels: _all_, _fatal_, _error_, _warn_, _info_, _debug_, _trace_, _off_. +Levels are ordered, so _all_ > _fatal_ > _error_ > _warn_ > _info_ > _debug_ > _trace_ > _off_. +A log record is being logged by the logger if its level is higher than or equal to the level of its logger. Otherwise, +the log record is ignored. + +The _all_ and _off_ levels can be used only in configuration and are just handy shortcuts that allow developer to log every +log record or disable logging entirely for the specific context. + +## Layouts + +Every appender should know exactly how to format log messages before they are written to the console or file on the disk. +This behaviour is controlled by the layouts and configured through `appender.layout` configuration property for every +custom appender. Currently we don't define any default layout for the custom appenders, so one should always make the choice +explicitly. diff --git a/packages/kbn-logging/package.json b/packages/kbn-logging/package.json new file mode 100644 index 00000000000000..922d5627336223 --- /dev/null +++ b/packages/kbn-logging/package.json @@ -0,0 +1,18 @@ +{ + "name": "@kbn/logging", + "version": "1.0.0", + "private": true, + "license": "Apache-2.0", + "main": "./target/index.js", + "scripts": { + "build": "tsc", + "kbn:bootstrap": "yarn build", + "kbn:watch": "yarn build --watch" + }, + "dependencies": { + "@kbn/std": "1.0.0" + }, + "devDependencies": { + "typescript": "4.0.2" + } +} diff --git a/packages/kbn-logging/src/appenders.ts b/packages/kbn-logging/src/appenders.ts new file mode 100644 index 00000000000000..346d3d6dd10687 --- /dev/null +++ b/packages/kbn-logging/src/appenders.ts @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { LogRecord } from './log_record'; + +/** + * Entity that can append `LogRecord` instances to file, stdout, memory or whatever + * is implemented internally. It's supposed to be used by `Logger`. + * @internal + */ +export interface Appender { + append(record: LogRecord): void; +} + +/** + * This interface should be additionally implemented by the `Appender`'s if they are supposed + * to be properly disposed. It's intentionally separated from `Appender` interface so that `Logger` + * that interacts with `Appender` doesn't have control over appender lifetime. + * @internal + */ +export interface DisposableAppender extends Appender { + dispose: () => void; +} diff --git a/packages/kbn-logging/src/index.ts b/packages/kbn-logging/src/index.ts new file mode 100644 index 00000000000000..d06218ac2eeb5c --- /dev/null +++ b/packages/kbn-logging/src/index.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { LogLevel, LogLevelId } from './log_level'; +export { LogRecord } from './log_record'; +export { Logger, LogMeta } from './logger'; +export { LoggerFactory } from './logger_factory'; +export { Layout } from './layout'; +export { Appender, DisposableAppender } from './appenders'; diff --git a/packages/kbn-logging/src/layout.ts b/packages/kbn-logging/src/layout.ts new file mode 100644 index 00000000000000..75556eab88bb67 --- /dev/null +++ b/packages/kbn-logging/src/layout.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { LogRecord } from './log_record'; + +/** + * Entity that can format `LogRecord` instance into a string. + * @internal + */ +export interface Layout { + format(record: LogRecord): string; +} diff --git a/src/core/server/logging/log_level.test.ts b/packages/kbn-logging/src/log_level.test.ts similarity index 100% rename from src/core/server/logging/log_level.test.ts rename to packages/kbn-logging/src/log_level.test.ts diff --git a/src/core/server/logging/log_level.ts b/packages/kbn-logging/src/log_level.ts similarity index 100% rename from src/core/server/logging/log_level.ts rename to packages/kbn-logging/src/log_level.ts diff --git a/src/core/server/logging/log_record.ts b/packages/kbn-logging/src/log_record.ts similarity index 100% rename from src/core/server/logging/log_record.ts rename to packages/kbn-logging/src/log_record.ts diff --git a/packages/kbn-logging/src/logger.ts b/packages/kbn-logging/src/logger.ts new file mode 100644 index 00000000000000..50e002a87fc52f --- /dev/null +++ b/packages/kbn-logging/src/logger.ts @@ -0,0 +1,96 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { LogRecord } from './log_record'; + +/** + * Contextual metadata + * + * @public + */ +export interface LogMeta { + [key: string]: any; +} + +/** + * Logger exposes all the necessary methods to log any type of information and + * this is the interface used by the logging consumers including plugins. + * + * @public + */ +export interface Logger { + /** + * Log messages at the most detailed log level + * + * @param message - The log message + * @param meta - + */ + trace(message: string, meta?: LogMeta): void; + + /** + * Log messages useful for debugging and interactive investigation + * @param message - The log message + * @param meta - + */ + debug(message: string, meta?: LogMeta): void; + + /** + * Logs messages related to general application flow + * @param message - The log message + * @param meta - + */ + info(message: string, meta?: LogMeta): void; + + /** + * Logs abnormal or unexpected errors or messages + * @param errorOrMessage - An Error object or message string to log + * @param meta - + */ + warn(errorOrMessage: string | Error, meta?: LogMeta): void; + + /** + * Logs abnormal or unexpected errors or messages that caused a failure in the application flow + * + * @param errorOrMessage - An Error object or message string to log + * @param meta - + */ + error(errorOrMessage: string | Error, meta?: LogMeta): void; + + /** + * Logs abnormal or unexpected errors or messages that caused an unrecoverable failure + * + * @param errorOrMessage - An Error object or message string to log + * @param meta - + */ + fatal(errorOrMessage: string | Error, meta?: LogMeta): void; + + /** @internal */ + log(record: LogRecord): void; + + /** + * Returns a new {@link Logger} instance extending the current logger context. + * + * @example + * ```typescript + * const logger = loggerFactory.get('plugin', 'service'); // 'plugin.service' context + * const subLogger = logger.get('feature'); // 'plugin.service.feature' context + * ``` + */ + get(...childContextPaths: string[]): Logger; +} diff --git a/src/core/server/logging/logger_factory.ts b/packages/kbn-logging/src/logger_factory.ts similarity index 100% rename from src/core/server/logging/logger_factory.ts rename to packages/kbn-logging/src/logger_factory.ts diff --git a/packages/kbn-logging/src/mocks/index.ts b/packages/kbn-logging/src/mocks/index.ts new file mode 100644 index 00000000000000..c7be3a1b8ce8c7 --- /dev/null +++ b/packages/kbn-logging/src/mocks/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { MockedLogger, loggerMock } from './logger.mock'; diff --git a/packages/kbn-logging/src/mocks/logger.mock.ts b/packages/kbn-logging/src/mocks/logger.mock.ts new file mode 100644 index 00000000000000..1a4c7a2dadf68a --- /dev/null +++ b/packages/kbn-logging/src/mocks/logger.mock.ts @@ -0,0 +1,70 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Logger } from '../logger'; + +export type MockedLogger = jest.Mocked & { context: string[] }; + +const createLoggerMock = (context: string[] = []) => { + const mockLog: MockedLogger = { + context, + debug: jest.fn(), + error: jest.fn(), + fatal: jest.fn(), + info: jest.fn(), + log: jest.fn(), + trace: jest.fn(), + warn: jest.fn(), + get: jest.fn(), + }; + mockLog.get.mockImplementation((...ctx) => ({ + ctx, + ...mockLog, + })); + + return mockLog; +}; + +const clearLoggerMock = (logger: MockedLogger) => { + logger.debug.mockClear(); + logger.info.mockClear(); + logger.warn.mockClear(); + logger.error.mockClear(); + logger.trace.mockClear(); + logger.fatal.mockClear(); + logger.log.mockClear(); +}; + +const collectLoggerMock = (logger: MockedLogger) => { + return { + debug: logger.debug.mock.calls, + error: logger.error.mock.calls, + fatal: logger.fatal.mock.calls, + info: logger.info.mock.calls, + log: logger.log.mock.calls, + trace: logger.trace.mock.calls, + warn: logger.warn.mock.calls, + }; +}; + +export const loggerMock = { + create: createLoggerMock, + clear: clearLoggerMock, + collect: collectLoggerMock, +}; diff --git a/packages/kbn-logging/tsconfig.json b/packages/kbn-logging/tsconfig.json new file mode 100644 index 00000000000000..c55c05de30a528 --- /dev/null +++ b/packages/kbn-logging/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target", + "stripInternal": false, + "declaration": true, + "declarationMap": true, + "types": ["jest", "node"] + }, + "include": ["./src/**/*.ts"] +} diff --git a/packages/kbn-logging/yarn.lock b/packages/kbn-logging/yarn.lock new file mode 120000 index 00000000000000..3f82ebc9cdbae3 --- /dev/null +++ b/packages/kbn-logging/yarn.lock @@ -0,0 +1 @@ +../../yarn.lock \ No newline at end of file diff --git a/packages/kbn-std/package.json b/packages/kbn-std/package.json index 4c67706b45d275..2cc9fd72082be5 100644 --- a/packages/kbn-std/package.json +++ b/packages/kbn-std/package.json @@ -15,7 +15,6 @@ }, "dependencies": { "@kbn/utility-types": "1.0.0", - "lodash": "^4.17.15", - "query-string": "5.1.1" + "lodash": "^4.17.15" } } diff --git a/packages/kbn-std/src/index.ts b/packages/kbn-std/src/index.ts index 7cf70a0e28e2cf..8cffcd43d75379 100644 --- a/packages/kbn-std/src/index.ts +++ b/packages/kbn-std/src/index.ts @@ -24,6 +24,6 @@ export { mapToObject } from './map_to_object'; export { merge } from './merge'; export { pick } from './pick'; export { withTimeout } from './promise'; -export { isRelativeUrl, modifyUrl, URLMeaningfulParts } from './url'; +export { isRelativeUrl, modifyUrl, URLMeaningfulParts, ParsedQuery } from './url'; export { unset } from './unset'; export { getFlattenedObject } from './get_flattened_object'; diff --git a/packages/kbn-std/src/url.ts b/packages/kbn-std/src/url.ts index 910fc8eaa43818..7a0f08130816de 100644 --- a/packages/kbn-std/src/url.ts +++ b/packages/kbn-std/src/url.ts @@ -16,9 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { ParsedQuery } from 'query-string'; + import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; +// duplicate type from 'query-string' to avoid adding the d.ts file to all packages depending on kbn-std +export interface ParsedQuery { + [key: string]: T | T[] | null | undefined; +} + /** * We define our own typings because the current version of @types/node * declares properties to be optional "hostname?: string". diff --git a/packages/kbn-std/tsconfig.json b/packages/kbn-std/tsconfig.json index 5c86ad17a90e96..fd186a6e43d1c0 100644 --- a/packages/kbn-std/tsconfig.json +++ b/packages/kbn-std/tsconfig.json @@ -8,9 +8,6 @@ "declarationMap": true, "types": ["jest", "node"] }, - "include": [ - "./src/**/*.ts", - "../../typings/query_string.d.ts" - ], + "include": ["./src/**/*.ts"], "exclude": ["target"] } diff --git a/src/cli/serve/integration_tests/reload_logging_config.test.ts b/src/cli/serve/integration_tests/reload_logging_config.test.ts index 35391b9b58ecc5..55f71ea2401dbb 100644 --- a/src/cli/serve/integration_tests/reload_logging_config.test.ts +++ b/src/cli/serve/integration_tests/reload_logging_config.test.ts @@ -26,8 +26,7 @@ import Del from 'del'; import * as Rx from 'rxjs'; import { map, filter, take } from 'rxjs/operators'; import { safeDump } from 'js-yaml'; - -import { getConfigFromFiles } from '../../../core/server/config/read_config'; +import { getConfigFromFiles } from '@kbn/config'; const legacyConfig = follow('__fixtures__/reload_logging_config/kibana.test.yml'); const configFileLogConsole = follow( diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index 86cacfe98f7672..1bff6cd9301edb 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -279,6 +279,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` } } navigateToApp={[Function]} + navigateToUrl={[Function]} onIsLockedUpdate={[Function]} recentlyAccessed$={ BehaviorSubject { @@ -2156,6 +2157,7 @@ exports[`CollapsibleNav renders the default nav 1`] = ` } } navigateToApp={[Function]} + navigateToUrl={[Function]} onIsLockedUpdate={[Function]} recentlyAccessed$={ BehaviorSubject { @@ -2391,6 +2393,7 @@ exports[`CollapsibleNav renders the default nav 2`] = ` } } navigateToApp={[Function]} + navigateToUrl={[Function]} onIsLockedUpdate={[Function]} recentlyAccessed$={ BehaviorSubject { @@ -2626,6 +2629,7 @@ exports[`CollapsibleNav renders the default nav 3`] = ` } } navigateToApp={[Function]} + navigateToUrl={[Function]} onIsLockedUpdate={[Function]} recentlyAccessed$={ BehaviorSubject { diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 505f2427942121..d90d0824a12376 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -5380,6 +5380,7 @@ exports[`Header renders 1`] = ` } } navigateToApp={[MockFunction]} + navigateToUrl={[MockFunction]} onIsLockedUpdate={[Function]} recentlyAccessed$={ BehaviorSubject { diff --git a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx index e33e76a45580e7..267e17dc0a9d03 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx @@ -67,6 +67,7 @@ function mockProps() { onIsLockedUpdate: () => {}, closeNav: () => {}, navigateToApp: () => Promise.resolve(), + navigateToUrl: () => Promise.resolve(), customNavLink$: new BehaviorSubject(undefined), }; } diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index 01cdb9c38881a5..b00e82b660e9f3 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -87,6 +87,7 @@ interface Props { onIsLockedUpdate: OnIsLockedUpdate; closeNav: () => void; navigateToApp: InternalApplicationStart['navigateToApp']; + navigateToUrl: InternalApplicationStart['navigateToUrl']; customNavLink$: Rx.Observable; } @@ -100,6 +101,7 @@ export function CollapsibleNav({ onIsLockedUpdate, closeNav, navigateToApp, + navigateToUrl, ...observables }: Props) { const navLinks = useObservable(observables.navLinks$, []).filter((link) => !link.hidden); @@ -217,17 +219,21 @@ export function CollapsibleNav({ listItems={recentlyAccessed.map((link) => { // TODO #64541 // Can remove icon from recent links completely - const { iconType, ...hydratedLink } = createRecentNavLink(link, navLinks, basePath); + const { iconType, onClick, ...hydratedLink } = createRecentNavLink( + link, + navLinks, + basePath, + navigateToUrl + ); return { ...hydratedLink, 'data-test-subj': 'collapsibleNavAppLink--recent', onClick: (event) => { - if (isModifiedOrPrevented(event)) { - return; + if (!isModifiedOrPrevented(event)) { + closeNav(); + onClick(event); } - - closeNav(); }, }; })} diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index 7ec03ea4c6da6e..e01a62a54c34d8 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -184,6 +184,7 @@ export function Header({ homeHref={homeHref} basePath={basePath} navigateToApp={application.navigateToApp} + navigateToUrl={application.navigateToUrl} onIsLockedUpdate={onIsLockedUpdate} closeNav={() => { setIsNavOpen(false); diff --git a/src/core/public/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx index 04d9c5bf7a10a6..a3c73fc4543628 100644 --- a/src/core/public/chrome/ui/header/nav_link.tsx +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem, CoreStart } from '../../..'; import { HttpStart } from '../../../http'; +import { InternalApplicationStart } from '../../../application/types'; import { relativeToAbsolute } from '../../nav_links/to_nav_link'; export const isModifiedOrPrevented = (event: React.MouseEvent) => @@ -87,6 +88,7 @@ export interface RecentNavLink { title: string; 'aria-label': string; iconType?: string; + onClick: React.MouseEventHandler; } /** @@ -102,8 +104,9 @@ export interface RecentNavLink { export function createRecentNavLink( recentLink: ChromeRecentlyAccessedHistoryItem, navLinks: ChromeNavLink[], - basePath: HttpStart['basePath'] -) { + basePath: HttpStart['basePath'], + navigateToUrl: InternalApplicationStart['navigateToUrl'] +): RecentNavLink { const { link, label } = recentLink; const href = relativeToAbsolute(basePath.prepend(link)); const navLink = navLinks.find((nl) => href.startsWith(nl.baseUrl)); @@ -125,5 +128,12 @@ export function createRecentNavLink( title: titleAndAriaLabel, 'aria-label': titleAndAriaLabel, iconType: navLink?.euiIconType, + /* Use href and onClick to support "open in new tab" and SPA navigation in the same link */ + onClick(event: React.MouseEvent) { + if (event.button === 0 && !isModifiedOrPrevented(event)) { + event.preventDefault(); + navigateToUrl(href); + } + }, }; } diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index d0b9e115bd524f..d22c7ae8058278 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -7,6 +7,8 @@ import { Action } from 'history'; import { ApiResponse } from '@elastic/elasticsearch/lib/Transport'; import Boom from 'boom'; +import { ConfigPath } from '@kbn/config'; +import { EnvironmentMode } from '@kbn/config'; import { EuiBreadcrumb } from '@elastic/eui'; import { EuiButtonEmptyProps } from '@elastic/eui'; import { EuiConfirmModalProps } from '@elastic/eui'; @@ -19,8 +21,11 @@ import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType } from 'src/core/server/kibana_config'; import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; +import { Logger } from '@kbn/logging'; +import { LogMeta } from '@kbn/logging'; import { MaybePromise } from '@kbn/utility-types'; import { Observable } from 'rxjs'; +import { PackageInfo } from '@kbn/config'; import { Path } from 'history'; import { PublicMethodsOf } from '@kbn/utility-types'; import { PublicUiSettingsParams as PublicUiSettingsParams_2 } from 'src/core/server/types'; @@ -576,15 +581,7 @@ export interface DocLinksStart { }; } -// @public (undocumented) -export interface EnvironmentMode { - // (undocumented) - dev: boolean; - // (undocumented) - name: 'development' | 'production'; - // (undocumented) - prod: boolean; -} +export { EnvironmentMode } // @public export interface ErrorToastOptions extends ToastOptions { @@ -912,19 +909,7 @@ export interface OverlayStart { openModal: OverlayModalStart['open']; } -// @public (undocumented) -export interface PackageInfo { - // (undocumented) - branch: string; - // (undocumented) - buildNum: number; - // (undocumented) - buildSha: string; - // (undocumented) - dist: boolean; - // (undocumented) - version: string; -} +export { PackageInfo } // @public export interface Plugin { diff --git a/src/core/server/bootstrap.ts b/src/core/server/bootstrap.ts index c0cbe3a39ff548..ff1a5c0340c466 100644 --- a/src/core/server/bootstrap.ts +++ b/src/core/server/bootstrap.ts @@ -60,7 +60,15 @@ export async function bootstrap({ return; } - const env = Env.createDefault({ + // `bootstrap` is exported from the `src/core/server/index` module, + // meaning that any test importing, implicitly or explicitly, anything concrete + // from `core/server` will load `dev-utils`. As some tests are mocking the `fs` package, + // and as `REPO_ROOT` is initialized on the fly when importing `dev-utils` and requires + // the `fs` package, it causes failures. This is why we use a dynamic `require` here. + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { REPO_ROOT } = require('@kbn/utils'); + + const env = Env.createDefault(REPO_ROOT, { configs, cliArgs, isDevClusterMaster: isMaster && cliArgs.dev && features.isClusterModeSupported, diff --git a/src/core/server/capabilities/integration_tests/capabilities_service.test.ts b/src/core/server/capabilities/integration_tests/capabilities_service.test.ts index b4d620965b0471..17f2c77bbf660f 100644 --- a/src/core/server/capabilities/integration_tests/capabilities_service.test.ts +++ b/src/core/server/capabilities/integration_tests/capabilities_service.test.ts @@ -18,16 +18,18 @@ */ import supertest from 'supertest'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { HttpService, InternalHttpServiceSetup } from '../../http'; import { contextServiceMock } from '../../context/context_service.mock'; import { loggingSystemMock } from '../../logging/logging_system.mock'; import { Env } from '../../config'; -import { getEnvOptions } from '../../config/__mocks__/env'; +import { getEnvOptions } from '../../config/mocks'; import { CapabilitiesService, CapabilitiesSetup } from '..'; import { createHttpServer } from '../../http/test_utils'; const coreId = Symbol('core'); -const env = Env.createDefault(getEnvOptions()); + +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); describe('CapabilitiesService', () => { let server: HttpService; diff --git a/src/core/server/config/deprecation/core_deprecations.test.ts b/src/core/server/config/deprecation/core_deprecations.test.ts index adf0f523393660..7a69dc2fa726e0 100644 --- a/src/core/server/config/deprecation/core_deprecations.test.ts +++ b/src/core/server/config/deprecation/core_deprecations.test.ts @@ -17,9 +17,8 @@ * under the License. */ +import { configDeprecationFactory, applyDeprecations } from '@kbn/config'; import { coreDeprecationProvider } from './core_deprecations'; -import { configDeprecationFactory } from './deprecation_factory'; -import { applyDeprecations } from './apply_deprecations'; const initialEnv = { ...process.env }; diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 2b8b8e383da241..2701edcf44e032 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -18,7 +18,7 @@ */ import { has, get } from 'lodash'; -import { ConfigDeprecationProvider, ConfigDeprecation } from './types'; +import { ConfigDeprecationProvider, ConfigDeprecation } from '@kbn/config'; const configPathDeprecation: ConfigDeprecation = (settings, fromPath, log) => { if (has(process.env, 'CONFIG_PATH')) { diff --git a/src/core/server/config/deprecation/index.ts b/src/core/server/config/deprecation/index.ts index f79338665166be..9e8e2e5a2bddf7 100644 --- a/src/core/server/config/deprecation/index.ts +++ b/src/core/server/config/deprecation/index.ts @@ -17,13 +17,4 @@ * under the License. */ -export { - ConfigDeprecation, - ConfigDeprecationWithContext, - ConfigDeprecationLogger, - ConfigDeprecationFactory, - ConfigDeprecationProvider, -} from './types'; -export { configDeprecationFactory } from './deprecation_factory'; export { coreDeprecationProvider } from './core_deprecations'; -export { applyDeprecations } from './apply_deprecations'; diff --git a/src/core/server/config/index.ts b/src/core/server/config/index.ts index 04dc402d35b226..f69a5d081cc8aa 100644 --- a/src/core/server/config/index.ts +++ b/src/core/server/config/index.ts @@ -17,17 +17,25 @@ * under the License. */ -export { ConfigService, IConfigService } from './config_service'; -export { RawConfigService, RawConfigurationProvider } from './raw_config_service'; -export { Config, ConfigPath, isConfigPath, hasConfigPathIntersection } from './config'; -export { ObjectToConfigAdapter } from './object_to_config_adapter'; -export { CliArgs, Env } from './env'; +export { coreDeprecationProvider } from './deprecation'; + export { + ConfigService, + IConfigService, + RawConfigService, + RawConfigurationProvider, + Config, + ConfigPath, + isConfigPath, + hasConfigPathIntersection, + ObjectToConfigAdapter, + CliArgs, + Env, ConfigDeprecation, ConfigDeprecationLogger, ConfigDeprecationProvider, ConfigDeprecationFactory, - coreDeprecationProvider, -} from './deprecation'; - -export { EnvironmentMode, PackageInfo } from './types'; + EnvironmentMode, + PackageInfo, + LegacyObjectToConfigAdapter, +} from '@kbn/config'; diff --git a/src/core/server/config/mocks.ts b/src/core/server/config/mocks.ts new file mode 100644 index 00000000000000..960dda496e6359 --- /dev/null +++ b/src/core/server/config/mocks.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { + getEnvOptions, + rawConfigServiceMock, + configServiceMock, + configMock, +} from '@kbn/config/target/mocks'; diff --git a/src/core/server/core_context.mock.ts b/src/core/server/core_context.mock.ts index 2b887358818e3c..bbf04783278f7e 100644 --- a/src/core/server/core_context.mock.ts +++ b/src/core/server/core_context.mock.ts @@ -16,15 +16,16 @@ * specific language governing permissions and limitations * under the License. */ + +import { REPO_ROOT } from '@kbn/dev-utils'; import { CoreContext } from './core_context'; -import { getEnvOptions } from './config/__mocks__/env'; import { Env, IConfigService } from './config'; +import { configServiceMock, getEnvOptions } from './config/mocks'; import { loggingSystemMock } from './logging/logging_system.mock'; -import { configServiceMock } from './config/config_service.mock'; import { ILoggingSystem } from './logging'; function create({ - env = Env.createDefault(getEnvOptions()), + env = Env.createDefault(REPO_ROOT, getEnvOptions()), logger = loggingSystemMock.create(), configService = configServiceMock.create(), }: { diff --git a/src/core/server/elasticsearch/client/configure_client.test.ts b/src/core/server/elasticsearch/client/configure_client.test.ts index 716e2fd98a5e10..250cfc18a757d9 100644 --- a/src/core/server/elasticsearch/client/configure_client.test.ts +++ b/src/core/server/elasticsearch/client/configure_client.test.ts @@ -16,9 +16,11 @@ * specific language governing permissions and limitations * under the License. */ +import { Buffer } from 'buffer'; +import { Readable } from 'stream'; import { RequestEvent, errors } from '@elastic/elasticsearch'; -import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport'; +import { TransportRequestParams, RequestBody } from '@elastic/elasticsearch/lib/Transport'; import { parseClientOptionsMock, ClientMock } from './configure_client.test.mocks'; import { loggingSystemMock } from '../../logging/logging_system.mock'; @@ -195,40 +197,184 @@ describe('configureClient', () => { `); }); - it('logs each queries if `logQueries` is true', () => { - const client = configureClient( - createFakeConfig({ - logQueries: true, - }), - { logger, scoped: false } - ); + describe('logs each queries if `logQueries` is true', () => { + function createResponseWithBody(body?: RequestBody) { + return createApiResponse({ + body: {}, + statusCode: 200, + params: { + method: 'GET', + path: '/foo', + querystring: { hello: 'dolly' }, + body, + }, + }); + } + + it('when request body is an object', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody({ + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` + Array [ + Array [ + "200 + GET /foo?hello=dolly + {\\"seq_no_primary_term\\":true,\\"query\\":{\\"term\\":{\\"user\\":\\"kimchy\\"}}}", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); + }); - const response = createApiResponse({ - body: {}, - statusCode: 200, - params: { - method: 'GET', - path: '/foo', - querystring: { hello: 'dolly' }, - }, + it('when request body is a string', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody( + JSON.stringify({ + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }) + ); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` + Array [ + Array [ + "200 + GET /foo?hello=dolly + {\\"seq_no_primary_term\\":true,\\"query\\":{\\"term\\":{\\"user\\":\\"kimchy\\"}}}", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); }); - client.emit('response', null, response); + it('when request body is a buffer', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody( + Buffer.from( + JSON.stringify({ + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }) + ) + ); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` + Array [ + Array [ + "200 + GET /foo?hello=dolly + [buffer]", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); + }); - expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` - Array [ + it('when request body is a readable stream', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody( + // @ts-expect-error definition doesn't know about from + Readable.from( + JSON.stringify({ + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }) + ) + ); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` Array [ - "200 - GET /foo - hello=dolly", - Object { - "tags": Array [ - "query", - ], - }, - ], - ] - `); + Array [ + "200 + GET /foo?hello=dolly + [stream]", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); + }); + + it('when request body is not defined', () => { + const client = configureClient( + createFakeConfig({ + logQueries: true, + }), + { logger, scoped: false } + ); + + const response = createResponseWithBody(); + + client.emit('response', null, response); + expect(loggingSystemMock.collect(logger).debug).toMatchInlineSnapshot(` + Array [ + Array [ + "200 + GET /foo?hello=dolly", + Object { + "tags": Array [ + "query", + ], + }, + ], + ] + `); + }); }); it('properly encode queries', () => { @@ -255,8 +401,7 @@ describe('configureClient', () => { Array [ Array [ "200 - GET /foo - city=M%C3%BCnich", + GET /foo?city=M%C3%BCnich", Object { "tags": Array [ "query", @@ -286,6 +431,12 @@ describe('configureClient', () => { method: 'GET', path: '/foo', querystring: { hello: 'dolly' }, + body: { + seq_no_primary_term: true, + query: { + term: { user: 'kimchy' }, + }, + }, }, }); client.emit('response', new errors.ResponseError(response), response); @@ -294,8 +445,8 @@ describe('configureClient', () => { Array [ Array [ "500 - GET /foo - hello=dolly", + GET /foo?hello=dolly + {\\"seq_no_primary_term\\":true,\\"query\\":{\\"term\\":{\\"user\\":\\"kimchy\\"}}}", Object { "tags": Array [ "query", diff --git a/src/core/server/elasticsearch/client/configure_client.ts b/src/core/server/elasticsearch/client/configure_client.ts index a777344813068d..bf07ea604d228d 100644 --- a/src/core/server/elasticsearch/client/configure_client.ts +++ b/src/core/server/elasticsearch/client/configure_client.ts @@ -16,9 +16,11 @@ * specific language governing permissions and limitations * under the License. */ - +import { Buffer } from 'buffer'; import { stringify } from 'querystring'; import { Client } from '@elastic/elasticsearch'; +import { RequestBody } from '@elastic/elasticsearch/lib/Transport'; + import { Logger } from '../../logging'; import { parseClientOptions, ElasticsearchClientConfig } from './client_config'; @@ -48,15 +50,11 @@ const addLogging = (client: Client, logger: Logger, logQueries: boolean) => { // definition is wrong, `params.querystring` can be either a string or an object const querystring = convertQueryString(params.querystring); - - logger.debug( - `${event.statusCode}\n${params.method} ${params.path}${ - querystring ? `\n${querystring}` : '' - }`, - { - tags: ['query'], - } - ); + const url = `${params.path}${querystring ? `?${querystring}` : ''}`; + const body = params.body ? `\n${ensureString(params.body)}` : ''; + logger.debug(`${event.statusCode}\n${params.method} ${url}${body}`, { + tags: ['query'], + }); } }); }; @@ -67,3 +65,10 @@ const convertQueryString = (qs: string | Record | undefined): strin } return stringify(qs); }; + +function ensureString(body: RequestBody): string { + if (typeof body === 'string') return body; + if (Buffer.isBuffer(body)) return '[buffer]'; + if ('readable' in body && body.readable && typeof body._read === 'function') return '[stream]'; + return JSON.stringify(body); +} diff --git a/src/core/server/elasticsearch/elasticsearch_config.test.ts b/src/core/server/elasticsearch/elasticsearch_config.test.ts index 648eaaf8a97f27..3912c68836bb24 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.test.ts @@ -23,8 +23,8 @@ import { mockReadPkcs12Truststore, } from './elasticsearch_config.test.mocks'; +import { applyDeprecations, configDeprecationFactory } from '@kbn/config'; import { ElasticsearchConfig, config } from './elasticsearch_config'; -import { applyDeprecations, configDeprecationFactory } from '../config/deprecation'; const CONFIG_PATH = 'elasticsearch'; diff --git a/src/core/server/elasticsearch/elasticsearch_service.test.ts b/src/core/server/elasticsearch/elasticsearch_service.test.ts index 49f5c8dd987902..ce82410f6061e0 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.test.ts @@ -20,10 +20,10 @@ import { MockLegacyClusterClient, MockClusterClient } from './elasticsearch_service.test.mocks'; import { BehaviorSubject } from 'rxjs'; import { first } from 'rxjs/operators'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; +import { configServiceMock, getEnvOptions } from '../config/mocks'; import { CoreContext } from '../core_context'; -import { configServiceMock } from '../config/config_service.mock'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { httpServiceMock } from '../http/http_service.mock'; import { auditTrailServiceMock } from '../audit_trail/audit_trail_service.mock'; @@ -64,7 +64,7 @@ let mockClusterClientInstance: ReturnType; beforeEach(() => { - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); coreContext = { coreId: Symbol(), env, logger, configService: configService as any }; elasticsearchService = new ElasticsearchService(coreContext); diff --git a/src/core/server/environment/environment_service.test.ts b/src/core/server/environment/environment_service.test.ts index f6cffb5e26a9e1..e4dcfc49c11301 100644 --- a/src/core/server/environment/environment_service.test.ts +++ b/src/core/server/environment/environment_service.test.ts @@ -18,13 +18,14 @@ */ import { BehaviorSubject } from 'rxjs'; + import { EnvironmentService } from './environment_service'; import { resolveInstanceUuid } from './resolve_uuid'; import { createDataFolder } from './create_data_folder'; import { writePidFile } from './write_pid_file'; import { CoreContext } from '../core_context'; -import { configServiceMock } from '../config/config_service.mock'; +import { configServiceMock } from '../config/mocks'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { mockCoreContext } from '../core_context.mock'; diff --git a/src/core/server/http/cookie_session_storage.test.ts b/src/core/server/http/cookie_session_storage.test.ts index 8e5dec7d4eadd7..b7ade0cbde0fcc 100644 --- a/src/core/server/http/cookie_session_storage.test.ts +++ b/src/core/server/http/cookie_session_storage.test.ts @@ -18,20 +18,20 @@ */ import request from 'request'; import supertest from 'supertest'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { ByteSizeValue } from '@kbn/config-schema'; import { BehaviorSubject } from 'rxjs'; import { CoreContext } from '../core_context'; import { HttpService } from './http_service'; import { KibanaRequest } from './router'; - import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; -import { configServiceMock } from '../config/config_service.mock'; + import { contextServiceMock } from '../context/context_service.mock'; import { loggingSystemMock } from '../logging/logging_system.mock'; - +import { getEnvOptions, configServiceMock } from '../config/mocks'; import { httpServerMock } from './http_server.mocks'; + import { createCookieSessionStorageFactory } from './cookie_session_storage'; let server: HttpService; @@ -72,7 +72,7 @@ configService.atPath.mockReturnValue( beforeEach(() => { logger = loggingSystemMock.create(); - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); coreContext = { coreId: Symbol(), env, logger, configService: configService as any }; server = new HttpService(coreContext); diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 3e38f6a6d384dd..f81708145edc46 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -22,7 +22,6 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { CspConfig } from '../csp'; import { mockRouter, RouterMock } from './router/router.mock'; -import { configMock } from '../config/config.mock'; import { InternalHttpServiceSetup, HttpServiceSetup, @@ -37,6 +36,7 @@ import { sessionStorageMock } from './cookie_session_storage.mocks'; import { OnPostAuthToolkit } from './lifecycle/on_post_auth'; import { OnPreAuthToolkit } from './lifecycle/on_pre_auth'; import { OnPreResponseToolkit } from './lifecycle/on_pre_response'; +import { configMock } from '../config/mocks'; type BasePathMocked = jest.Mocked; type AuthMocked = jest.Mocked; diff --git a/src/core/server/http/http_service.test.ts b/src/core/server/http/http_service.test.ts index 3d759b427d9fb0..11cea88fa0dd2f 100644 --- a/src/core/server/http/http_service.test.ts +++ b/src/core/server/http/http_service.test.ts @@ -21,17 +21,18 @@ import { mockHttpServer } from './http_service.test.mocks'; import { noop } from 'lodash'; import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import { getEnvOptions } from '../config/mocks'; import { HttpService } from '.'; import { HttpConfigType, config } from './http_config'; import { httpServerMock } from './http_server.mocks'; import { ConfigService, Env } from '../config'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { contextServiceMock } from '../context/context_service.mock'; -import { getEnvOptions } from '../config/__mocks__/env'; import { config as cspConfig } from '../csp'; const logger = loggingSystemMock.create(); -const env = Env.createDefault(getEnvOptions()); +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); const coreId = Symbol(); const createConfigService = (value: Partial = {}) => { @@ -115,7 +116,7 @@ test('spins up notReady server until started if configured with `autoListen:true const service = new HttpService({ coreId, configService, - env: new Env('.', getEnvOptions()), + env: Env.createDefault(REPO_ROOT, getEnvOptions()), logger, }); @@ -263,7 +264,7 @@ test('does not start http server if process is dev cluster master', async () => const service = new HttpService({ coreId, configService, - env: new Env('.', getEnvOptions({ isDevClusterMaster: true })), + env: Env.createDefault(REPO_ROOT, getEnvOptions({ isDevClusterMaster: true })), logger, }); @@ -288,7 +289,7 @@ test('does not start http server if configured with `autoListen:false`', async ( const service = new HttpService({ coreId, configService, - env: new Env('.', getEnvOptions()), + env: Env.createDefault(REPO_ROOT, getEnvOptions()), logger, }); diff --git a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts index 8d70b5c3ad119d..a964130550bf55 100644 --- a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts +++ b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts @@ -26,7 +26,7 @@ import { HttpService } from '../http_service'; import { HttpServerSetup } from '../http_server'; import { IRouter, RouteRegistrar } from '../router'; -import { configServiceMock } from '../../config/config_service.mock'; +import { configServiceMock } from '../../config/mocks'; import { contextServiceMock } from '../../context/context_service.mock'; // eslint-disable-next-line @typescript-eslint/no-var-requires diff --git a/src/core/server/http/test_utils.ts b/src/core/server/http/test_utils.ts index c3afae108027e1..b4d91926f13f41 100644 --- a/src/core/server/http/test_utils.ts +++ b/src/core/server/http/test_utils.ts @@ -18,16 +18,16 @@ */ import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { ByteSizeValue } from '@kbn/config-schema'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; import { HttpService } from './http_service'; import { CoreContext } from '../core_context'; -import { configServiceMock } from '../config/config_service.mock'; +import { getEnvOptions, configServiceMock } from '../config/mocks'; import { loggingSystemMock } from '../logging/logging_system.mock'; const coreId = Symbol('core'); -const env = Env.createDefault(getEnvOptions()); +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); const logger = loggingSystemMock.create(); diff --git a/src/core/server/legacy/config/ensure_valid_configuration.test.ts b/src/core/server/legacy/config/ensure_valid_configuration.test.ts index d8917b46eba62b..702840b8a0a6a2 100644 --- a/src/core/server/legacy/config/ensure_valid_configuration.test.ts +++ b/src/core/server/legacy/config/ensure_valid_configuration.test.ts @@ -19,7 +19,7 @@ import { ensureValidConfiguration } from './ensure_valid_configuration'; import { getUnusedConfigKeys } from './get_unused_config_keys'; -import { configServiceMock } from '../../config/config_service.mock'; +import { configServiceMock } from '../../config/mocks'; jest.mock('./get_unused_config_keys'); diff --git a/src/core/server/legacy/config/index.ts b/src/core/server/legacy/config/index.ts index b56b83ca324cbc..c8781945d7cbcd 100644 --- a/src/core/server/legacy/config/index.ts +++ b/src/core/server/legacy/config/index.ts @@ -18,4 +18,3 @@ */ export { ensureValidConfiguration } from './ensure_valid_configuration'; -export { LegacyObjectToConfigAdapter } from './legacy_object_to_config_adapter'; diff --git a/src/core/server/legacy/index.ts b/src/core/server/legacy/index.ts index 208e9b11672536..6b0963e3129c6b 100644 --- a/src/core/server/legacy/index.ts +++ b/src/core/server/legacy/index.ts @@ -18,7 +18,7 @@ */ /** @internal */ -export { LegacyObjectToConfigAdapter, ensureValidConfiguration } from './config'; +export { ensureValidConfiguration } from './config'; /** @internal */ export { LegacyInternals } from './legacy_internals'; /** @internal */ diff --git a/src/core/server/legacy/integration_tests/logging.test.ts b/src/core/server/legacy/integration_tests/logging.test.ts index 2ebe17ea92978c..dfcecd3f36b361 100644 --- a/src/core/server/legacy/integration_tests/logging.test.ts +++ b/src/core/server/legacy/integration_tests/logging.test.ts @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ + +import { LegacyLoggingConfig } from '@kbn/config'; import * as kbnTestServer from '../../../test_helpers/kbn_server'; import { @@ -23,8 +25,6 @@ import { getLegacyPlatformLogsFromMock, } from '../../logging/integration_tests/utils'; -import { LegacyLoggingConfig } from '../config/legacy_object_to_config_adapter'; - function createRoot(legacyLoggingConfig: LegacyLoggingConfig = {}) { return kbnTestServer.createRoot({ migrations: { skip: true }, // otherwise stuck in polling ES diff --git a/src/core/server/legacy/legacy_internals.test.ts b/src/core/server/legacy/legacy_internals.test.ts index 67f2f433d4570d..935e36a989a0cf 100644 --- a/src/core/server/legacy/legacy_internals.test.ts +++ b/src/core/server/legacy/legacy_internals.test.ts @@ -19,7 +19,7 @@ import { Server } from 'hapi'; -import { configMock } from '../config/config.mock'; +import { configMock } from '../config/mocks'; import { httpServiceMock } from '../http/http_service.mock'; import { httpServerMock } from '../http/http_server.mocks'; import { findLegacyPluginSpecsMock } from './legacy_service.test.mocks'; diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index d1caccc733689c..a6fe95deb3979e 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -25,16 +25,16 @@ import { } from './legacy_service.test.mocks'; import { BehaviorSubject, throwError } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; // @ts-expect-error js file to remove TS dependency on cli import { ClusterManager as MockClusterManager } from './cluster_manager'; import KbnServer from '../../../legacy/server/kbn_server'; import { Config, Env, ObjectToConfigAdapter } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; import { BasePathProxyServer } from '../http'; import { DiscoveredPlugin } from '../plugins'; -import { configServiceMock } from '../config/config_service.mock'; +import { getEnvOptions, configServiceMock } from '../config/mocks'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { contextServiceMock } from '../context/context_service.mock'; import { httpServiceMock } from '../http/http_service.mock'; @@ -69,7 +69,7 @@ let environmentSetup: ReturnType { coreId = Symbol(); - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); configService = configServiceMock.create(); environmentSetup = environmentServiceMock.createSetupContract(); @@ -365,6 +365,7 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => { const devClusterLegacyService = new LegacyService({ coreId, env: Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { silent: true, basePath: false }, isDevClusterMaster: true, @@ -393,6 +394,7 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => { const devClusterLegacyService = new LegacyService({ coreId, env: Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { quiet: true, basePath: true }, isDevClusterMaster: true, diff --git a/src/core/server/legacy/logging/appenders/legacy_appender.test.ts b/src/core/server/legacy/logging/appenders/legacy_appender.test.ts index 538d987e781d06..697e5bc37d6027 100644 --- a/src/core/server/legacy/logging/appenders/legacy_appender.test.ts +++ b/src/core/server/legacy/logging/appenders/legacy_appender.test.ts @@ -19,8 +19,7 @@ jest.mock('../legacy_logging_server'); -import { LogLevel } from '../../../logging/log_level'; -import { LogRecord } from '../../../logging/log_record'; +import { LogRecord, LogLevel } from '../../../logging'; import { LegacyLoggingServer } from '../legacy_logging_server'; import { LegacyAppender } from './legacy_appender'; diff --git a/src/core/server/legacy/logging/appenders/legacy_appender.ts b/src/core/server/legacy/logging/appenders/legacy_appender.ts index a5d36423ba4c69..67337c7d676297 100644 --- a/src/core/server/legacy/logging/appenders/legacy_appender.ts +++ b/src/core/server/legacy/logging/appenders/legacy_appender.ts @@ -18,8 +18,7 @@ */ import { schema } from '@kbn/config-schema'; -import { DisposableAppender } from '../../../logging/appenders/appenders'; -import { LogRecord } from '../../../logging/log_record'; +import { DisposableAppender, LogRecord } from '../../../logging'; import { LegacyLoggingServer } from '../legacy_logging_server'; import { LegacyVars } from '../../types'; diff --git a/src/core/server/legacy/logging/legacy_logging_server.test.ts b/src/core/server/legacy/logging/legacy_logging_server.test.ts index 6dca3a199728e6..2f6c34e0fc5d6b 100644 --- a/src/core/server/legacy/logging/legacy_logging_server.test.ts +++ b/src/core/server/legacy/logging/legacy_logging_server.test.ts @@ -20,7 +20,7 @@ jest.mock('../../../../legacy/server/config'); jest.mock('../../../../legacy/server/logging'); -import { LogLevel } from '../../logging/log_level'; +import { LogLevel } from '../../logging'; import { LegacyLoggingServer } from './legacy_logging_server'; test('correctly forwards log records.', () => { diff --git a/src/core/server/legacy/logging/legacy_logging_server.ts b/src/core/server/legacy/logging/legacy_logging_server.ts index 4a7fea87cf69f8..096dbe54565e11 100644 --- a/src/core/server/legacy/logging/legacy_logging_server.ts +++ b/src/core/server/legacy/logging/legacy_logging_server.ts @@ -23,8 +23,7 @@ import Podium from 'podium'; import { Config } from '../../../../legacy/server/config'; // @ts-expect-error: implicit any for JS file import { setupLogging } from '../../../../legacy/server/logging'; -import { LogLevel } from '../../logging/log_level'; -import { LogRecord } from '../../logging/log_record'; +import { LogLevel, LogRecord } from '../../logging'; import { LegacyVars } from '../../types'; export const metadataSymbol = Symbol('log message with metadata'); diff --git a/src/core/server/logging/appenders/appenders.ts b/src/core/server/logging/appenders/appenders.ts index 9c19ee2bd8be54..4e6920c50686c5 100644 --- a/src/core/server/logging/appenders/appenders.ts +++ b/src/core/server/logging/appenders/appenders.ts @@ -19,13 +19,13 @@ import { schema } from '@kbn/config-schema'; import { assertNever } from '@kbn/std'; +import { DisposableAppender } from '@kbn/logging'; import { LegacyAppender, LegacyAppenderConfig, } from '../../legacy/logging/appenders/legacy_appender'; import { Layouts } from '../layouts/layouts'; -import { LogRecord } from '../log_record'; import { ConsoleAppender, ConsoleAppenderConfig } from './console/console_appender'; import { FileAppender, FileAppenderConfig } from './file/file_appender'; @@ -44,25 +44,6 @@ export const appendersSchema = schema.oneOf([ /** @public */ export type AppenderConfigType = ConsoleAppenderConfig | FileAppenderConfig | LegacyAppenderConfig; -/** - * Entity that can append `LogRecord` instances to file, stdout, memory or whatever - * is implemented internally. It's supposed to be used by `Logger`. - * @internal - */ -export interface Appender { - append(record: LogRecord): void; -} - -/** - * This interface should be additionally implemented by the `Appender`'s if they are supposed - * to be properly disposed. It's intentionally separated from `Appender` interface so that `Logger` - * that interacts with `Appender` doesn't have control over appender lifetime. - * @internal - */ -export interface DisposableAppender extends Appender { - dispose: () => void; -} - /** @internal */ export class Appenders { public static configSchema = appendersSchema; diff --git a/src/core/server/logging/appenders/buffer/buffer_appender.test.ts b/src/core/server/logging/appenders/buffer/buffer_appender.test.ts index 49d70db8d5d43e..7981aef64e5890 100644 --- a/src/core/server/logging/appenders/buffer/buffer_appender.test.ts +++ b/src/core/server/logging/appenders/buffer/buffer_appender.test.ts @@ -17,8 +17,7 @@ * under the License. */ -import { LogLevel } from '../../log_level'; -import { LogRecord } from '../../log_record'; +import { LogLevel, LogRecord } from '@kbn/logging'; import { BufferAppender } from './buffer_appender'; test('`flush()` does not return any record buffered at the beginning.', () => { diff --git a/src/core/server/logging/appenders/buffer/buffer_appender.ts b/src/core/server/logging/appenders/buffer/buffer_appender.ts index 7024d3e5d16df1..9e3a9d0f910f89 100644 --- a/src/core/server/logging/appenders/buffer/buffer_appender.ts +++ b/src/core/server/logging/appenders/buffer/buffer_appender.ts @@ -17,8 +17,7 @@ * under the License. */ -import { LogRecord } from '../../log_record'; -import { DisposableAppender } from '../appenders'; +import { LogRecord, DisposableAppender } from '@kbn/logging'; /** * Simple appender that just buffers `LogRecord` instances it receives. It is a *reserved* appender diff --git a/src/core/server/logging/appenders/console/console_appender.test.ts b/src/core/server/logging/appenders/console/console_appender.test.ts index 6e30df1cfb65c4..0601ac10167ac5 100644 --- a/src/core/server/logging/appenders/console/console_appender.test.ts +++ b/src/core/server/logging/appenders/console/console_appender.test.ts @@ -29,8 +29,7 @@ jest.mock('../../layouts/layouts', () => { }; }); -import { LogLevel } from '../../log_level'; -import { LogRecord } from '../../log_record'; +import { LogRecord, LogLevel } from '@kbn/logging'; import { ConsoleAppender } from './console_appender'; test('`configSchema` creates correct schema.', () => { diff --git a/src/core/server/logging/appenders/console/console_appender.ts b/src/core/server/logging/appenders/console/console_appender.ts index a54674b1d347ce..dc491fcff664c5 100644 --- a/src/core/server/logging/appenders/console/console_appender.ts +++ b/src/core/server/logging/appenders/console/console_appender.ts @@ -18,10 +18,8 @@ */ import { schema } from '@kbn/config-schema'; - -import { Layout, Layouts, LayoutConfigType } from '../../layouts/layouts'; -import { LogRecord } from '../../log_record'; -import { DisposableAppender } from '../appenders'; +import { Layout, LogRecord, DisposableAppender } from '@kbn/logging'; +import { Layouts, LayoutConfigType } from '../../layouts/layouts'; const { literal, object } = schema; diff --git a/src/core/server/logging/appenders/file/file_appender.test.ts b/src/core/server/logging/appenders/file/file_appender.test.ts index bff60029faf116..645455c5ae04ce 100644 --- a/src/core/server/logging/appenders/file/file_appender.test.ts +++ b/src/core/server/logging/appenders/file/file_appender.test.ts @@ -19,8 +19,7 @@ import { mockCreateWriteStream } from './file_appender.test.mocks'; -import { LogLevel } from '../../log_level'; -import { LogRecord } from '../../log_record'; +import { LogRecord, LogLevel } from '@kbn/logging'; import { FileAppender } from './file_appender'; const tickMs = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/src/core/server/logging/appenders/file/file_appender.ts b/src/core/server/logging/appenders/file/file_appender.ts index a0e484cd87c8f5..b1712bd4e9412d 100644 --- a/src/core/server/logging/appenders/file/file_appender.ts +++ b/src/core/server/logging/appenders/file/file_appender.ts @@ -18,11 +18,10 @@ */ import { schema } from '@kbn/config-schema'; +import { LogRecord, Layout, DisposableAppender } from '@kbn/logging'; import { createWriteStream, WriteStream } from 'fs'; -import { Layout, Layouts, LayoutConfigType } from '../../layouts/layouts'; -import { LogRecord } from '../../log_record'; -import { DisposableAppender } from '../appenders'; +import { Layouts, LayoutConfigType } from '../../layouts/layouts'; export interface FileAppenderConfig { kind: 'file'; diff --git a/src/core/server/logging/index.ts b/src/core/server/logging/index.ts index 94719720302817..01f153cae9e2dc 100644 --- a/src/core/server/logging/index.ts +++ b/src/core/server/logging/index.ts @@ -17,10 +17,17 @@ * under the License. */ -export { Logger, LogMeta } from './logger'; -export { LoggerFactory } from './logger_factory'; -export { LogRecord } from './log_record'; -export { LogLevel } from './log_level'; +export { + DisposableAppender, + Appender, + LogRecord, + Layout, + LoggerFactory, + LogMeta, + Logger, + LogLevelId, + LogLevel, +} from '@kbn/logging'; export { config, LoggingConfigType, diff --git a/src/core/server/logging/layouts/conversions/date.ts b/src/core/server/logging/layouts/conversions/date.ts index d3ed54fb982402..3a43c0ffcd389f 100644 --- a/src/core/server/logging/layouts/conversions/date.ts +++ b/src/core/server/logging/layouts/conversions/date.ts @@ -18,9 +18,9 @@ */ import moment from 'moment-timezone'; import { last } from 'lodash'; +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; const dateRegExp = /%date({(?[^}]+)})?({(?[^}]+)})?/g; diff --git a/src/core/server/logging/layouts/conversions/level.ts b/src/core/server/logging/layouts/conversions/level.ts index 58b271140eff5b..83208242dc2581 100644 --- a/src/core/server/logging/layouts/conversions/level.ts +++ b/src/core/server/logging/layouts/conversions/level.ts @@ -18,10 +18,9 @@ */ import chalk from 'chalk'; +import { LogRecord, LogLevel } from '@kbn/logging'; import { Conversion } from './type'; -import { LogLevel } from '../../log_level'; -import { LogRecord } from '../../log_record'; const LEVEL_COLORS = new Map([ [LogLevel.Fatal, chalk.red], diff --git a/src/core/server/logging/layouts/conversions/logger.ts b/src/core/server/logging/layouts/conversions/logger.ts index debb1737ab95a3..e63976052443bf 100644 --- a/src/core/server/logging/layouts/conversions/logger.ts +++ b/src/core/server/logging/layouts/conversions/logger.ts @@ -18,9 +18,9 @@ */ import chalk from 'chalk'; +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; export const LoggerConversion: Conversion = { pattern: /%logger/g, diff --git a/src/core/server/logging/layouts/conversions/message.ts b/src/core/server/logging/layouts/conversions/message.ts index f8c5e68ada4fbf..73d85532a5a900 100644 --- a/src/core/server/logging/layouts/conversions/message.ts +++ b/src/core/server/logging/layouts/conversions/message.ts @@ -17,8 +17,8 @@ * under the License. */ +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; export const MessageConversion: Conversion = { pattern: /%message/g, diff --git a/src/core/server/logging/layouts/conversions/meta.ts b/src/core/server/logging/layouts/conversions/meta.ts index ee8c207389fbe0..b78db41b7e9695 100644 --- a/src/core/server/logging/layouts/conversions/meta.ts +++ b/src/core/server/logging/layouts/conversions/meta.ts @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ + +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; export const MetaConversion: Conversion = { pattern: /%meta/g, diff --git a/src/core/server/logging/layouts/conversions/pid.ts b/src/core/server/logging/layouts/conversions/pid.ts index 37d34a4f1cf8b7..f6902005f5668d 100644 --- a/src/core/server/logging/layouts/conversions/pid.ts +++ b/src/core/server/logging/layouts/conversions/pid.ts @@ -17,8 +17,8 @@ * under the License. */ +import { LogRecord } from '@kbn/logging'; import { Conversion } from './type'; -import { LogRecord } from '../../log_record'; export const PidConversion: Conversion = { pattern: /%pid/g, diff --git a/src/core/server/logging/layouts/conversions/type.ts b/src/core/server/logging/layouts/conversions/type.ts index a57a1f954e53ae..be172a0a98f7d2 100644 --- a/src/core/server/logging/layouts/conversions/type.ts +++ b/src/core/server/logging/layouts/conversions/type.ts @@ -16,7 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import { LogRecord } from 'kibana/server'; + +import { LogRecord } from '@kbn/logging'; export interface Conversion { pattern: RegExp; diff --git a/src/core/server/logging/layouts/json_layout.test.ts b/src/core/server/logging/layouts/json_layout.test.ts index 6cda1e4806aa85..f476e3f217278b 100644 --- a/src/core/server/logging/layouts/json_layout.test.ts +++ b/src/core/server/logging/layouts/json_layout.test.ts @@ -17,8 +17,7 @@ * under the License. */ -import { LogLevel } from '../log_level'; -import { LogRecord } from '../log_record'; +import { LogLevel, LogRecord } from '@kbn/logging'; import { JsonLayout } from './json_layout'; const timestamp = new Date(Date.UTC(2012, 1, 1, 14, 30, 22, 11)); diff --git a/src/core/server/logging/layouts/json_layout.ts b/src/core/server/logging/layouts/json_layout.ts index 37eb6b8c4806e8..7573d0b8374161 100644 --- a/src/core/server/logging/layouts/json_layout.ts +++ b/src/core/server/logging/layouts/json_layout.ts @@ -20,9 +20,7 @@ import moment from 'moment-timezone'; import { merge } from 'lodash'; import { schema } from '@kbn/config-schema'; - -import { LogRecord } from '../log_record'; -import { Layout } from './layouts'; +import { LogRecord, Layout } from '@kbn/logging'; const { literal, object } = schema; diff --git a/src/core/server/logging/layouts/layouts.ts b/src/core/server/logging/layouts/layouts.ts index 03e8adbee6311f..faf05e4d1e3477 100644 --- a/src/core/server/logging/layouts/layouts.ts +++ b/src/core/server/logging/layouts/layouts.ts @@ -18,9 +18,9 @@ */ import { schema } from '@kbn/config-schema'; +import { Layout } from '@kbn/logging'; import { assertNever } from '@kbn/std'; -import { LogRecord } from '../log_record'; import { JsonLayout, JsonLayoutConfigType } from './json_layout'; import { PatternLayout, PatternLayoutConfigType } from './pattern_layout'; @@ -28,14 +28,6 @@ const { oneOf } = schema; export type LayoutConfigType = PatternLayoutConfigType | JsonLayoutConfigType; -/** - * Entity that can format `LogRecord` instance into a string. - * @internal - */ -export interface Layout { - format(record: LogRecord): string; -} - /** @internal */ export class Layouts { public static configSchema = oneOf([JsonLayout.configSchema, PatternLayout.configSchema]); diff --git a/src/core/server/logging/layouts/pattern_layout.test.ts b/src/core/server/logging/layouts/pattern_layout.test.ts index cce55b147e0ed3..d37ee3c87d08fd 100644 --- a/src/core/server/logging/layouts/pattern_layout.test.ts +++ b/src/core/server/logging/layouts/pattern_layout.test.ts @@ -17,9 +17,8 @@ * under the License. */ +import { LogLevel, LogRecord } from '@kbn/logging'; import { stripAnsiSnapshotSerializer } from '../../../test_helpers/strip_ansi_snapshot_serializer'; -import { LogLevel } from '../log_level'; -import { LogRecord } from '../log_record'; import { PatternLayout, patternSchema } from './pattern_layout'; const timestamp = new Date(Date.UTC(2012, 1, 1, 14, 30, 22, 11)); diff --git a/src/core/server/logging/layouts/pattern_layout.ts b/src/core/server/logging/layouts/pattern_layout.ts index 5dfc8aca77f181..2ca444f54b4994 100644 --- a/src/core/server/logging/layouts/pattern_layout.ts +++ b/src/core/server/logging/layouts/pattern_layout.ts @@ -18,9 +18,8 @@ */ import { schema } from '@kbn/config-schema'; +import { LogRecord, Layout } from '@kbn/logging'; -import { LogRecord } from '../log_record'; -import { Layout } from './layouts'; import { Conversion, LoggerConversion, diff --git a/src/core/server/logging/logger.mock.ts b/src/core/server/logging/logger.mock.ts index a3bb07ea4c0951..5af967ecd430eb 100644 --- a/src/core/server/logging/logger.mock.ts +++ b/src/core/server/logging/logger.mock.ts @@ -17,30 +17,4 @@ * under the License. */ -import { Logger } from './logger'; - -export type MockedLogger = jest.Mocked & { context: string[] }; - -const createLoggerMock = (context: string[] = []) => { - const mockLog: MockedLogger = { - context, - debug: jest.fn(), - error: jest.fn(), - fatal: jest.fn(), - info: jest.fn(), - log: jest.fn(), - trace: jest.fn(), - warn: jest.fn(), - get: jest.fn(), - }; - mockLog.get.mockImplementation((...ctx) => ({ - ctx, - ...mockLog, - })); - - return mockLog; -}; - -export const loggerMock = { - create: createLoggerMock, -}; +export { loggerMock, MockedLogger } from '@kbn/logging/target/mocks'; diff --git a/src/core/server/logging/logger.test.ts b/src/core/server/logging/logger.test.ts index 1cc00a254300b3..1796519ff65e50 100644 --- a/src/core/server/logging/logger.test.ts +++ b/src/core/server/logging/logger.test.ts @@ -17,9 +17,8 @@ * under the License. */ +import { LogLevel, Appender } from '@kbn/logging'; import { LoggingConfig } from './logging_config'; -import { Appender } from './appenders/appenders'; -import { LogLevel } from './log_level'; import { BaseLogger } from './logger'; const context = LoggingConfig.getLoggerContext(['context', 'parent', 'child']); diff --git a/src/core/server/logging/logger.ts b/src/core/server/logging/logger.ts index 285998c23832ce..6861072ef3b8b0 100644 --- a/src/core/server/logging/logger.ts +++ b/src/core/server/logging/logger.ts @@ -17,86 +17,7 @@ * under the License. */ -import { Appender } from './appenders/appenders'; -import { LogLevel } from './log_level'; -import { LogRecord } from './log_record'; -import { LoggerFactory } from './logger_factory'; - -/** - * Contextual metadata - * - * @public - */ -export interface LogMeta { - [key: string]: any; -} - -/** - * Logger exposes all the necessary methods to log any type of information and - * this is the interface used by the logging consumers including plugins. - * - * @public - */ -export interface Logger { - /** - * Log messages at the most detailed log level - * - * @param message - The log message - * @param meta - - */ - trace(message: string, meta?: LogMeta): void; - - /** - * Log messages useful for debugging and interactive investigation - * @param message - The log message - * @param meta - - */ - debug(message: string, meta?: LogMeta): void; - - /** - * Logs messages related to general application flow - * @param message - The log message - * @param meta - - */ - info(message: string, meta?: LogMeta): void; - - /** - * Logs abnormal or unexpected errors or messages - * @param errorOrMessage - An Error object or message string to log - * @param meta - - */ - warn(errorOrMessage: string | Error, meta?: LogMeta): void; - - /** - * Logs abnormal or unexpected errors or messages that caused a failure in the application flow - * - * @param errorOrMessage - An Error object or message string to log - * @param meta - - */ - error(errorOrMessage: string | Error, meta?: LogMeta): void; - - /** - * Logs abnormal or unexpected errors or messages that caused an unrecoverable failure - * - * @param errorOrMessage - An Error object or message string to log - * @param meta - - */ - fatal(errorOrMessage: string | Error, meta?: LogMeta): void; - - /** @internal */ - log(record: LogRecord): void; - - /** - * Returns a new {@link Logger} instance extending the current logger context. - * - * @example - * ```typescript - * const logger = loggerFactory.get('plugin', 'service'); // 'plugin.service' context - * const subLogger = logger.get('feature'); // 'plugin.service.feature' context - * ``` - */ - get(...childContextPaths: string[]): Logger; -} +import { Appender, LogLevel, LogRecord, LoggerFactory, LogMeta, Logger } from '@kbn/logging'; function isError(x: any): x is Error { return x instanceof Error; diff --git a/src/core/server/logging/logger_adapter.ts b/src/core/server/logging/logger_adapter.ts index 14e5712e55c584..4ce65bf3302e7c 100644 --- a/src/core/server/logging/logger_adapter.ts +++ b/src/core/server/logging/logger_adapter.ts @@ -17,8 +17,7 @@ * under the License. */ -import { LogRecord } from './log_record'; -import { Logger, LogMeta } from './logger'; +import { LogRecord, Logger, LogMeta } from '@kbn/logging'; /** @internal */ export class LoggerAdapter implements Logger { diff --git a/src/core/server/logging/logging_service.ts b/src/core/server/logging/logging_service.ts index 09051f8f077023..f2b609f2258c76 100644 --- a/src/core/server/logging/logging_service.ts +++ b/src/core/server/logging/logging_service.ts @@ -18,10 +18,10 @@ */ import { Observable, Subscription } from 'rxjs'; +import { Logger } from '@kbn/logging'; import { CoreService } from '../../types'; import { LoggingConfig, LoggerContextConfigInput } from './logging_config'; import { ILoggingSystem } from './logging_system'; -import { Logger } from './logger'; import { CoreContext } from '../core_context'; /** diff --git a/src/core/server/logging/logging_system.mock.ts b/src/core/server/logging/logging_system.mock.ts index ac1e9b5196002e..6ea784be5411fa 100644 --- a/src/core/server/logging/logging_system.mock.ts +++ b/src/core/server/logging/logging_system.mock.ts @@ -18,8 +18,8 @@ */ // Test helpers to simplify mocking logs and collecting all their outputs +import { LoggerFactory } from '@kbn/logging'; import { ILoggingSystem } from './logging_system'; -import { LoggerFactory } from './logger_factory'; import { loggerMock, MockedLogger } from './logger.mock'; const createLoggingSystemMock = () => { @@ -48,15 +48,7 @@ const createLoggingSystemMock = () => { const collectLoggingSystemMock = (loggerFactory: LoggerFactory) => { const mockLog = loggerFactory.get() as MockedLogger; - return { - debug: mockLog.debug.mock.calls, - error: mockLog.error.mock.calls, - fatal: mockLog.fatal.mock.calls, - info: mockLog.info.mock.calls, - log: mockLog.log.mock.calls, - trace: mockLog.trace.mock.calls, - warn: mockLog.warn.mock.calls, - }; + return loggerMock.collect(mockLog); }; const clearLoggingSystemMock = (loggerFactory: LoggerFactory) => { @@ -67,13 +59,7 @@ const clearLoggingSystemMock = (loggerFactory: LoggerFactory) => { mockedLoggerFactory.stop.mockClear(); const mockLog = loggerFactory.get() as MockedLogger; - mockLog.debug.mockClear(); - mockLog.info.mockClear(); - mockLog.warn.mockClear(); - mockLog.error.mockClear(); - mockLog.trace.mockClear(); - mockLog.fatal.mockClear(); - mockLog.log.mockClear(); + loggerMock.clear(mockLog); }; export const loggingSystemMock = { diff --git a/src/core/server/logging/logging_system.ts b/src/core/server/logging/logging_system.ts index a3970b17209508..8bc22bdf537afb 100644 --- a/src/core/server/logging/logging_system.ts +++ b/src/core/server/logging/logging_system.ts @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ + import type { PublicMethodsOf } from '@kbn/utility-types'; -import { Appenders, DisposableAppender } from './appenders/appenders'; +import { DisposableAppender, LogLevel, Logger, LoggerFactory } from '@kbn/logging'; +import { Appenders } from './appenders/appenders'; import { BufferAppender } from './appenders/buffer/buffer_appender'; -import { LogLevel } from './log_level'; -import { BaseLogger, Logger } from './logger'; +import { BaseLogger } from './logger'; import { LoggerAdapter } from './logger_adapter'; -import { LoggerFactory } from './logger_factory'; import { LoggingConfigType, LoggerConfigType, diff --git a/src/core/server/metrics/metrics_service.test.ts b/src/core/server/metrics/metrics_service.test.ts index f2019de7b6cabf..269931d0e33ad8 100644 --- a/src/core/server/metrics/metrics_service.test.ts +++ b/src/core/server/metrics/metrics_service.test.ts @@ -18,10 +18,11 @@ */ import moment from 'moment'; + +import { configServiceMock } from '../config/mocks'; import { mockOpsCollector } from './metrics_service.test.mocks'; import { MetricsService } from './metrics_service'; import { mockCoreContext } from '../core_context.mock'; -import { configServiceMock } from '../config/config_service.mock'; import { httpServiceMock } from '../http/http_service.mock'; import { take } from 'rxjs/operators'; diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 9627fe62d8c05a..7e001ffe28100e 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -18,6 +18,7 @@ */ import { of } from 'rxjs'; import { duration } from 'moment'; +import { ByteSizeValue } from '@kbn/config-schema'; import { PluginInitializerContext, CoreSetup, CoreStart, StartServicesAccessor } from '.'; import { loggingSystemMock } from './logging/logging_system.mock'; import { loggingServiceMock } from './logging/logging_service.mock'; @@ -37,10 +38,10 @@ import { environmentServiceMock } from './environment/environment_service.mock'; import { statusServiceMock } from './status/status_service.mock'; import { auditTrailServiceMock } from './audit_trail/audit_trail_service.mock'; +export { configServiceMock } from './config/mocks'; export { httpServerMock } from './http/http_server.mocks'; export { httpResourcesMock } from './http_resources/http_resources_service.mock'; export { sessionStorageMock } from './http/cookie_session_storage.mocks'; -export { configServiceMock } from './config/config_service.mock'; export { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock'; export { httpServiceMock } from './http/http_service.mock'; export { loggingSystemMock } from './logging/logging_system.mock'; @@ -68,6 +69,9 @@ export function pluginInitializerContextConfigMock(config: T) { startupTimeout: duration('30s'), }, path: { data: '/tmp' }, + savedObjects: { + maxImportPayloadBytes: new ByteSizeValue(10485760), + }, }; const mock: jest.Mocked['config']> = { diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts index eb8dc1e6095826..3634a7ed2c277b 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.test.mocks.ts @@ -17,6 +17,12 @@ * under the License. */ +const realFs = jest.requireActual('fs'); + export const mockReadFile = jest.fn(); const mockStat = jest.fn(); -jest.mock('fs', () => ({ readFile: mockReadFile, stat: mockStat })); +jest.doMock('fs', () => ({ + ...realFs, + readFile: mockReadFile, + stat: mockStat, +})); diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts index 836aabf881474f..cb9f5982d0f198 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts @@ -17,9 +17,9 @@ * under the License. */ -import { PluginDiscoveryErrorType } from './plugin_discovery_error'; - import { mockReadFile } from './plugin_manifest_parser.test.mocks'; + +import { PluginDiscoveryErrorType } from './plugin_discovery_error'; import { loggingSystemMock } from '../../logging/logging_system.mock'; import { resolve } from 'path'; diff --git a/src/core/server/plugins/discovery/plugins_discovery.test.mocks.ts b/src/core/server/plugins/discovery/plugins_discovery.test.mocks.ts index 83accc06cb9956..131ee58e21a81e 100644 --- a/src/core/server/plugins/discovery/plugins_discovery.test.mocks.ts +++ b/src/core/server/plugins/discovery/plugins_discovery.test.mocks.ts @@ -17,5 +17,10 @@ * under the License. */ -export const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] }); -jest.mock('../../../../../package.json', () => mockPackage); +export const mockPackage = { + raw: {}, +}; + +jest.doMock('load-json-file', () => ({ + sync: () => mockPackage.raw, +})); diff --git a/src/core/server/plugins/discovery/plugins_discovery.test.ts b/src/core/server/plugins/discovery/plugins_discovery.test.ts index 4894f19e38df42..0dfc5599e8007c 100644 --- a/src/core/server/plugins/discovery/plugins_discovery.test.ts +++ b/src/core/server/plugins/discovery/plugins_discovery.test.ts @@ -17,18 +17,19 @@ * under the License. */ +// must be before mocks imports to avoid conflicting with `REPO_ROOT` accessor. +import { REPO_ROOT } from '@kbn/dev-utils'; import { mockPackage } from './plugins_discovery.test.mocks'; import mockFs from 'mock-fs'; import { loggingSystemMock } from '../../logging/logging_system.mock'; +import { getEnvOptions, rawConfigServiceMock } from '../../config/mocks'; import { first, map, toArray } from 'rxjs/operators'; import { resolve } from 'path'; import { ConfigService, Env } from '../../config'; -import { getEnvOptions } from '../../config/__mocks__/env'; import { PluginsConfig, PluginsConfigType, config } from '../plugins_config'; import type { InstanceInfo } from '../plugin_context'; import { discover } from './plugins_discovery'; -import { rawConfigServiceMock } from '../../config/raw_config_service.mock'; import { CoreContext } from '../../core_context'; const KIBANA_ROOT = process.cwd(); @@ -94,6 +95,7 @@ describe('plugins discovery system', () => { }; env = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { envName: 'development' }, }) @@ -382,6 +384,7 @@ describe('plugins discovery system', () => { const extraPluginTestPath = resolve(process.cwd(), 'my-extra-plugin'); env = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false, envName: 'development' }, }) @@ -409,6 +412,7 @@ describe('plugins discovery system', () => { const extraPluginTestPath = resolve(process.cwd(), 'my-extra-plugin'); env = Env.createDefault( + REPO_ROOT, getEnvOptions({ cliArgs: { dev: false, envName: 'production' }, }) diff --git a/src/core/server/plugins/integration_tests/plugins_service.test.mocks.ts b/src/core/server/plugins/integration_tests/plugins_service.test.mocks.ts index d81a7eb5db4ae6..ed73fe9b99be61 100644 --- a/src/core/server/plugins/integration_tests/plugins_service.test.mocks.ts +++ b/src/core/server/plugins/integration_tests/plugins_service.test.mocks.ts @@ -17,11 +17,13 @@ * under the License. */ -export const mockPackage = new Proxy( - { raw: { __dirname: '/tmp' } as any }, - { get: (obj, prop) => obj.raw[prop] } -); -jest.mock('../../../../core/server/utils/package_json', () => ({ pkg: mockPackage })); +export const mockPackage = { + raw: { __dirname: '/tmp' } as any, +}; + +jest.doMock('load-json-file', () => ({ + sync: () => mockPackage.raw, +})); export const mockDiscover = jest.fn(); jest.mock('../discovery/plugins_discovery', () => ({ discover: mockDiscover })); diff --git a/src/core/server/plugins/integration_tests/plugins_service.test.ts b/src/core/server/plugins/integration_tests/plugins_service.test.ts index 5a216b75a83b9a..93fee05016ff21 100644 --- a/src/core/server/plugins/integration_tests/plugins_service.test.ts +++ b/src/core/server/plugins/integration_tests/plugins_service.test.ts @@ -17,15 +17,16 @@ * under the License. */ +// must be before mocks imports to avoid conflicting with `REPO_ROOT` accessor. +import { REPO_ROOT } from '@kbn/dev-utils'; import { mockPackage, mockDiscover } from './plugins_service.test.mocks'; import { join } from 'path'; import { PluginsService } from '../plugins_service'; import { ConfigPath, ConfigService, Env } from '../../config'; -import { getEnvOptions } from '../../config/__mocks__/env'; +import { getEnvOptions, rawConfigServiceMock } from '../../config/mocks'; import { BehaviorSubject, from } from 'rxjs'; -import { rawConfigServiceMock } from '../../config/raw_config_service.mock'; import { config } from '../plugins_config'; import { loggingSystemMock } from '../../logging/logging_system.mock'; import { environmentServiceMock } from '../../environment/environment_service.mock'; @@ -93,7 +94,7 @@ describe('PluginsService', () => { }, }; - const env = Env.createDefault(getEnvOptions()); + const env = Env.createDefault(REPO_ROOT, getEnvOptions()); const config$ = new BehaviorSubject>({ plugins: { initialize: true, diff --git a/src/core/server/plugins/plugin.test.ts b/src/core/server/plugins/plugin.test.ts index 1108ffc2481615..ae22ead2aa4be9 100644 --- a/src/core/server/plugins/plugin.test.ts +++ b/src/core/server/plugins/plugin.test.ts @@ -19,14 +19,14 @@ import { join } from 'path'; import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { schema } from '@kbn/config-schema'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; import { CoreContext } from '../core_context'; import { coreMock } from '../mocks'; -import { configServiceMock } from '../config/config_service.mock'; import { loggingSystemMock } from '../logging/logging_system.mock'; +import { getEnvOptions, configServiceMock } from '../config/mocks'; import { PluginWrapper } from './plugin'; import { PluginManifest } from './types'; @@ -77,7 +77,7 @@ const setupDeps = coreMock.createInternalSetup(); beforeEach(() => { coreId = Symbol('core'); - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); instanceInfo = { uuid: 'instance-uuid', }; diff --git a/src/core/server/plugins/plugin_context.test.ts b/src/core/server/plugins/plugin_context.test.ts index 578c5f39d71ea1..cb4e8f20be982f 100644 --- a/src/core/server/plugins/plugin_context.test.ts +++ b/src/core/server/plugins/plugin_context.test.ts @@ -19,15 +19,16 @@ import { duration } from 'moment'; import { first } from 'rxjs/operators'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { createPluginInitializerContext, InstanceInfo } from './plugin_context'; import { CoreContext } from '../core_context'; import { Env } from '../config'; import { loggingSystemMock } from '../logging/logging_system.mock'; -import { rawConfigServiceMock } from '../config/raw_config_service.mock'; -import { getEnvOptions } from '../config/__mocks__/env'; +import { rawConfigServiceMock, getEnvOptions } from '../config/mocks'; import { PluginManifest } from './types'; import { Server } from '../server'; import { fromRoot } from '../utils'; +import { ByteSizeValue } from '@kbn/config-schema'; const logger = loggingSystemMock.create(); @@ -58,7 +59,7 @@ describe('createPluginInitializerContext', () => { instanceInfo = { uuid: 'instance-uuid', }; - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); const config$ = rawConfigServiceMock.create({ rawConfig: {} }); server = new Server(config$, env, logger); await server.setupCoreConfig(); @@ -93,6 +94,7 @@ describe('createPluginInitializerContext', () => { startupTimeout: duration(5, 's'), }, path: { data: fromRoot('data') }, + savedObjects: { maxImportPayloadBytes: new ByteSizeValue(10485760) }, }); }); diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 7153e5f838829e..ab3f471fd79425 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -35,6 +35,7 @@ import { ElasticsearchConfigType, config as elasticsearchConfig, } from '../elasticsearch/elasticsearch_config'; +import { SavedObjectsConfigType, savedObjectsConfig } from '../saved_objects/saved_objects_config'; import { CoreSetup, CoreStart } from '..'; export interface InstanceInfo { @@ -91,16 +92,18 @@ export function createPluginInitializerContext( * Note: naming not final here, it will be renamed in a near future (https://github.com/elastic/kibana/issues/46240) * @deprecated */ - globalConfig$: combineLatest( + globalConfig$: combineLatest([ coreContext.configService.atPath(kibanaConfig.path), coreContext.configService.atPath(elasticsearchConfig.path), - coreContext.configService.atPath(pathConfig.path) - ).pipe( - map(([kibana, elasticsearch, path]) => + coreContext.configService.atPath(pathConfig.path), + coreContext.configService.atPath(savedObjectsConfig.path), + ]).pipe( + map(([kibana, elasticsearch, path, savedObjects]) => deepFreeze({ kibana: pick(kibana, SharedGlobalConfigKeys.kibana), elasticsearch: pick(elasticsearch, SharedGlobalConfigKeys.elasticsearch), path: pick(path, SharedGlobalConfigKeys.path), + savedObjects: pick(savedObjects, SharedGlobalConfigKeys.savedObjects), }) ) ), diff --git a/src/core/server/plugins/plugins_config.test.ts b/src/core/server/plugins/plugins_config.test.ts index 180d6093e04045..2a64e79d19bdad 100644 --- a/src/core/server/plugins/plugins_config.test.ts +++ b/src/core/server/plugins/plugins_config.test.ts @@ -17,13 +17,14 @@ * under the License. */ +import { REPO_ROOT } from '@kbn/dev-utils'; +import { getEnvOptions } from '../config/mocks'; import { PluginsConfig, PluginsConfigType } from './plugins_config'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; describe('PluginsConfig', () => { it('retrieves additionalPluginPaths from config.paths when in production mode', () => { - const env = Env.createDefault(getEnvOptions({ cliArgs: { dev: false } })); + const env = Env.createDefault(REPO_ROOT, getEnvOptions({ cliArgs: { dev: false } })); const rawConfig: PluginsConfigType = { initialize: true, paths: ['some-path', 'another-path'], @@ -33,7 +34,7 @@ describe('PluginsConfig', () => { }); it('retrieves additionalPluginPaths from config.paths when in development mode', () => { - const env = Env.createDefault(getEnvOptions({ cliArgs: { dev: true } })); + const env = Env.createDefault(REPO_ROOT, getEnvOptions({ cliArgs: { dev: true } })); const rawConfig: PluginsConfigType = { initialize: true, paths: ['some-path', 'another-path'], diff --git a/src/core/server/plugins/plugins_service.test.mocks.ts b/src/core/server/plugins/plugins_service.test.mocks.ts index 8d4ba12c8375c0..15e4187ef95edd 100644 --- a/src/core/server/plugins/plugins_service.test.mocks.ts +++ b/src/core/server/plugins/plugins_service.test.mocks.ts @@ -17,11 +17,25 @@ * under the License. */ -export const mockPackage = new Proxy( - { raw: { __dirname: '/tmp' } as any }, - { get: (obj, prop) => obj.raw[prop] } -); -jest.mock('../../../core/server/utils/package_json', () => ({ pkg: mockPackage })); +import { REPO_ROOT } from '@kbn/utils'; +import { resolve } from 'path'; + +const loadJsonFile = jest.requireActual('load-json-file'); +const kibanaPackagePath = resolve(REPO_ROOT, 'package.json'); + +export const mockPackage = { + raw: { __dirname: '/tmp', name: 'kibana' } as any, +}; + +jest.doMock('load-json-file', () => ({ + ...loadJsonFile, + sync: (path: string) => { + if (path === kibanaPackagePath) { + return mockPackage.raw; + } + return loadJsonFile.sync(path); + }, +})); export const mockDiscover = jest.fn(); jest.mock('./discovery/plugins_discovery', () => ({ discover: mockDiscover })); diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 5e613343c302fa..d36fd2251176a2 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -22,11 +22,10 @@ import { mockDiscover, mockPackage } from './plugins_service.test.mocks'; import { resolve, join } from 'path'; import { BehaviorSubject, from } from 'rxjs'; import { schema } from '@kbn/config-schema'; -import { createAbsolutePathSerializer } from '@kbn/dev-utils'; +import { createAbsolutePathSerializer, REPO_ROOT } from '@kbn/dev-utils'; import { ConfigPath, ConfigService, Env } from '../config'; -import { rawConfigServiceMock } from '../config/raw_config_service.mock'; -import { getEnvOptions } from '../config/__mocks__/env'; +import { rawConfigServiceMock, getEnvOptions } from '../config/mocks'; import { coreMock } from '../mocks'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { environmentServiceMock } from '../environment/environment_service.mock'; @@ -116,7 +115,7 @@ describe('PluginsService', () => { }; coreId = Symbol('core'); - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); config$ = new BehaviorSubject>({ plugins: { initialize: true } }); const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); diff --git a/src/core/server/plugins/plugins_system.test.ts b/src/core/server/plugins/plugins_system.test.ts index 71ac31db13f928..ae9267ca5cf607 100644 --- a/src/core/server/plugins/plugins_system.test.ts +++ b/src/core/server/plugins/plugins_system.test.ts @@ -24,10 +24,10 @@ import { import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; +import { configServiceMock, getEnvOptions } from '../config/mocks'; import { CoreContext } from '../core_context'; -import { configServiceMock } from '../config/config_service.mock'; import { loggingSystemMock } from '../logging/logging_system.mock'; import { PluginWrapper } from './plugin'; @@ -74,7 +74,7 @@ const setupDeps = coreMock.createInternalSetup(); const startDeps = coreMock.createInternalStart(); beforeEach(() => { - env = Env.createDefault(getEnvOptions()); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); coreContext = { coreId: Symbol(), env, logger, configService: configService as any }; diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index 34d5e044222eb8..9de181124a3494 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -26,6 +26,7 @@ import { ConfigPath, EnvironmentMode, PackageInfo, ConfigDeprecationProvider } f import { LoggerFactory } from '../logging'; import { KibanaConfigType } from '../kibana_config'; import { ElasticsearchConfigType } from '../elasticsearch/elasticsearch_config'; +import { SavedObjectsConfigType } from '../saved_objects/saved_objects_config'; import { CoreSetup, CoreStart } from '..'; /** @@ -263,6 +264,7 @@ export const SharedGlobalConfigKeys = { kibana: ['index', 'autocompleteTerminateAfter', 'autocompleteTimeout'] as const, elasticsearch: ['shardTimeout', 'requestTimeout', 'pingTimeout', 'startupTimeout'] as const, path: ['data'] as const, + savedObjects: ['maxImportPayloadBytes'] as const, }; /** @@ -272,6 +274,7 @@ export type SharedGlobalConfig = RecursiveReadonly<{ kibana: Pick; elasticsearch: Pick; path: Pick; + savedObjects: Pick; }>; /** diff --git a/src/core/server/root/index.test.mocks.ts b/src/core/server/root/index.test.mocks.ts index ef4a40fa3db2d0..d81b1575adb888 100644 --- a/src/core/server/root/index.test.mocks.ts +++ b/src/core/server/root/index.test.mocks.ts @@ -23,15 +23,14 @@ jest.doMock('../logging/logging_system', () => ({ LoggingSystem: jest.fn(() => logger), })); -import { configServiceMock } from '../config/config_service.mock'; -export const configService = configServiceMock.create(); -jest.doMock('../config/config_service', () => ({ - ConfigService: jest.fn(() => configService), -})); +const realKbnConfig = jest.requireActual('@kbn/config'); -import { rawConfigServiceMock } from '../config/raw_config_service.mock'; +import { configServiceMock, rawConfigServiceMock } from '../config/mocks'; +export const configService = configServiceMock.create(); export const rawConfigService = rawConfigServiceMock.create(); -jest.doMock('../config/raw_config_service', () => ({ +jest.doMock('@kbn/config', () => ({ + ...realKbnConfig, + ConfigService: jest.fn(() => configService), RawConfigService: jest.fn(() => rawConfigService), })); diff --git a/src/core/server/root/index.test.ts b/src/core/server/root/index.test.ts index 5b853903ea4be1..4d3fe24c7ba830 100644 --- a/src/core/server/root/index.test.ts +++ b/src/core/server/root/index.test.ts @@ -21,11 +21,12 @@ import { rawConfigService, configService, logger, mockServer } from './index.tes import { BehaviorSubject } from 'rxjs'; import { filter, first } from 'rxjs/operators'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import { getEnvOptions } from '../config/mocks'; import { Root } from '.'; import { Env } from '../config'; -import { getEnvOptions } from '../config/__mocks__/env'; -const env = new Env('.', getEnvOptions()); +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); let mockConsoleError: jest.SpyInstance; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 4b32db3b5eec84..1dcf8a22e9cfd4 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -21,6 +21,7 @@ import { CatSnapshotsParams } from 'elasticsearch'; import { CatTasksParams } from 'elasticsearch'; import { CatThreadPoolParams } from 'elasticsearch'; import { ClearScrollParams } from 'elasticsearch'; +import { CliArgs } from '@kbn/config'; import { Client } from 'elasticsearch'; import { ClientOptions } from '@elastic/elasticsearch'; import { ClusterAllocationExplainParams } from 'elasticsearch'; @@ -31,7 +32,13 @@ import { ClusterPutSettingsParams } from 'elasticsearch'; import { ClusterRerouteParams } from 'elasticsearch'; import { ClusterStateParams } from 'elasticsearch'; import { ClusterStatsParams } from 'elasticsearch'; +import { ConfigDeprecation } from '@kbn/config'; +import { ConfigDeprecationFactory } from '@kbn/config'; +import { ConfigDeprecationLogger } from '@kbn/config'; +import { ConfigDeprecationProvider } from '@kbn/config'; import { ConfigOptions } from 'elasticsearch'; +import { ConfigPath } from '@kbn/config'; +import { ConfigService } from '@kbn/config'; import { CountParams } from 'elasticsearch'; import { CreateDocumentParams } from 'elasticsearch'; import { DeleteDocumentByQueryParams } from 'elasticsearch'; @@ -40,6 +47,7 @@ import { DeleteScriptParams } from 'elasticsearch'; import { DeleteTemplateParams } from 'elasticsearch'; import { DetailedPeerCertificate } from 'tls'; import { Duration } from 'moment'; +import { EnvironmentMode } from '@kbn/config'; import { ExistsParams } from 'elasticsearch'; import { ExplainParams } from 'elasticsearch'; import { FieldStatsParams } from 'elasticsearch'; @@ -94,6 +102,11 @@ import { IngestPutPipelineParams } from 'elasticsearch'; import { IngestSimulateParams } from 'elasticsearch'; import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType } from 'src/core/server/kibana_config'; +import { Logger } from '@kbn/logging'; +import { LoggerFactory } from '@kbn/logging'; +import { LogLevel } from '@kbn/logging'; +import { LogMeta } from '@kbn/logging'; +import { LogRecord } from '@kbn/logging'; import { MGetParams } from 'elasticsearch'; import { MGetResponse } from 'elasticsearch'; import { MSearchParams } from 'elasticsearch'; @@ -105,6 +118,7 @@ import { NodesInfoParams } from 'elasticsearch'; import { NodesStatsParams } from 'elasticsearch'; import { ObjectType } from '@kbn/config-schema'; import { Observable } from 'rxjs'; +import { PackageInfo } from '@kbn/config'; import { PathConfigType } from '@kbn/utils'; import { PeerCertificate } from 'tls'; import { PingParams } from 'elasticsearch'; @@ -363,45 +377,17 @@ export const config: { }; }; -// @public -export type ConfigDeprecation = (config: Record, fromPath: string, logger: ConfigDeprecationLogger) => Record; +export { ConfigDeprecation } -// @public -export interface ConfigDeprecationFactory { - rename(oldKey: string, newKey: string): ConfigDeprecation; - renameFromRoot(oldKey: string, newKey: string, silent?: boolean): ConfigDeprecation; - unused(unusedKey: string): ConfigDeprecation; - unusedFromRoot(unusedKey: string): ConfigDeprecation; -} +export { ConfigDeprecationFactory } -// @public -export type ConfigDeprecationLogger = (message: string) => void; +export { ConfigDeprecationLogger } -// @public -export type ConfigDeprecationProvider = (factory: ConfigDeprecationFactory) => ConfigDeprecation[]; +export { ConfigDeprecationProvider } -// @public (undocumented) -export type ConfigPath = string | string[]; +export { ConfigPath } -// @internal (undocumented) -export class ConfigService { - // Warning: (ae-forgotten-export) The symbol "RawConfigurationProvider" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "Env" needs to be exported by the entry point index.d.ts - constructor(rawConfigProvider: RawConfigurationProvider, env: Env, logger: LoggerFactory); - addDeprecationProvider(path: ConfigPath, provider: ConfigDeprecationProvider): void; - atPath(path: ConfigPath): Observable; - // Warning: (ae-forgotten-export) The symbol "Config" needs to be exported by the entry point index.d.ts - getConfig$(): Observable; - // (undocumented) - getUnusedPaths(): Promise; - // (undocumented) - getUsedPaths(): Promise; - // (undocumented) - isEnabledAtPath(path: ConfigPath): Promise; - optionalAtPath(path: ConfigPath): Observable; - setSchema(path: ConfigPath, schema: Type): Promise; - validate(): Promise; - } +export { ConfigService } // @public export interface ContextSetup { @@ -672,15 +658,7 @@ export interface ElasticsearchStatusMeta { warningNodes: NodesVersionCompatibility['warningNodes']; } -// @public (undocumented) -export interface EnvironmentMode { - // (undocumented) - dev: boolean; - // (undocumented) - name: 'development' | 'production'; - // (undocumented) - prod: boolean; -} +export { EnvironmentMode } // @public export interface ErrorHttpResponseOptions { @@ -1418,18 +1396,7 @@ export interface LegacyUiExports { // @public export type LifecycleResponseFactory = typeof lifecycleResponseFactory; -// @public -export interface Logger { - debug(message: string, meta?: LogMeta): void; - error(errorOrMessage: string | Error, meta?: LogMeta): void; - fatal(errorOrMessage: string | Error, meta?: LogMeta): void; - get(...childContextPaths: string[]): Logger; - info(message: string, meta?: LogMeta): void; - // @internal (undocumented) - log(record: LogRecord): void; - trace(message: string, meta?: LogMeta): void; - warn(errorOrMessage: string | Error, meta?: LogMeta): void; -} +export { Logger } // Warning: (ae-forgotten-export) The symbol "loggerSchema" needs to be exported by the entry point index.d.ts // @@ -1444,69 +1411,18 @@ export interface LoggerContextConfigInput { loggers?: LoggerConfigType[]; } -// @public -export interface LoggerFactory { - get(...contextParts: string[]): Logger; -} +export { LoggerFactory } // @public export interface LoggingServiceSetup { configure(config$: Observable): void; } -// @internal -export class LogLevel { - // (undocumented) - static readonly All: LogLevel; - // (undocumented) - static readonly Debug: LogLevel; - // (undocumented) - static readonly Error: LogLevel; - // (undocumented) - static readonly Fatal: LogLevel; - static fromId(level: LogLevelId): LogLevel; - // Warning: (ae-forgotten-export) The symbol "LogLevelId" needs to be exported by the entry point index.d.ts - // - // (undocumented) - readonly id: LogLevelId; - // (undocumented) - static readonly Info: LogLevel; - // (undocumented) - static readonly Off: LogLevel; - supports(level: LogLevel): boolean; - // (undocumented) - static readonly Trace: LogLevel; - // (undocumented) - readonly value: number; - // (undocumented) - static readonly Warn: LogLevel; -} +export { LogLevel } -// @public -export interface LogMeta { - // (undocumented) - [key: string]: any; -} +export { LogMeta } -// @internal -export interface LogRecord { - // (undocumented) - context: string; - // (undocumented) - error?: Error; - // (undocumented) - level: LogLevel; - // (undocumented) - message: string; - // (undocumented) - meta?: { - [name: string]: any; - }; - // (undocumented) - pid: number; - // (undocumented) - timestamp: Date; -} +export { LogRecord } // @public export interface MetricsServiceSetup { @@ -1668,19 +1584,7 @@ export interface OpsServerMetrics { }; } -// @public (undocumented) -export interface PackageInfo { - // (undocumented) - branch: string; - // (undocumented) - buildNum: number; - // (undocumented) - buildSha: string; - // (undocumented) - dist: boolean; - // (undocumented) - version: string; -} +export { PackageInfo } // @public export interface Plugin { @@ -1694,6 +1598,7 @@ export interface Plugin { + // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported deprecations?: ConfigDeprecationProvider; exposeToBrowser?: { [P in keyof T]?: boolean; @@ -1731,6 +1636,7 @@ export interface PluginInitializerContext { // @public export interface PluginManifest { + // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported readonly configPath: ConfigPath; // @deprecated readonly extraPublicDirs?: string[]; @@ -2761,6 +2667,7 @@ export type SharedGlobalConfig = RecursiveReadonly<{ kibana: Pick; elasticsearch: Pick; path: Pick; + savedObjects: Pick; }>; // @public @@ -2846,7 +2753,8 @@ export const validBodyOutput: readonly ["data", "stream"]; // // src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts // src/core/server/legacy/types.ts:135:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:272:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts -// src/core/server/plugins/types.ts:272:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:274:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:274:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts +// src/core/server/plugins/types.ts:277:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts ``` diff --git a/src/core/server/server.test.mocks.ts b/src/core/server/server.test.mocks.ts index 471e482a20e962..77f2787b75412c 100644 --- a/src/core/server/server.test.mocks.ts +++ b/src/core/server/server.test.mocks.ts @@ -41,9 +41,12 @@ jest.mock('./legacy/legacy_service', () => ({ LegacyService: jest.fn(() => mockLegacyService), })); -import { configServiceMock } from './config/config_service.mock'; +const realKbnConfig = jest.requireActual('@kbn/config'); + +import { configServiceMock } from './config/mocks'; export const mockConfigService = configServiceMock.create(); -jest.doMock('./config/config_service', () => ({ +jest.doMock('@kbn/config', () => ({ + ...realKbnConfig, ConfigService: jest.fn(() => mockConfigService), })); diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index 8bf16d9130ef5d..3258840d09df22 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -35,14 +35,14 @@ import { } from './server.test.mocks'; import { BehaviorSubject } from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import { rawConfigServiceMock, getEnvOptions } from './config/mocks'; import { Env } from './config'; import { Server } from './server'; -import { getEnvOptions } from './config/__mocks__/env'; import { loggingSystemMock } from './logging/logging_system.mock'; -import { rawConfigServiceMock } from './config/raw_config_service.mock'; -const env = new Env('.', getEnvOptions()); +const env = Env.createDefault(REPO_ROOT, getEnvOptions()); const logger = loggingSystemMock.create(); const rawConfigService = rawConfigServiceMock.create({}); diff --git a/src/core/server/types.ts b/src/core/server/types.ts index 2433aad1a2be57..f8d2f635671fa8 100644 --- a/src/core/server/types.ts +++ b/src/core/server/types.ts @@ -22,4 +22,4 @@ export { PluginOpaqueId } from './plugins/types'; export * from './saved_objects/types'; export * from './ui_settings/types'; export * from './legacy/types'; -export { EnvironmentMode, PackageInfo } from './config/types'; +export type { EnvironmentMode, PackageInfo } from '@kbn/config'; diff --git a/src/core/test_helpers/kbn_server.ts b/src/core/test_helpers/kbn_server.ts index 488c4b919d3e41..93a173cdbdece4 100644 --- a/src/core/test_helpers/kbn_server.ts +++ b/src/core/test_helpers/kbn_server.ts @@ -17,7 +17,7 @@ * under the License. */ import { Client } from 'elasticsearch'; -import { ToolingLog } from '@kbn/dev-utils'; +import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils'; import { createLegacyEsTestCluster, DEFAULT_SUPERUSER_PASS, @@ -66,7 +66,7 @@ export function createRootWithSettings( settings: Record, cliArgs: Partial = {} ) { - const env = Env.createDefault({ + const env = Env.createDefault(REPO_ROOT, { configs: [], cliArgs: { dev: false, diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile index d235bfe9d6fbc3..24649a52b729bd 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/Dockerfile @@ -85,7 +85,6 @@ RUN groupadd --gid 1000 kibana && \ useradd --uid 1000 --gid 1000 \ --home-dir /usr/share/kibana --no-create-home \ kibana -USER kibana LABEL org.label-schema.build-date="{{dockerBuildDate}}" \ org.label-schema.license="{{license}}" \ @@ -115,8 +114,13 @@ LABEL name="Kibana" \ release="1" \ summary="Kibana" \ description="Your window into the Elastic Stack." + +RUN mkdir /licenses && \ + cp LICENSE.txt /licenses/LICENSE {{/ubi}} +USER kibana + ENTRYPOINT ["/usr/local/bin/dumb-init", "--"] CMD ["/usr/local/bin/kibana-docker"] diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts index c79c7900148eaa..d3b3a73a4b50f5 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts @@ -113,15 +113,13 @@ describe('IndexPatterns', () => { test('caches saved objects', async () => { await indexPatterns.getIds(); await indexPatterns.getTitles(); - await indexPatterns.getFields(['id', 'title']); expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); }); test('can refresh the saved objects caches', async () => { await indexPatterns.getIds(); await indexPatterns.getTitles(true); - await indexPatterns.getFields(['id', 'title'], true); - expect(savedObjectsClient.find).toHaveBeenCalledTimes(3); + expect(savedObjectsClient.find).toHaveBeenCalledTimes(2); }); test('deletes the index pattern', async () => { diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 88a7e9f6cef4c1..47484f8ec75bbe 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -41,8 +41,6 @@ const indexPatternCache = createIndexPatternCache(); const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; const savedObjectType = 'index-pattern'; -type IndexPatternCachedFieldType = 'id' | 'title'; - export interface IndexPatternSavedObjectAttrs { title: string; } @@ -116,22 +114,6 @@ export class IndexPatternsService { return this.savedObjectsCache.map((obj) => obj?.attributes?.title); }; - getFields = async (fields: IndexPatternCachedFieldType[], refresh: boolean = false) => { - if (!this.savedObjectsCache || refresh) { - await this.refreshSavedObjectsCache(); - } - if (!this.savedObjectsCache) { - return []; - } - return this.savedObjectsCache.map((obj: Record) => { - const result: Partial> = {}; - fields.forEach( - (f: IndexPatternCachedFieldType) => (result[f] = obj[f] || obj?.attributes?.[f]) - ); - return result; - }); - }; - getFieldsForTimePattern = (options: GetFieldsOptions = {}) => { return this.apiClient.getFieldsForTimePattern(options); }; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index a8fbbcb08d3588..7ce53a219fb443 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -17,6 +17,7 @@ import { CoreSetup as CoreSetup_2 } from 'kibana/public'; import { CoreStart } from 'kibana/public'; import { CoreStart as CoreStart_2 } from 'src/core/public'; import { Ensure } from '@kbn/utility-types'; +import { EnvironmentMode } from '@kbn/config'; import { ErrorToastOptions } from 'src/core/public/notifications'; import { EuiBreadcrumb } from '@elastic/eui'; import { EuiButtonEmptyProps } from '@elastic/eui'; @@ -38,12 +39,15 @@ import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType } from 'src/core/server/kibana_config'; import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; +import { Logger } from '@kbn/logging'; +import { LogMeta } from '@kbn/logging'; import { MaybePromise } from '@kbn/utility-types'; import { METRIC_TYPE } from '@kbn/analytics'; import { Moment } from 'moment'; import moment from 'moment'; import { NameList } from 'elasticsearch'; import { Observable } from 'rxjs'; +import { PackageInfo } from '@kbn/config'; import { Path } from 'history'; import { Plugin as Plugin_2 } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index f5b1214185f535..14f176176f647e 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -32,6 +32,7 @@ import { ClusterPutSettingsParams } from 'elasticsearch'; import { ClusterRerouteParams } from 'elasticsearch'; import { ClusterStateParams } from 'elasticsearch'; import { ClusterStatsParams } from 'elasticsearch'; +import { ConfigDeprecationProvider } from '@kbn/config'; import { CoreSetup } from 'src/core/server'; import { CoreSetup as CoreSetup_2 } from 'kibana/server'; import { CoreStart } from 'src/core/server'; @@ -43,6 +44,7 @@ import { DeleteScriptParams } from 'elasticsearch'; import { DeleteTemplateParams } from 'elasticsearch'; import { Duration } from 'moment'; import { Ensure } from '@kbn/utility-types'; +import { EnvironmentMode } from '@kbn/config'; import { ErrorToastOptions } from 'src/core/public/notifications'; import { ExistsParams } from 'elasticsearch'; import { ExplainParams } from 'elasticsearch'; @@ -104,7 +106,10 @@ import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaConfigType as KibanaConfigType_2 } from 'src/core/server/kibana_config'; import { KibanaRequest } from 'kibana/server'; import { LegacyAPICaller as LegacyAPICaller_2 } from 'kibana/server'; +import { Logger } from '@kbn/logging'; import { Logger as Logger_2 } from 'kibana/server'; +import { LoggerFactory } from '@kbn/logging'; +import { LogMeta } from '@kbn/logging'; import { MGetParams } from 'elasticsearch'; import { MGetResponse } from 'elasticsearch'; import { Moment } from 'moment'; @@ -117,6 +122,7 @@ import { NodesHotThreadsParams } from 'elasticsearch'; import { NodesInfoParams } from 'elasticsearch'; import { NodesStatsParams } from 'elasticsearch'; import { Observable } from 'rxjs'; +import { PackageInfo } from '@kbn/config'; import { PathConfigType } from '@kbn/utils'; import { PingParams } from 'elasticsearch'; import { Plugin as Plugin_2 } from 'src/core/server'; diff --git a/src/plugins/legacy_export/server/plugin.ts b/src/plugins/legacy_export/server/plugin.ts index 22c7c1a05dddbb..9e1e6319032404 100644 --- a/src/plugins/legacy_export/server/plugin.ts +++ b/src/plugins/legacy_export/server/plugin.ts @@ -18,18 +18,23 @@ */ import { Plugin, CoreSetup, PluginInitializerContext } from 'kibana/server'; +import { first } from 'rxjs/operators'; import { registerRoutes } from './routes'; export class LegacyExportPlugin implements Plugin<{}, {}> { - private readonly kibanaVersion: string; + constructor(private readonly initContext: PluginInitializerContext) {} - constructor(context: PluginInitializerContext) { - this.kibanaVersion = context.env.packageInfo.version; - } + public async setup({ http }: CoreSetup) { + const globalConfig = await this.initContext.config.legacy.globalConfig$ + .pipe(first()) + .toPromise(); - public setup({ http }: CoreSetup) { const router = http.createRouter(); - registerRoutes(router, this.kibanaVersion); + registerRoutes( + router, + this.initContext.env.packageInfo.version, + globalConfig.savedObjects.maxImportPayloadBytes.getValueInBytes() + ); return {}; } diff --git a/src/plugins/legacy_export/server/routes/import.ts b/src/plugins/legacy_export/server/routes/import.ts index cf6f28683be176..8d33983ad7e3cf 100644 --- a/src/plugins/legacy_export/server/routes/import.ts +++ b/src/plugins/legacy_export/server/routes/import.ts @@ -21,7 +21,7 @@ import { schema } from '@kbn/config-schema'; import { IRouter, SavedObject } from 'src/core/server'; import { importDashboards } from '../lib'; -export const registerImportRoute = (router: IRouter) => { +export const registerImportRoute = (router: IRouter, maxImportPayloadBytes: number) => { router.post( { path: '/api/kibana/dashboards/import', @@ -39,6 +39,9 @@ export const registerImportRoute = (router: IRouter) => { }, options: { tags: ['api'], + body: { + maxBytes: maxImportPayloadBytes, + }, }, }, async (ctx, req, res) => { diff --git a/src/plugins/legacy_export/server/routes/index.ts b/src/plugins/legacy_export/server/routes/index.ts index 7b9de7f016b6ba..cac405ce9bdf97 100644 --- a/src/plugins/legacy_export/server/routes/index.ts +++ b/src/plugins/legacy_export/server/routes/index.ts @@ -21,7 +21,11 @@ import { IRouter } from 'src/core/server'; import { registerImportRoute } from './import'; import { registerExportRoute } from './export'; -export const registerRoutes = (router: IRouter, kibanaVersion: string) => { +export const registerRoutes = ( + router: IRouter, + kibanaVersion: string, + maxImportPayloadBytes: number +) => { registerExportRoute(router, kibanaVersion); - registerImportRoute(router); + registerImportRoute(router, maxImportPayloadBytes); }; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap index 0a330d074fd42e..3a03c5c01b3c2d 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap @@ -222,9 +222,11 @@ exports[`Flyout conflicts should allow conflict resolution 2`] = ` "indexPatterns": Array [ Object { "id": "1", + "title": undefined, }, Object { "id": "2", + "title": undefined, }, ], "isLegacyFile": false, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx index cc9d2ed160241f..72ed69cdca8168 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx @@ -64,7 +64,10 @@ describe('Flyout', () => { done: jest.fn(), newIndexPatternUrl: '', indexPatterns: { - getFields: jest.fn().mockImplementation(() => [{ id: '1' }, { id: '2' }]), + getCache: jest.fn().mockImplementation(() => [ + { id: '1', attributes: {} }, + { id: '2', attributes: {} }, + ]), } as any, overlays, http, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx index 47d82077294cc4..3165ea4ca0794d 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx @@ -132,7 +132,10 @@ export class Flyout extends Component { } fetchIndexPatterns = async () => { - const indexPatterns = await this.props.indexPatterns.getFields(['id', 'title']); + const indexPatterns = (await this.props.indexPatterns.getCache())?.map((savedObject) => ({ + id: savedObject.id, + title: savedObject.attributes.title, + })); this.setState({ indexPatterns } as any); }; diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index c55b21b2f90291..af29a1d5374990 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -280,12 +280,14 @@ The following table describes the properties of the `options` object. | params | The `params` value to give the action type executor. | object | | spaceId | The space id the action is within. | string | | apiKey | The Elasticsearch API key to use for context. (Note: only required and used when security is enabled). | string | +| source | The source of the execution, either an HTTP request or a reference to a Saved Object. | object, optional | ## Example This example makes action `3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5` send an email. The action plugin will load the saved object and find what action type to call with `params`. ```typescript +const request: KibanaRequest = { ... }; const actionsClient = await server.plugins.actions.getActionsClientWithRequest(request); await actionsClient.enqueueExecution({ id: '3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5', @@ -296,6 +298,7 @@ await actionsClient.enqueueExecution({ subject: 'My email subject', body: 'My email body', }, + source: asHttpRequestExecutionSource(request), }); ``` @@ -305,10 +308,11 @@ This api runs the action and asynchronously returns the result of running the ac The following table describes the properties of the `options` object. -| Property | Description | Type | -| -------- | ---------------------------------------------------- | ------ | -| id | The id of the action you want to execute. | string | -| params | The `params` value to give the action type executor. | object | +| Property | Description | Type | +| -------- | ------------------------------------------------------------------------------------ | ------ | +| id | The id of the action you want to execute. | string | +| params | The `params` value to give the action type executor. | object | +| source | The source of the execution, either an HTTP request or a reference to a Saved Object.| object, optional | ## Example @@ -324,6 +328,10 @@ const result = await actionsClient.execute({ subject: 'My email subject', body: 'My email body', }, + source: asSavedObjectExecutionSource({ + id: '573891ae-8c48-49cb-a197-0cd5ec34a88b', + type: 'alert' + }), }); ``` diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index d46ad3e2e24238..06c9555f3a18d0 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -30,6 +30,7 @@ import { } from './create_execute_function'; import { ActionsAuthorization } from './authorization/actions_authorization'; import { ActionType } from '../common'; +import { shouldLegacyRbacApplyBySource } from './authorization/should_legacy_rbac_apply_by_source'; // We are assuming there won't be many actions. This is why we will load // all the actions in advance and assume the total count to not go over 10000. @@ -298,13 +299,19 @@ export class ActionsClient { public async execute({ actionId, params, + source, }: Omit): Promise> { - await this.authorization.ensureAuthorized('execute'); - return this.actionExecutor.execute({ actionId, params, request: this.request }); + if (!(await shouldLegacyRbacApplyBySource(this.unsecuredSavedObjectsClient, source))) { + await this.authorization.ensureAuthorized('execute'); + } + return this.actionExecutor.execute({ actionId, params, source, request: this.request }); } public async enqueueExecution(options: EnqueueExecutionOptions): Promise { - await this.authorization.ensureAuthorized('execute'); + const { source } = options; + if (!(await shouldLegacyRbacApplyBySource(this.unsecuredSavedObjectsClient, source))) { + await this.authorization.ensureAuthorized('execute'); + } return this.executionEnqueuer(this.unsecuredSavedObjectsClient, options); } diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts index 14573161b8d5d3..08c4472f8007ba 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts @@ -9,6 +9,7 @@ import { ActionsAuthorization } from './actions_authorization'; import { actionsAuthorizationAuditLoggerMock } from './audit_logger.mock'; import { ActionsAuthorizationAuditLogger, AuthorizationResult } from './audit_logger'; import { ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from '../saved_objects'; +import { AuthenticatedUser } from '../../../security/server'; const request = {} as KibanaRequest; @@ -19,12 +20,13 @@ const mockAuthorizationAction = (type: string, operation: string) => `${type}/${ function mockSecurity() { const security = securityMock.createSetup(); const authorization = security.authz; + const authentication = security.authc; // typescript is having trouble inferring jest's automocking (authorization.actions.savedObject.get as jest.MockedFunction< typeof authorization.actions.savedObject.get >).mockImplementation(mockAuthorizationAction); authorization.mode.useRbacForRequest.mockReturnValue(true); - return { authorization }; + return { authorization, authentication }; } beforeEach(() => { @@ -192,4 +194,38 @@ describe('ensureAuthorized', () => { ] `); }); + + test('exempts users from requiring privileges to execute actions when shouldUseLegacyRbac is true', async () => { + const { authorization, authentication } = mockSecurity(); + const checkPrivileges: jest.MockedFunction> = jest.fn(); + authorization.checkPrivilegesDynamicallyWithRequest.mockReturnValue(checkPrivileges); + const actionsAuthorization = new ActionsAuthorization({ + request, + authorization, + authentication, + auditLogger, + shouldUseLegacyRbac: true, + }); + + authentication.getCurrentUser.mockReturnValueOnce(({ + username: 'some-user', + } as unknown) as AuthenticatedUser); + + await actionsAuthorization.ensureAuthorized('execute', 'myType'); + + expect(authorization.actions.savedObject.get).not.toHaveBeenCalled(); + expect(checkPrivileges).not.toHaveBeenCalled(); + + expect(auditLogger.actionsAuthorizationSuccess).toHaveBeenCalledTimes(1); + expect(auditLogger.actionsAuthorizationFailure).not.toHaveBeenCalled(); + expect(auditLogger.actionsAuthorizationSuccess.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "some-user", + "execute", + "myType", + ] + `); + }); }); diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.ts index 3ba798ddf1715f..bd6e355c2cf9d1 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.ts @@ -14,6 +14,15 @@ export interface ConstructorOptions { request: KibanaRequest; auditLogger: ActionsAuthorizationAuditLogger; authorization?: SecurityPluginSetup['authz']; + authentication?: SecurityPluginSetup['authc']; + // In order to support legacy Alerts which predate the introduction of the + // Actions feature in Kibana we need a way of "dialing down" the level of + // authorization for certain opearations. + // Specifically, we want to allow these old alerts and their scheduled + // actions to continue to execute - which requires that we exempt auth on + // `get` for Connectors and `execute` for Action execution when used by + // these legacy alerts + shouldUseLegacyRbac?: boolean; } const operationAlias: Record< @@ -27,33 +36,57 @@ const operationAlias: Record< list: (authorization) => authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, 'find'), }; +const LEGACY_RBAC_EXEMPT_OPERATIONS = new Set(['get', 'execute']); + export class ActionsAuthorization { private readonly request: KibanaRequest; private readonly authorization?: SecurityPluginSetup['authz']; + private readonly authentication?: SecurityPluginSetup['authc']; private readonly auditLogger: ActionsAuthorizationAuditLogger; + private readonly shouldUseLegacyRbac: boolean; - constructor({ request, authorization, auditLogger }: ConstructorOptions) { + constructor({ + request, + authorization, + authentication, + auditLogger, + shouldUseLegacyRbac = false, + }: ConstructorOptions) { this.request = request; this.authorization = authorization; + this.authentication = authentication; this.auditLogger = auditLogger; + this.shouldUseLegacyRbac = shouldUseLegacyRbac; } public async ensureAuthorized(operation: string, actionTypeId?: string) { const { authorization } = this; if (authorization?.mode?.useRbacForRequest(this.request)) { - const checkPrivileges = authorization.checkPrivilegesDynamicallyWithRequest(this.request); - const { hasAllRequested, username } = await checkPrivileges({ - kibana: operationAlias[operation] - ? operationAlias[operation](authorization) - : authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation), - }); - if (hasAllRequested) { - this.auditLogger.actionsAuthorizationSuccess(username, operation, actionTypeId); - } else { - throw Boom.forbidden( - this.auditLogger.actionsAuthorizationFailure(username, operation, actionTypeId) + if (this.isOperationExemptDueToLegacyRbac(operation)) { + this.auditLogger.actionsAuthorizationSuccess( + this.authentication?.getCurrentUser(this.request)?.username ?? '', + operation, + actionTypeId ); + } else { + const checkPrivileges = authorization.checkPrivilegesDynamicallyWithRequest(this.request); + const { hasAllRequested, username } = await checkPrivileges({ + kibana: operationAlias[operation] + ? operationAlias[operation](authorization) + : authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation), + }); + if (hasAllRequested) { + this.auditLogger.actionsAuthorizationSuccess(username, operation, actionTypeId); + } else { + throw Boom.forbidden( + this.auditLogger.actionsAuthorizationFailure(username, operation, actionTypeId) + ); + } } } } + + private isOperationExemptDueToLegacyRbac(operation: string) { + return this.shouldUseLegacyRbac && LEGACY_RBAC_EXEMPT_OPERATIONS.has(operation); + } } diff --git a/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.test.ts b/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.test.ts new file mode 100644 index 00000000000000..03062994adeb6a --- /dev/null +++ b/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.test.ts @@ -0,0 +1,114 @@ +/* + * 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 { shouldLegacyRbacApplyBySource } from './should_legacy_rbac_apply_by_source'; +import { savedObjectsClientMock } from '../../../../../src/core/server/mocks'; +import uuid from 'uuid'; +import { asSavedObjectExecutionSource } from '../lib'; + +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +describe(`#shouldLegacyRbacApplyBySource`, () => { + test('should return false if no source is provided', async () => { + expect(await shouldLegacyRbacApplyBySource(unsecuredSavedObjectsClient)).toEqual(false); + }); + + test('should return false if source is not an alert', async () => { + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'action', + id: uuid.v4(), + }) + ) + ).toEqual(false); + }); + + test('should return false if source alert is not marked as legacy', async () => { + const id = uuid.v4(); + unsecuredSavedObjectsClient.get.mockResolvedValue(mockAlert({ id })); + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'alert', + id, + }) + ) + ).toEqual(false); + }); + + test('should return true if source alert is marked as legacy', async () => { + const id = uuid.v4(); + unsecuredSavedObjectsClient.get.mockResolvedValue( + mockAlert({ id, attributes: { meta: { versionApiKeyLastmodified: 'pre-7.10.0' } } }) + ); + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'alert', + id, + }) + ) + ).toEqual(true); + }); + + test('should return false if source alert is marked as modern', async () => { + const id = uuid.v4(); + unsecuredSavedObjectsClient.get.mockResolvedValue( + mockAlert({ id, attributes: { meta: { versionApiKeyLastmodified: '7.10.0' } } }) + ); + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'alert', + id, + }) + ) + ).toEqual(false); + }); + + test('should return false if source alert is marked with a last modified version', async () => { + const id = uuid.v4(); + unsecuredSavedObjectsClient.get.mockResolvedValue(mockAlert({ id, attributes: { meta: {} } })); + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'alert', + id, + }) + ) + ).toEqual(false); + }); +}); + +const mockAlert = (overrides: Record = {}) => ({ + id: '1', + type: 'alert', + attributes: { + consumer: 'myApp', + schedule: { interval: '10s' }, + alertTypeId: 'myType', + enabled: false, + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + version: '123', + references: [], + ...overrides, +}); diff --git a/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.ts b/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.ts new file mode 100644 index 00000000000000..06d5776003ede4 --- /dev/null +++ b/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.ts @@ -0,0 +1,27 @@ +/* + * 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 { SavedObjectsClientContract } from 'src/core/server'; +import { ActionExecutionSource, isSavedObjectExecutionSource } from '../lib'; +import { ALERT_SAVED_OBJECT_TYPE } from '../saved_objects'; + +const LEGACY_VERSION = 'pre-7.10.0'; + +export async function shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient: SavedObjectsClientContract, + executionSource?: ActionExecutionSource +): Promise { + return isSavedObjectExecutionSource(executionSource) && + executionSource?.source?.type === ALERT_SAVED_OBJECT_TYPE + ? ( + await unsecuredSavedObjectsClient.get<{ + meta?: { + versionApiKeyLastmodified?: string; + }; + }>(ALERT_SAVED_OBJECT_TYPE, executionSource.source.id) + ).attributes.meta?.versionApiKeyLastmodified === LEGACY_VERSION + : false; +} diff --git a/x-pack/plugins/actions/server/create_execute_function.test.ts b/x-pack/plugins/actions/server/create_execute_function.test.ts index 04d4d92945cdb3..7682f01ed769df 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -4,13 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ +import { KibanaRequest } from 'src/core/server'; +import uuid from 'uuid'; import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; import { createExecutionEnqueuerFunction } from './create_execute_function'; import { savedObjectsClientMock } from '../../../../src/core/server/mocks'; import { actionTypeRegistryMock } from './action_type_registry.mock'; +import { + asHttpRequestExecutionSource, + asSavedObjectExecutionSource, +} from './lib/action_execution_source'; const mockTaskManager = taskManagerMock.start(); const savedObjectsClient = savedObjectsClientMock.create(); +const request = {} as KibanaRequest; beforeEach(() => jest.resetAllMocks()); @@ -41,6 +48,7 @@ describe('execute()', () => { params: { baz: false }, spaceId: 'default', apiKey: Buffer.from('123:abc').toString('base64'), + source: asHttpRequestExecutionSource(request), }); expect(mockTaskManager.schedule).toHaveBeenCalledTimes(1); expect(mockTaskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` @@ -59,11 +67,15 @@ describe('execute()', () => { ] `); expect(savedObjectsClient.get).toHaveBeenCalledWith('action', '123'); - expect(savedObjectsClient.create).toHaveBeenCalledWith('action_task_params', { - actionId: '123', - params: { baz: false }, - apiKey: Buffer.from('123:abc').toString('base64'), - }); + expect(savedObjectsClient.create).toHaveBeenCalledWith( + 'action_task_params', + { + actionId: '123', + params: { baz: false }, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + {} + ); }); test('schedules the action with all given parameters with a preconfigured action', async () => { @@ -82,6 +94,8 @@ describe('execute()', () => { }, ], }); + const source = { type: 'alert', id: uuid.v4() }; + savedObjectsClient.get.mockResolvedValueOnce({ id: '123', type: 'action', @@ -101,6 +115,7 @@ describe('execute()', () => { params: { baz: false }, spaceId: 'default', apiKey: Buffer.from('123:abc').toString('base64'), + source: asSavedObjectExecutionSource(source), }); expect(mockTaskManager.schedule).toHaveBeenCalledTimes(1); expect(mockTaskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` @@ -119,11 +134,23 @@ describe('execute()', () => { ] `); expect(savedObjectsClient.get).not.toHaveBeenCalled(); - expect(savedObjectsClient.create).toHaveBeenCalledWith('action_task_params', { - actionId: '123', - params: { baz: false }, - apiKey: Buffer.from('123:abc').toString('base64'), - }); + expect(savedObjectsClient.create).toHaveBeenCalledWith( + 'action_task_params', + { + actionId: '123', + params: { baz: false }, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + { + references: [ + { + id: source.id, + name: 'source', + type: source.type, + }, + ], + } + ); }); test('throws when passing isESOUsingEphemeralEncryptionKey with true as a value', async () => { diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts index 85052eef93e051..b226583fade525 100644 --- a/x-pack/plugins/actions/server/create_execute_function.ts +++ b/x-pack/plugins/actions/server/create_execute_function.ts @@ -8,6 +8,8 @@ import { SavedObjectsClientContract } from '../../../../src/core/server'; import { TaskManagerStartContract } from '../../task_manager/server'; import { RawAction, ActionTypeRegistryContract, PreConfiguredAction } from './types'; import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from './saved_objects'; +import { ExecuteOptions as ActionExecutorOptions } from './lib/action_executor'; +import { isSavedObjectExecutionSource } from './lib'; interface CreateExecuteFunctionOptions { taskManager: TaskManagerStartContract; @@ -16,15 +18,14 @@ interface CreateExecuteFunctionOptions { preconfiguredActions: PreConfiguredAction[]; } -export interface ExecuteOptions { +export interface ExecuteOptions extends Pick { id: string; - params: Record; spaceId: string; apiKey: string | null; } export type ExecutionEnqueuer = ( - savedObjectsClient: SavedObjectsClientContract, + unsecuredSavedObjectsClient: SavedObjectsClientContract, options: ExecuteOptions ) => Promise; @@ -35,8 +36,8 @@ export function createExecutionEnqueuerFunction({ preconfiguredActions, }: CreateExecuteFunctionOptions) { return async function execute( - savedObjectsClient: SavedObjectsClientContract, - { id, params, spaceId, apiKey }: ExecuteOptions + unsecuredSavedObjectsClient: SavedObjectsClientContract, + { id, params, spaceId, source, apiKey }: ExecuteOptions ) { if (isESOUsingEphemeralEncryptionKey === true) { throw new Error( @@ -44,19 +45,24 @@ export function createExecutionEnqueuerFunction({ ); } - const actionTypeId = await getActionTypeId(id); + const actionTypeId = await getActionTypeId( + unsecuredSavedObjectsClient, + preconfiguredActions, + id + ); if (!actionTypeRegistry.isActionExecutable(id, actionTypeId)) { actionTypeRegistry.ensureActionTypeEnabled(actionTypeId); } - const actionTaskParamsRecord = await savedObjectsClient.create( + const actionTaskParamsRecord = await unsecuredSavedObjectsClient.create( ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, { actionId: id, params, apiKey, - } + }, + executionSourceAsSavedObjectReferences(source) ); await taskManager.schedule({ @@ -68,15 +74,34 @@ export function createExecutionEnqueuerFunction({ state: {}, scope: ['actions'], }); + }; +} - async function getActionTypeId(actionId: string): Promise { - const pcAction = preconfiguredActions.find((action) => action.id === actionId); - if (pcAction) { - return pcAction.actionTypeId; +function executionSourceAsSavedObjectReferences(executionSource: ActionExecutorOptions['source']) { + return isSavedObjectExecutionSource(executionSource) + ? { + references: [ + { + name: 'source', + ...executionSource.source, + }, + ], } + : {}; +} - const actionSO = await savedObjectsClient.get('action', actionId); - return actionSO.attributes.actionTypeId; - } - }; +async function getActionTypeId( + unsecuredSavedObjectsClient: SavedObjectsClientContract, + preconfiguredActions: PreConfiguredAction[], + actionId: string +): Promise { + const pcAction = preconfiguredActions.find((action) => action.id === actionId); + if (pcAction) { + return pcAction.actionTypeId; + } + + const { + attributes: { actionTypeId }, + } = await unsecuredSavedObjectsClient.get('action', actionId); + return actionTypeId; } diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 31c4d26d1793e7..bbe298c0585a3a 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -23,6 +23,8 @@ export { } from './types'; export { PluginSetupContract, PluginStartContract } from './plugin'; +export { asSavedObjectExecutionSource, asHttpRequestExecutionSource } from './lib'; + export const plugin = (initContext: PluginInitializerContext) => new ActionsPlugin(initContext); export const config: PluginConfigDescriptor = { diff --git a/x-pack/plugins/actions/server/lib/action_execution_source.ts b/x-pack/plugins/actions/server/lib/action_execution_source.ts new file mode 100644 index 00000000000000..53f815f070beae --- /dev/null +++ b/x-pack/plugins/actions/server/lib/action_execution_source.ts @@ -0,0 +1,47 @@ +/* + * 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 { KibanaRequest, SavedObjectReference } from 'src/core/server'; + +export enum ActionExecutionSourceType { + SAVED_OBJECT = 'SAVED_OBJECT', + HTTP_REQUEST = 'HTTP_REQUEST', +} + +export interface ActionExecutionSource { + type: ActionExecutionSourceType; + source: T; +} +export type HttpRequestExecutionSource = ActionExecutionSource; +export type SavedObjectExecutionSource = ActionExecutionSource>; + +export function asHttpRequestExecutionSource(source: KibanaRequest): HttpRequestExecutionSource { + return { + type: ActionExecutionSourceType.HTTP_REQUEST, + source, + }; +} + +export function asSavedObjectExecutionSource( + source: Omit +): SavedObjectExecutionSource { + return { + type: ActionExecutionSourceType.SAVED_OBJECT, + source, + }; +} + +export function isHttpRequestExecutionSource( + executionSource?: ActionExecutionSource +): executionSource is HttpRequestExecutionSource { + return executionSource?.type === ActionExecutionSourceType.HTTP_REQUEST; +} + +export function isSavedObjectExecutionSource( + executionSource?: ActionExecutionSource +): executionSource is SavedObjectExecutionSource { + return executionSource?.type === ActionExecutionSourceType.SAVED_OBJECT; +} diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 97c08124f5546d..a607dc0de0bda4 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Logger, KibanaRequest } from '../../../../../src/core/server'; +import { Logger, KibanaRequest } from 'src/core/server'; import { validateParams, validateConfig, validateSecrets } from './validate_with_schema'; import { ActionTypeExecutorResult, @@ -16,15 +16,19 @@ import { } from '../types'; import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; import { SpacesServiceSetup } from '../../../spaces/server'; -import { EVENT_LOG_ACTIONS, PluginStartContract } from '../plugin'; +import { EVENT_LOG_ACTIONS } from '../plugin'; import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { ActionsClient } from '../actions_client'; +import { ActionExecutionSource } from './action_execution_source'; export interface ActionExecutorContext { logger: Logger; spaces?: SpacesServiceSetup; getServices: GetServicesFunction; - getActionsClientWithRequest: PluginStartContract['getActionsClientWithRequest']; + getActionsClientWithRequest: ( + request: KibanaRequest, + executionSource?: ActionExecutionSource + ) => Promise>; encryptedSavedObjectsClient: EncryptedSavedObjectsClient; actionTypeRegistry: ActionTypeRegistryContract; eventLogger: IEventLogger; @@ -32,10 +36,11 @@ export interface ActionExecutorContext { proxySettings?: ProxySettings; } -export interface ExecuteOptions { +export interface ExecuteOptions { actionId: string; request: KibanaRequest; params: Record; + source?: ActionExecutionSource; } export type ActionExecutorContract = PublicMethodsOf; @@ -61,6 +66,7 @@ export class ActionExecutor { actionId, params, request, + source, }: ExecuteOptions): Promise> { if (!this.isInitialized) { throw new Error('ActionExecutor not initialized'); @@ -88,7 +94,7 @@ export class ActionExecutor { const namespace = spaceId && spaceId !== 'default' ? { namespace: spaceId } : {}; const { actionTypeId, name, config, secrets } = await getActionInfo( - await getActionsClientWithRequest(request), + await getActionsClientWithRequest(request, source), encryptedSavedObjectsClient, preconfiguredActions, actionId, diff --git a/x-pack/plugins/actions/server/lib/index.ts b/x-pack/plugins/actions/server/lib/index.ts index f03b6de1fc5fbc..e97875b91cf333 100644 --- a/x-pack/plugins/actions/server/lib/index.ts +++ b/x-pack/plugins/actions/server/lib/index.ts @@ -15,3 +15,10 @@ export { ActionTypeDisabledReason, isErrorThatHandlesItsOwnResponse, } from './errors'; +export { + ActionExecutionSource, + asSavedObjectExecutionSource, + isSavedObjectExecutionSource, + asHttpRequestExecutionSource, + isHttpRequestExecutionSource, +} from './action_execution_source'; diff --git a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts index 78522682054e16..18cbd9f9c5fada 100644 --- a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts +++ b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts @@ -71,13 +71,13 @@ const taskRunnerFactoryInitializerParams = { logger: loggingSystemMock.create().get(), encryptedSavedObjectsClient: mockedEncryptedSavedObjectsClient, getBasePath: jest.fn().mockReturnValue(undefined), - getScopedSavedObjectsClient: jest.fn().mockReturnValue(services.savedObjectsClient), + getUnsecuredSavedObjectsClient: jest.fn().mockReturnValue(services.savedObjectsClient), }; beforeEach(() => { jest.resetAllMocks(); actionExecutorInitializerParams.getServices.mockReturnValue(services); - taskRunnerFactoryInitializerParams.getScopedSavedObjectsClient.mockReturnValue( + taskRunnerFactoryInitializerParams.getUnsecuredSavedObjectsClient.mockReturnValue( services.savedObjectsClient ); }); diff --git a/x-pack/plugins/actions/server/lib/task_runner_factory.ts b/x-pack/plugins/actions/server/lib/task_runner_factory.ts index 10a8501e856d26..aeeeb4ed7d520d 100644 --- a/x-pack/plugins/actions/server/lib/task_runner_factory.ts +++ b/x-pack/plugins/actions/server/lib/task_runner_factory.ts @@ -4,9 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +import { pick } from 'lodash'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { map, fromNullable, getOrElse } from 'fp-ts/lib/Option'; +import { + Logger, + SavedObjectsClientContract, + KibanaRequest, + SavedObjectReference, +} from 'src/core/server'; import { ActionExecutorContract } from './action_executor'; import { ExecutorError } from './executor_error'; -import { Logger, CoreStart, KibanaRequest } from '../../../../../src/core/server'; import { RunContext } from '../../../task_manager/server'; import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; import { ActionTypeDisabledError } from './errors'; @@ -18,6 +26,7 @@ import { ActionTypeExecutorResult, } from '../types'; import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from '../saved_objects'; +import { asSavedObjectExecutionSource } from './action_execution_source'; export interface TaskRunnerContext { logger: Logger; @@ -25,7 +34,7 @@ export interface TaskRunnerContext { encryptedSavedObjectsClient: EncryptedSavedObjectsClient; spaceIdToNamespace: SpaceIdToNamespaceFunction; getBasePath: GetBasePathFunction; - getScopedSavedObjectsClient: CoreStart['savedObjects']['getScopedClient']; + getUnsecuredSavedObjectsClient: (request: KibanaRequest) => SavedObjectsClientContract; } export class TaskRunnerFactory { @@ -56,7 +65,7 @@ export class TaskRunnerFactory { encryptedSavedObjectsClient, spaceIdToNamespace, getBasePath, - getScopedSavedObjectsClient, + getUnsecuredSavedObjectsClient, } = this.taskRunnerContext!; return { @@ -66,6 +75,7 @@ export class TaskRunnerFactory { const { attributes: { actionId, params, apiKey }, + references, } = await encryptedSavedObjectsClient.getDecryptedAsInternalUser( ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, actionTaskParamsId, @@ -100,6 +110,7 @@ export class TaskRunnerFactory { params, actionId, request: fakeRequest, + ...getSourceFromReferences(references), }); } catch (e) { if (e instanceof ActionTypeDisabledError) { @@ -121,8 +132,14 @@ export class TaskRunnerFactory { // Cleanup action_task_params object now that we're done with it try { - const savedObjectsClient = getScopedSavedObjectsClient(fakeRequest); - await savedObjectsClient.delete(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, actionTaskParamsId); + // If the request has reached this far we can assume the user is allowed to run clean up + // We would idealy secure every operation but in order to support clean up of legacy alerts + // we allow this operation in an unsecured manner + // Once support for legacy alert RBAC is dropped, this can be secured + await getUnsecuredSavedObjectsClient(fakeRequest).delete( + ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, + actionTaskParamsId + ); } catch (e) { // Log error only, we shouldn't fail the task because of an error here (if ever there's retry logic) logger.error( @@ -133,3 +150,13 @@ export class TaskRunnerFactory { }; } } + +function getSourceFromReferences(references: SavedObjectReference[]) { + return pipe( + fromNullable(references.find((ref) => ref.name === 'source')), + map((source) => ({ + source: asSavedObjectExecutionSource(pick(source, 'id', 'type')), + })), + getOrElse(() => ({})) + ); +} diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 592ca93ef5a163..97cefafad4385c 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -65,10 +65,13 @@ import { setupSavedObjects, ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, + ALERT_SAVED_OBJECT_TYPE, } from './saved_objects'; import { ACTIONS_FEATURE } from './feature'; import { ActionsAuthorization } from './authorization/actions_authorization'; import { ActionsAuthorizationAuditLogger } from './authorization/audit_logger'; +import { ActionExecutionSource } from './lib/action_execution_source'; +import { shouldLegacyRbacApplyBySource } from './authorization/should_legacy_rbac_apply_by_source'; const EVENT_LOG_PROVIDER = 'actions'; export const EVENT_LOG_ACTIONS = { @@ -109,7 +112,11 @@ export interface ActionsPluginsStart { taskManager: TaskManagerStartContract; } -const includedHiddenTypes = [ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE]; +const includedHiddenTypes = [ + ACTION_SAVED_OBJECT_TYPE, + ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, + ALERT_SAVED_OBJECT_TYPE, +]; export class ActionsPlugin implements Plugin, PluginStartContract> { private readonly kibanaIndex: Promise; @@ -265,29 +272,39 @@ export class ActionsPlugin implements Plugin, Plugi isESOUsingEphemeralEncryptionKey, preconfiguredActions, instantiateAuthorization, + getUnsecuredSavedObjectsClient, } = this; const encryptedSavedObjectsClient = plugins.encryptedSavedObjects.getClient({ includedHiddenTypes, }); - const getActionsClientWithRequest = async (request: KibanaRequest) => { + const getActionsClientWithRequest = async ( + request: KibanaRequest, + source?: ActionExecutionSource + ) => { if (isESOUsingEphemeralEncryptionKey === true) { throw new Error( `Unable to create actions client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml` ); } + + const unsecuredSavedObjectsClient = getUnsecuredSavedObjectsClient( + core.savedObjects, + request + ); + return new ActionsClient({ - unsecuredSavedObjectsClient: core.savedObjects.getScopedClient(request, { - excludedWrappers: ['security'], - includedHiddenTypes, - }), + unsecuredSavedObjectsClient, actionTypeRegistry: actionTypeRegistry!, defaultKibanaIndex: await kibanaIndex, scopedClusterClient: core.elasticsearch.legacy.client.asScoped(request), preconfiguredActions, request, - authorization: instantiateAuthorization(request), + authorization: instantiateAuthorization( + request, + await shouldLegacyRbacApplyBySource(unsecuredSavedObjectsClient, source) + ), actionExecutor: actionExecutor!, executionEnqueuer: createExecutionEnqueuerFunction({ taskManager: plugins.taskManager, @@ -298,8 +315,13 @@ export class ActionsPlugin implements Plugin, Plugi }); }; + // Ensure the public API cannot be used to circumvent authorization + // using our legacy exemption mechanism + const secureGetActionsClientWithRequest = (request: KibanaRequest) => + getActionsClientWithRequest(request); + this.eventLogService!.registerSavedObjectProvider('action', (request) => { - const client = getActionsClientWithRequest(request); + const client = secureGetActionsClientWithRequest(request); return async (type: string, id: string) => (await client).get({ id }); }); @@ -335,10 +357,8 @@ export class ActionsPlugin implements Plugin, Plugi encryptedSavedObjectsClient, getBasePath: this.getBasePath, spaceIdToNamespace: this.spaceIdToNamespace, - getScopedSavedObjectsClient: (request: KibanaRequest) => - core.savedObjects.getScopedClient(request, { - includedHiddenTypes, - }), + getUnsecuredSavedObjectsClient: (request: KibanaRequest) => + this.getUnsecuredSavedObjectsClient(core.savedObjects, request), }); scheduleActionsTelemetry(this.telemetryLogger, plugins.taskManager); @@ -353,15 +373,29 @@ export class ActionsPlugin implements Plugin, Plugi getActionsAuthorizationWithRequest(request: KibanaRequest) { return instantiateAuthorization(request); }, - getActionsClientWithRequest, + getActionsClientWithRequest: secureGetActionsClientWithRequest, preconfiguredActions, }; } - private instantiateAuthorization = (request: KibanaRequest) => { + private getUnsecuredSavedObjectsClient = ( + savedObjects: CoreStart['savedObjects'], + request: KibanaRequest + ) => + savedObjects.getScopedClient(request, { + excludedWrappers: ['security'], + includedHiddenTypes, + }); + + private instantiateAuthorization = ( + request: KibanaRequest, + shouldUseLegacyRbac: boolean = false + ) => { return new ActionsAuthorization({ request, + shouldUseLegacyRbac, authorization: this.security?.authz, + authentication: this.security?.authc, auditLogger: new ActionsAuthorizationAuditLogger( this.security?.audit.getLogger(ACTIONS_FEATURE.id) ), diff --git a/x-pack/plugins/actions/server/routes/execute.test.ts b/x-pack/plugins/actions/server/routes/execute.test.ts index b668e3460828a8..e0c83711818ad6 100644 --- a/x-pack/plugins/actions/server/routes/execute.test.ts +++ b/x-pack/plugins/actions/server/routes/execute.test.ts @@ -8,7 +8,7 @@ import { executeActionRoute } from './execute'; import { httpServiceMock } from 'src/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './_mock_handler_arguments'; -import { verifyApiAccess, ActionTypeDisabledError } from '../lib'; +import { verifyApiAccess, ActionTypeDisabledError, asHttpRequestExecutionSource } from '../lib'; import { actionsClientMock } from '../actions_client.mock'; import { ActionTypeExecutorResult } from '../types'; @@ -61,6 +61,7 @@ describe('executeActionRoute', () => { params: { someData: 'data', }, + source: asHttpRequestExecutionSource(req), }); expect(res.ok).toHaveBeenCalled(); @@ -97,6 +98,7 @@ describe('executeActionRoute', () => { expect(actionsClient.execute).toHaveBeenCalledWith({ actionId: '1', params: {}, + source: asHttpRequestExecutionSource(req), }); expect(res.ok).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/actions/server/routes/execute.ts b/x-pack/plugins/actions/server/routes/execute.ts index f15a117106210a..8191b6946d3324 100644 --- a/x-pack/plugins/actions/server/routes/execute.ts +++ b/x-pack/plugins/actions/server/routes/execute.ts @@ -15,6 +15,7 @@ import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from import { ActionTypeExecutorResult } from '../types'; import { BASE_ACTION_API_PATH } from '../../common'; +import { asHttpRequestExecutionSource } from '../lib/action_execution_source'; const paramSchema = schema.object({ id: schema.string(), @@ -51,6 +52,7 @@ export const executeActionRoute = (router: IRouter, licenseState: ILicenseState) const body: ActionTypeExecutorResult = await actionsClient.execute({ params, actionId: id, + source: asHttpRequestExecutionSource(req), }); return body ? res.ok({ diff --git a/x-pack/plugins/actions/server/saved_objects/index.ts b/x-pack/plugins/actions/server/saved_objects/index.ts index 54f186acc1ba57..afc51ea4842f52 100644 --- a/x-pack/plugins/actions/server/saved_objects/index.ts +++ b/x-pack/plugins/actions/server/saved_objects/index.ts @@ -9,6 +9,7 @@ import mappings from './mappings.json'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; export const ACTION_SAVED_OBJECT_TYPE = 'action'; +export const ALERT_SAVED_OBJECT_TYPE = 'alert'; export const ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE = 'action_task_params'; export function setupSavedObjects( diff --git a/x-pack/plugins/alerts/server/alerts_client.test.ts b/x-pack/plugins/alerts/server/alerts_client.test.ts index b20e6c6be2ebfa..4b5af942024c0c 100644 --- a/x-pack/plugins/alerts/server/alerts_client.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client.test.ts @@ -33,6 +33,7 @@ const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); const authorization = alertsAuthorizationMock.create(); const actionsAuthorization = actionsAuthorizationMock.create(); +const kibanaVersion = 'v7.10.0'; const alertsClientParams: jest.Mocked = { taskManager, alertTypeRegistry, @@ -48,6 +49,7 @@ const alertsClientParams: jest.Mocked = { encryptedSavedObjectsClient: encryptedSavedObjects, getActionsClient: jest.fn(), getEventLogClient: jest.fn(), + kibanaVersion, }; beforeEach(() => { @@ -374,6 +376,9 @@ describe('create()', () => { "createdAt": "2019-02-12T21:01:22.479Z", "createdBy": "elastic", "enabled": true, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, "muteAll": false, "mutedInstanceIds": Array [], "name": "abc", @@ -426,10 +431,10 @@ describe('create()', () => { expect(unsecuredSavedObjectsClient.update.mock.calls[0][0]).toEqual('alert'); expect(unsecuredSavedObjectsClient.update.mock.calls[0][1]).toEqual('1'); expect(unsecuredSavedObjectsClient.update.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "scheduledTaskId": "task-123", - } - `); + Object { + "scheduledTaskId": "task-123", + } + `); }); test('creates an alert with multiple actions', async () => { @@ -999,6 +1004,9 @@ describe('create()', () => { createdAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', enabled: true, + meta: { + versionApiKeyLastmodified: 'v7.10.0', + }, schedule: { interval: '10s' }, throttle: null, muteAll: false, @@ -1112,6 +1120,9 @@ describe('create()', () => { createdAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', enabled: false, + meta: { + versionApiKeyLastmodified: 'v7.10.0', + }, schedule: { interval: '10s' }, throttle: null, muteAll: false, @@ -1237,6 +1248,9 @@ describe('enable()', () => { alertTypeId: 'myType', consumer: 'myApp', enabled: true, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, updatedBy: 'elastic', apiKey: null, apiKeyOwner: null, @@ -1322,6 +1336,9 @@ describe('enable()', () => { alertTypeId: 'myType', consumer: 'myApp', enabled: true, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, apiKey: Buffer.from('123:abc').toString('base64'), apiKeyOwner: 'elastic', updatedBy: 'elastic', @@ -1487,6 +1504,9 @@ describe('disable()', () => { apiKey: null, apiKeyOwner: null, enabled: false, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, scheduledTaskId: null, updatedBy: 'elastic', actions: [ @@ -1527,6 +1547,9 @@ describe('disable()', () => { apiKey: null, apiKeyOwner: null, enabled: false, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, scheduledTaskId: null, updatedBy: 'elastic', actions: [ @@ -3203,6 +3226,9 @@ describe('update()', () => { "apiKeyOwner": null, "consumer": "myApp", "enabled": true, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, "name": "abc", "params": Object { "bar": true, @@ -3360,6 +3386,9 @@ describe('update()', () => { "apiKeyOwner": "elastic", "consumer": "myApp", "enabled": true, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, "name": "abc", "params": Object { "bar": true, @@ -3511,6 +3540,9 @@ describe('update()', () => { "apiKeyOwner": null, "consumer": "myApp", "enabled": false, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, "name": "abc", "params": Object { "bar": true, @@ -4196,6 +4228,9 @@ describe('updateApiKey()', () => { }, }, ], + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, }, { version: '123' } ); @@ -4232,6 +4267,9 @@ describe('updateApiKey()', () => { }, }, ], + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, }, { version: '123' } ); diff --git a/x-pack/plugins/alerts/server/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client.ts index f8da17f4bf0893..0a08ca848c73d7 100644 --- a/x-pack/plugins/alerts/server/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client.ts @@ -12,6 +12,7 @@ import { SavedObjectsClientContract, SavedObjectReference, SavedObject, + PluginInitializerContext, } from 'src/core/server'; import { esKuery } from '../../../../src/plugins/data/server'; import { ActionsClient, ActionsAuthorization } from '../../actions/server'; @@ -71,6 +72,7 @@ export interface ConstructorOptions { invalidateAPIKey: (params: InvalidateAPIKeyParams) => Promise; getActionsClient: () => Promise; getEventLogClient: () => Promise; + kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; } export interface MuteOptions extends IndexType { @@ -157,7 +159,8 @@ export class AlertsClient { private readonly getActionsClient: () => Promise; private readonly actionsAuthorization: ActionsAuthorization; private readonly getEventLogClient: () => Promise; - encryptedSavedObjectsClient: EncryptedSavedObjectsClient; + private readonly encryptedSavedObjectsClient: EncryptedSavedObjectsClient; + private readonly kibanaVersion!: PluginInitializerContext['env']['packageInfo']['version']; constructor({ alertTypeRegistry, @@ -174,6 +177,7 @@ export class AlertsClient { getActionsClient, actionsAuthorization, getEventLogClient, + kibanaVersion, }: ConstructorOptions) { this.logger = logger; this.getUserName = getUserName; @@ -189,6 +193,7 @@ export class AlertsClient { this.getActionsClient = getActionsClient; this.actionsAuthorization = actionsAuthorization; this.getEventLogClient = getEventLogClient; + this.kibanaVersion = kibanaVersion; } public async create({ data, options }: CreateOptions): Promise { @@ -222,10 +227,14 @@ export class AlertsClient { muteAll: false, mutedInstanceIds: [], }; - const createdAlert = await this.unsecuredSavedObjectsClient.create('alert', rawAlert, { - ...options, - references, - }); + const createdAlert = await this.unsecuredSavedObjectsClient.create( + 'alert', + this.updateMeta(rawAlert), + { + ...options, + references, + } + ); if (data.enabled) { let scheduledTask; try { @@ -373,7 +382,7 @@ export class AlertsClient { } public async delete({ id }: { id: string }) { - let taskIdToRemove: string | undefined; + let taskIdToRemove: string | undefined | null; let apiKeyToInvalidate: string | null = null; let attributes: RawAlert; @@ -482,14 +491,14 @@ export class AlertsClient { const updatedObject = await this.unsecuredSavedObjectsClient.update( 'alert', id, - { + this.updateMeta({ ...attributes, ...data, ...apiKeyAttributes, params: validatedAlertTypeParams as RawAlert['params'], actions, updatedBy: username, - }, + }), { version, references, @@ -547,7 +556,7 @@ export class AlertsClient { WriteOperations.UpdateApiKey ); - if (attributes.actions.length) { + if (attributes.actions.length && !this.authorization.shouldUseLegacyAuthorization(attributes)) { await this.actionsAuthorization.ensureAuthorized('execute'); } @@ -555,14 +564,14 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', id, - { + this.updateMeta({ ...attributes, ...this.apiKeyAsAlertAttributes( await this.createAPIKey(this.generateAPIKeyName(attributes.alertTypeId, attributes.name)), username ), updatedBy: username, - }, + }), { version } ); @@ -625,7 +634,7 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', id, - { + this.updateMeta({ ...attributes, enabled: true, ...this.apiKeyAsAlertAttributes( @@ -635,7 +644,7 @@ export class AlertsClient { username ), updatedBy: username, - }, + }), { version } ); const scheduledTask = await this.scheduleAlert(id, attributes.alertTypeId); @@ -681,14 +690,14 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', id, - { + this.updateMeta({ ...attributes, enabled: false, scheduledTaskId: null, apiKey: null, apiKeyOwner: null, updatedBy: await this.getUserName(), - }, + }), { version } ); @@ -713,11 +722,15 @@ export class AlertsClient { await this.actionsAuthorization.ensureAuthorized('execute'); } - await this.unsecuredSavedObjectsClient.update('alert', id, { - muteAll: true, - mutedInstanceIds: [], - updatedBy: await this.getUserName(), - }); + await this.unsecuredSavedObjectsClient.update( + 'alert', + id, + this.updateMeta({ + muteAll: true, + mutedInstanceIds: [], + updatedBy: await this.getUserName(), + }) + ); } public async unmuteAll({ id }: { id: string }) { @@ -732,11 +745,15 @@ export class AlertsClient { await this.actionsAuthorization.ensureAuthorized('execute'); } - await this.unsecuredSavedObjectsClient.update('alert', id, { - muteAll: false, - mutedInstanceIds: [], - updatedBy: await this.getUserName(), - }); + await this.unsecuredSavedObjectsClient.update( + 'alert', + id, + this.updateMeta({ + muteAll: false, + mutedInstanceIds: [], + updatedBy: await this.getUserName(), + }) + ); } public async muteInstance({ alertId, alertInstanceId }: MuteOptions) { @@ -761,10 +778,10 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', alertId, - { + this.updateMeta({ mutedInstanceIds, updatedBy: await this.getUserName(), - }, + }), { version } ); } @@ -795,11 +812,10 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', alertId, - { + this.updateMeta({ updatedBy: await this.getUserName(), - mutedInstanceIds: mutedInstanceIds.filter((id: string) => id !== alertInstanceId), - }, + }), { version } ); } @@ -859,7 +875,7 @@ export class AlertsClient { private getPartialAlertFromRaw( id: string, - { createdAt, ...rawAlert }: Partial, + { createdAt, meta, scheduledTaskId, ...rawAlert }: Partial, updatedAt: SavedObject['updated_at'] = createdAt, references: SavedObjectReference[] | undefined ): PartialAlert { @@ -874,6 +890,7 @@ export class AlertsClient { : [], ...(updatedAt ? { updatedAt: new Date(updatedAt) } : {}), ...(createdAt ? { createdAt: new Date(createdAt) } : {}), + ...(scheduledTaskId ? { scheduledTaskId } : {}), }; } @@ -941,6 +958,14 @@ export class AlertsClient { private generateAPIKeyName(alertTypeId: string, alertName: string) { return truncate(`Alerting: ${alertTypeId}/${trim(alertName)}`, { length: 256 }); } + + private updateMeta>(alertAttributes: T): T { + if (alertAttributes.hasOwnProperty('apiKey') || alertAttributes.hasOwnProperty('apiKeyOwner')) { + alertAttributes.meta = alertAttributes.meta ?? {}; + alertAttributes.meta.versionApiKeyLastmodified = this.kibanaVersion; + } + return alertAttributes; + } } function parseDate(dateString: string | undefined, propertyName: string, defaultValue: Date): Date { diff --git a/x-pack/plugins/alerts/server/alerts_client_factory.test.ts b/x-pack/plugins/alerts/server/alerts_client_factory.test.ts index 99e81344715a54..ac91d689798c97 100644 --- a/x-pack/plugins/alerts/server/alerts_client_factory.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client_factory.test.ts @@ -44,6 +44,7 @@ const alertsClientFactoryParams: jest.Mocked = { actions: actionsMock.createStart(), features, eventLog: eventLogMock.createStart(), + kibanaVersion: '7.10.0', }; const fakeRequest = ({ app: {}, @@ -126,6 +127,7 @@ test('creates an alerts client with proper constructor arguments when security i createAPIKey: expect.any(Function), invalidateAPIKey: expect.any(Function), encryptedSavedObjectsClient: alertsClientFactoryParams.encryptedSavedObjectsClient, + kibanaVersion: '7.10.0', }); }); @@ -169,6 +171,7 @@ test('creates an alerts client with proper constructor arguments', async () => { encryptedSavedObjectsClient: alertsClientFactoryParams.encryptedSavedObjectsClient, getActionsClient: expect.any(Function), getEventLogClient: expect.any(Function), + kibanaVersion: '7.10.0', }); }); diff --git a/x-pack/plugins/alerts/server/alerts_client_factory.ts b/x-pack/plugins/alerts/server/alerts_client_factory.ts index 83202424c97733..eccd8103913073 100644 --- a/x-pack/plugins/alerts/server/alerts_client_factory.ts +++ b/x-pack/plugins/alerts/server/alerts_client_factory.ts @@ -4,11 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ +import { + KibanaRequest, + Logger, + SavedObjectsServiceStart, + PluginInitializerContext, +} from 'src/core/server'; import { PluginStartContract as ActionsPluginStartContract } from '../../actions/server'; import { AlertsClient } from './alerts_client'; import { ALERTS_FEATURE_ID } from '../common'; import { AlertTypeRegistry, SpaceIdToNamespaceFunction } from './types'; -import { KibanaRequest, Logger, SavedObjectsServiceStart } from '../../../../src/core/server'; import { InvalidateAPIKeyParams, SecurityPluginSetup } from '../../security/server'; import { EncryptedSavedObjectsClient } from '../../encrypted_saved_objects/server'; import { TaskManagerStartContract } from '../../task_manager/server'; @@ -30,6 +35,7 @@ export interface AlertsClientFactoryOpts { actions: ActionsPluginStartContract; features: FeaturesPluginStart; eventLog: IEventLogClientService; + kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; } export class AlertsClientFactory { @@ -45,6 +51,7 @@ export class AlertsClientFactory { private actions!: ActionsPluginStartContract; private features!: FeaturesPluginStart; private eventLog!: IEventLogClientService; + private kibanaVersion!: PluginInitializerContext['env']['packageInfo']['version']; public initialize(options: AlertsClientFactoryOpts) { if (this.isInitialized) { @@ -62,6 +69,7 @@ export class AlertsClientFactory { this.actions = options.actions; this.features = options.features; this.eventLog = options.eventLog; + this.kibanaVersion = options.kibanaVersion; } public create(request: KibanaRequest, savedObjects: SavedObjectsServiceStart): AlertsClient { @@ -80,6 +88,7 @@ export class AlertsClientFactory { return new AlertsClient({ spaceId, + kibanaVersion: this.kibanaVersion, logger: this.logger, taskManager: this.taskManager, alertTypeRegistry: this.alertTypeRegistry, diff --git a/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts b/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts index d7705f834ad41c..3728daa946d5b9 100644 --- a/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts +++ b/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts @@ -14,6 +14,7 @@ const createAlertsAuthorizationMock = () => { ensureAuthorized: jest.fn(), filterByAlertTypeAuthorization: jest.fn(), getFindAuthorizationFilter: jest.fn(), + shouldUseLegacyAuthorization: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts index 20b9fecd601e60..17691baa25af85 100644 --- a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts +++ b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts @@ -8,12 +8,13 @@ import Boom from 'boom'; import { map, mapValues, fromPairs, has } from 'lodash'; import { KibanaRequest } from 'src/core/server'; import { ALERTS_FEATURE_ID } from '../../common'; -import { AlertTypeRegistry } from '../types'; +import { AlertTypeRegistry, RawAlert } from '../types'; import { SecurityPluginSetup } from '../../../security/server'; import { RegistryAlertType } from '../alert_type_registry'; import { PluginStartContract as FeaturesPluginStart } from '../../../features/server'; import { AlertsAuthorizationAuditLogger, ScopeType } from './audit_logger'; import { Space } from '../../../spaces/server'; +import { LEGACY_LAST_MODIFIED_VERSION } from '../saved_objects/migrations'; import { asFiltersByAlertTypeAndConsumer } from './alerts_authorization_kuery'; import { KueryNode } from '../../../../../src/plugins/data/server'; @@ -111,6 +112,10 @@ export class AlertsAuthorization { ); } + public shouldUseLegacyAuthorization(alert: RawAlert): boolean { + return alert.meta?.versionApiKeyLastmodified === LEGACY_LAST_MODIFIED_VERSION; + } + private shouldCheckAuthorization(): boolean { return this.authorization?.mode?.useRbacForRequest(this.request) ?? false; } diff --git a/x-pack/plugins/alerts/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts index 4f9b1f7c22e6dd..8f09d55c9a0e01 100644 --- a/x-pack/plugins/alerts/server/plugin.ts +++ b/x-pack/plugins/alerts/server/plugin.ts @@ -109,6 +109,7 @@ export class AlertingPlugin { private readonly alertsClientFactory: AlertsClientFactory; private readonly telemetryLogger: Logger; private readonly kibanaIndex: Promise; + private readonly kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; private eventLogService?: IEventLogService; private eventLogger?: IEventLogger; @@ -123,6 +124,7 @@ export class AlertingPlugin { map((config: SharedGlobalConfig) => config.kibana.index) ) .toPromise(); + this.kibanaVersion = initializerContext.env.packageInfo.version; } public async setup( @@ -241,6 +243,7 @@ export class AlertingPlugin { actions: plugins.actions, features: plugins.features, eventLog: plugins.eventLog, + kibanaVersion: this.kibanaVersion, }); const getAlertsClientWithRequest = (request: KibanaRequest) => { diff --git a/x-pack/plugins/alerts/server/saved_objects/mappings.json b/x-pack/plugins/alerts/server/saved_objects/mappings.json index a7e85febf2446b..8440b963975ffc 100644 --- a/x-pack/plugins/alerts/server/saved_objects/mappings.json +++ b/x-pack/plugins/alerts/server/saved_objects/mappings.json @@ -76,6 +76,13 @@ }, "mutedInstanceIds": { "type": "keyword" + }, + "meta": { + "properties": { + "versionApiKeyLastmodified": { + "type": "keyword" + } + } } } } diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts index 46fa2bcd512ff3..1c1261ae3fa08c 100644 --- a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts @@ -21,15 +21,18 @@ describe('7.10.0', () => { ); }); - test('changes nothing on alerts by other plugins', () => { + test('marks alerts as legacy', () => { const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; const alert = getMockData({}); - expect(migration710(alert, { log })).toMatchObject(alert); - - expect(encryptedSavedObjectsSetup.createMigration).toHaveBeenCalledWith( - expect.any(Function), - expect.any(Function) - ); + expect(migration710(alert, { log })).toMatchObject({ + ...alert, + attributes: { + ...alert.attributes, + meta: { + versionApiKeyLastmodified: 'pre-7.10.0', + }, + }, + }); }); test('migrates the consumer for metrics', () => { @@ -42,6 +45,26 @@ describe('7.10.0', () => { attributes: { ...alert.attributes, consumer: 'infrastructure', + meta: { + versionApiKeyLastmodified: 'pre-7.10.0', + }, + }, + }); + }); + + test('migrates the consumer for siem', () => { + const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; + const alert = getMockData({ + consumer: 'securitySolution', + }); + expect(migration710(alert, { log })).toMatchObject({ + ...alert, + attributes: { + ...alert.attributes, + consumer: 'siem', + meta: { + versionApiKeyLastmodified: 'pre-7.10.0', + }, }, }); }); @@ -56,6 +79,9 @@ describe('7.10.0', () => { attributes: { ...alert.attributes, consumer: 'alerts', + meta: { + versionApiKeyLastmodified: 'pre-7.10.0', + }, }, }); }); @@ -64,9 +90,9 @@ describe('7.10.0', () => { describe('7.10.0 migrates with failure', () => { beforeEach(() => { jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockRejectedValueOnce( - new Error(`Can't migrate!`) as never - ); + encryptedSavedObjectsSetup.createMigration.mockImplementationOnce(() => () => { + throw new Error(`Can't migrate!`); + }); }); test('should show the proper exception', () => { @@ -82,7 +108,7 @@ describe('7.10.0 migrates with failure', () => { }, }); expect(log.error).toHaveBeenCalledWith( - `encryptedSavedObject migration failed for alert ${alert.id} with error: migrationFunc is not a function`, + `encryptedSavedObject 7.10.0 migration failed for alert ${alert.id} with error: Can't migrate!`, { alertDocument: { ...alert, diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.ts index 30570eeb0a4530..c88f4d786c2125 100644 --- a/x-pack/plugins/alerts/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerts/server/saved_objects/migrations.ts @@ -11,54 +11,54 @@ import { } from '../../../../../src/core/server'; import { RawAlert } from '../types'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; +import { + APP_ID as SIEM_APP_ID, + SERVER_APP_ID as SIEM_SERVER_APP_ID, +} from '../../../security_solution/common/constants'; + +export const LEGACY_LAST_MODIFIED_VERSION = 'pre-7.10.0'; export function getMigrations( encryptedSavedObjects: EncryptedSavedObjectsPluginSetup ): SavedObjectMigrationMap { - const alertsMigration = changeAlertingConsumer(encryptedSavedObjects, 'alerting', 'alerts'); - - const infrastructureMigration = changeAlertingConsumer( - encryptedSavedObjects, - 'metrics', - 'infrastructure' - ); + const migrationWhenRBACWasIntroduced = markAsLegacyAndChangeConsumer(encryptedSavedObjects); return { - '7.10.0': (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => { - if (doc.attributes.consumer === 'alerting') { - return executeMigration(doc, context, alertsMigration); - } else if (doc.attributes.consumer === 'metrics') { - return executeMigration(doc, context, infrastructureMigration); - } - return doc; - }, + '7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'), }; } -function executeMigration( - doc: SavedObjectUnsanitizedDoc, - context: SavedObjectMigrationContext, - migrationFunc: SavedObjectMigrationFn +function executeMigrationWithErrorHandling( + migrationFunc: SavedObjectMigrationFn, + version: string ) { - try { - return migrationFunc(doc, context); - } catch (ex) { - context.log.error( - `encryptedSavedObject migration failed for alert ${doc.id} with error: ${ex.message}`, - { alertDocument: doc } - ); - } - return doc; + return (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => { + try { + return migrationFunc(doc, context); + } catch (ex) { + context.log.error( + `encryptedSavedObject ${version} migration failed for alert ${doc.id} with error: ${ex.message}`, + { alertDocument: doc } + ); + } + return doc; + }; } -function changeAlertingConsumer( - encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, - from: string, - to: string +const consumersToChange: Map = new Map( + Object.entries({ + alerting: 'alerts', + metrics: 'infrastructure', + [SIEM_APP_ID]: SIEM_SERVER_APP_ID, + }) +); +function markAsLegacyAndChangeConsumer( + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup ): SavedObjectMigrationFn { return encryptedSavedObjects.createMigration( function shouldBeMigrated(doc): doc is SavedObjectUnsanitizedDoc { - return doc.attributes.consumer === from; + // migrate all documents in 7.10 in order to add the "meta" RBAC field + return true; }, (doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc => { const { @@ -68,7 +68,11 @@ function changeAlertingConsumer( ...doc, attributes: { ...doc.attributes, - consumer: consumer === from ? to : consumer, + consumer: consumersToChange.get(consumer) ?? consumer, + // mark any alert predating 7.10 as a legacy alert + meta: { + versionApiKeyLastmodified: LEGACY_LAST_MODIFIED_VERSION, + }, }, }; } diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts index 677040d8174e39..2f0754d34492fb 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts @@ -10,6 +10,7 @@ import { loggingSystemMock } from '../../../../../src/core/server/mocks'; import { actionsMock, actionsClientMock } from '../../../actions/server/mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { KibanaRequest } from 'kibana/server'; +import { asSavedObjectExecutionSource } from '../../../actions/server'; const alertType: AlertType = { id: 'test', @@ -79,20 +80,27 @@ test('enqueues execution per selected action', async () => { ).toHaveBeenCalledWith(createExecutionHandlerParams.request); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", + "contextVal": "My goes here", + "foo": true, + "stateVal": "My goes here", + }, + "source": Object { + "source": Object { "id": "1", - "params": Object { - "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", - "contextVal": "My goes here", - "foo": true, - "stateVal": "My goes here", - }, - "spaceId": "default", + "type": "alert", }, - ] - `); + "type": "SAVED_OBJECT", + }, + "spaceId": "default", + }, + ] + `); const eventLogger = createExecutionHandlerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(1); @@ -161,6 +169,10 @@ test(`doesn't call actionsPlugin.execute for disabled actionTypes`, async () => contextVal: 'My other goes here', stateVal: 'My other goes here', }, + source: asSavedObjectExecutionSource({ + id: '1', + type: 'alert', + }), spaceId: 'default', apiKey: createExecutionHandlerParams.apiKey, }); @@ -231,20 +243,27 @@ test('context attribute gets parameterized', async () => { }); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", + "contextVal": "My context-val goes here", + "foo": true, + "stateVal": "My goes here", + }, + "source": Object { + "source": Object { "id": "1", - "params": Object { - "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", - "contextVal": "My context-val goes here", - "foo": true, - "stateVal": "My goes here", - }, - "spaceId": "default", + "type": "alert", }, - ] - `); + "type": "SAVED_OBJECT", + }, + "spaceId": "default", + }, + ] + `); }); test('state attribute gets parameterized', async () => { @@ -257,20 +276,27 @@ test('state attribute gets parameterized', async () => { }); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", + "contextVal": "My goes here", + "foo": true, + "stateVal": "My state-val goes here", + }, + "source": Object { + "source": Object { "id": "1", - "params": Object { - "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", - "contextVal": "My goes here", - "foo": true, - "stateVal": "My state-val goes here", - }, - "spaceId": "default", + "type": "alert", }, - ] - `); + "type": "SAVED_OBJECT", + }, + "spaceId": "default", + }, + ] + `); }); test(`logs an error when action group isn't part of actionGroups available for the alertType`, async () => { diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts index bf074e2c60ee39..f873b0178ece99 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts @@ -7,7 +7,10 @@ import { map } from 'lodash'; import { Logger, KibanaRequest } from '../../../../../src/core/server'; import { transformActionParams } from './transform_action_params'; -import { PluginStartContract as ActionsPluginStartContract } from '../../../actions/server'; +import { + PluginStartContract as ActionsPluginStartContract, + asSavedObjectExecutionSource, +} from '../../../actions/server'; import { IEventLogger, IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { EVENT_LOG_ACTIONS } from '../plugin'; import { @@ -97,6 +100,10 @@ export function createExecutionHandler({ params: action.params, spaceId, apiKey, + source: asSavedObjectExecutionSource({ + id: alertId, + type: 'alert', + }), }); const namespace = spaceId === 'default' ? {} : { namespace: spaceId }; diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts index 58b1fa4a123e10..801d30b6406ee5 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts @@ -211,17 +211,24 @@ describe('Task Runner', () => { await taskRunner.run(); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", - "id": "1", - "params": Object { - "foo": true, - }, - "spaceId": undefined, - }, - ] - `); + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "foo": true, + }, + "source": Object { + "source": Object { + "id": "1", + "type": "alert", + }, + "type": "SAVED_OBJECT", + }, + "spaceId": undefined, + }, + ] + `); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); @@ -351,17 +358,24 @@ describe('Task Runner', () => { }); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", - "id": "1", - "params": Object { - "foo": true, - }, - "spaceId": undefined, - }, - ] - `); + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "foo": true, + }, + "source": Object { + "source": Object { + "id": "1", + "type": "alert", + }, + "type": "SAVED_OBJECT", + }, + "spaceId": undefined, + }, + ] + `); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 20943ba28885cb..8d568e8b7ecd1b 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -111,6 +111,10 @@ export interface RawAlertAction extends SavedObjectAttributes { params: AlertActionParams; } +export interface AlertMeta extends SavedObjectAttributes { + versionApiKeyLastmodified?: string; +} + export type PartialAlert = Pick & Partial>; export interface RawAlert extends SavedObjectAttributes { @@ -122,7 +126,7 @@ export interface RawAlert extends SavedObjectAttributes { schedule: SavedObjectAttributes; actions: RawAlertAction[]; params: SavedObjectAttributes; - scheduledTaskId?: string; + scheduledTaskId?: string | null; createdBy: string | null; updatedBy: string | null; createdAt: string; @@ -131,6 +135,7 @@ export interface RawAlert extends SavedObjectAttributes { throttle: string | null; muteAll: boolean; mutedInstanceIds: string[]; + meta?: AlertMeta; } export type AlertInfoParams = Pick< diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/index.tsx index d0bd674ef5c797..b027609fd3a7f3 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/index.tsx @@ -5,11 +5,9 @@ */ import * as React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; - -import { useFetcher } from '../../../../hooks/useFetcher'; -import { useUrlParams } from '../../../../hooks/useUrlParams'; import { CLS_LABEL, FID_LABEL, LCP_LABEL } from './translations'; import { CoreVitalItem } from './CoreVitalItem'; +import { UXMetrics } from '../UXMetrics'; const CoreVitalsThresholds = { LCP: { good: '2.5s', bad: '4.0s' }, @@ -17,27 +15,12 @@ const CoreVitalsThresholds = { CLS: { good: '0.1', bad: '0.25' }, }; -export function CoreVitals() { - const { urlParams, uiFilters } = useUrlParams(); - - const { start, end } = urlParams; - - const { data, status } = useFetcher( - (callApmApi) => { - const { serviceName } = uiFilters; - if (start && end && serviceName) { - return callApmApi({ - pathname: '/api/apm/rum-client/web-core-vitals', - params: { - query: { start, end, uiFilters: JSON.stringify(uiFilters) }, - }, - }); - } - return Promise.resolve(null); - }, - [start, end, uiFilters] - ); +interface Props { + data?: UXMetrics | null; + loading: boolean; +} +export function CoreVitals({ data, loading }: Props) { const { lcp, lcpRanks, fid, fidRanks, cls, clsRanks } = data || {}; return ( @@ -47,7 +30,7 @@ export function CoreVitals() { title={LCP_LABEL} value={lcp ? lcp + 's' : '0'} ranks={lcpRanks} - loading={status !== 'success'} + loading={loading} thresholds={CoreVitalsThresholds.LCP} /> @@ -56,7 +39,7 @@ export function CoreVitals() { title={FID_LABEL} value={fid ? fid + 's' : '0'} ranks={fidRanks} - loading={status !== 'success'} + loading={loading} thresholds={CoreVitalsThresholds.FID} /> @@ -65,7 +48,7 @@ export function CoreVitals() { title={CLS_LABEL} value={cls ?? '0'} ranks={clsRanks} - loading={status !== 'success'} + loading={loading} thresholds={CoreVitalsThresholds.CLS} /> diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/translations.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/translations.ts index 136dfb279e3362..063d062db76506 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/translations.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/translations.ts @@ -26,6 +26,27 @@ export const TBT_LABEL = i18n.translate('xpack.apm.rum.coreVitals.tbt', { defaultMessage: 'Total blocking time', }); +export const NO_OF_LONG_TASK = i18n.translate( + 'xpack.apm.rum.uxMetrics.noOfLongTasks', + { + defaultMessage: 'No. of long tasks', + } +); + +export const LONGEST_LONG_TASK = i18n.translate( + 'xpack.apm.rum.uxMetrics.longestLongTasks', + { + defaultMessage: 'Longest long task duration', + } +); + +export const SUM_LONG_TASKS = i18n.translate( + 'xpack.apm.rum.uxMetrics.sumLongTasks', + { + defaultMessage: 'Total long tasks duration', + } +); + export const POOR_LABEL = i18n.translate('xpack.apm.rum.coreVitals.poor', { defaultMessage: 'a poor', }); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx index 48c0f6cc60d841..296c0d268d1311 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx @@ -17,7 +17,7 @@ import { PageViewsTrend } from './PageViewsTrend'; import { PageLoadDistribution } from './PageLoadDistribution'; import { I18LABELS } from './translations'; import { VisitorBreakdown } from './VisitorBreakdown'; -import { CoreVitals } from './CoreVitals'; +import { UXMetrics } from './UXMetrics'; import { VisitorBreakdownMap } from './VisitorBreakdownMap'; export function RumDashboard() { @@ -37,17 +37,7 @@ export function RumDashboard() { - - - - -

{I18LABELS.coreWebVitals}

-
- - -
-
-
+
diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx new file mode 100644 index 00000000000000..b4a350d41557f4 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx @@ -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. + */ + +import React from 'react'; +import { EuiFlexItem, EuiStat, EuiFlexGroup } from '@elastic/eui'; +import { UXMetrics } from './index'; +import { + FCP_LABEL, + LONGEST_LONG_TASK, + NO_OF_LONG_TASK, + SUM_LONG_TASKS, + TBT_LABEL, +} from '../CoreVitals/translations'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; +import { useFetcher } from '../../../../hooks/useFetcher'; + +export function formatToSec( + value?: number | string, + fromUnit = 'MicroSec' +): string { + const valueInMs = Number(value ?? 0) / (fromUnit === 'MicroSec' ? 1000 : 1); + + if (valueInMs < 1000) { + return valueInMs + ' ms'; + } + return (valueInMs / 1000).toFixed(2) + ' s'; +} +const STAT_STYLE = { width: '240px' }; + +interface Props { + data?: UXMetrics | null; + loading: boolean; +} + +export function KeyUXMetrics({ data, loading }: Props) { + const { urlParams, uiFilters } = useUrlParams(); + + const { start, end, serviceName } = urlParams; + + const { data: longTaskData, status } = useFetcher( + (callApmApi) => { + if (start && end && serviceName) { + return callApmApi({ + pathname: '/api/apm/rum-client/long-task-metrics', + params: { + query: { start, end, uiFilters: JSON.stringify(uiFilters) }, + }, + }); + } + return Promise.resolve(null); + }, + [start, end, serviceName, uiFilters] + ); + + // Note: FCP value is in ms unit + return ( + + + + + + + + + + + + + + + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/FormatToSec.test.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/FormatToSec.test.ts new file mode 100644 index 00000000000000..6cdf469d980faa --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/FormatToSec.test.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 { formatToSec } from '../KeyUXMetrics'; + +describe('FormatToSec', () => { + test('it returns the expected value', () => { + expect(formatToSec(3413000)).toStrictEqual('3.41 s'); + expect(formatToSec(15548000)).toStrictEqual('15.55 s'); + expect(formatToSec(1147.5, 'ms')).toStrictEqual('1.15 s'); + expect(formatToSec(114, 'ms')).toStrictEqual('114 ms'); + expect(formatToSec(undefined, 'ms')).toStrictEqual('0 ms'); + expect(formatToSec(undefined)).toStrictEqual('0 ms'); + expect(formatToSec('1123232')).toStrictEqual('1.12 s'); + }); +}); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx new file mode 100644 index 00000000000000..bb5d37a10fb338 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx @@ -0,0 +1,78 @@ +/* + * 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 { + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiPanel, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { I18LABELS } from '../translations'; +import { CoreVitals } from '../CoreVitals'; +import { KeyUXMetrics } from './KeyUXMetrics'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; +import { useFetcher } from '../../../../hooks/useFetcher'; + +export interface UXMetrics { + cls: string; + fid: string; + lcp: string; + tbt: string; + fcp: number; + lcpRanks: number[]; + fidRanks: number[]; + clsRanks: number[]; +} + +export function UXMetrics() { + const { urlParams, uiFilters } = useUrlParams(); + + const { start, end } = urlParams; + + const { data, status } = useFetcher( + (callApmApi) => { + const { serviceName } = uiFilters; + if (start && end && serviceName) { + return callApmApi({ + pathname: '/api/apm/rum-client/web-core-vitals', + params: { + query: { start, end, uiFilters: JSON.stringify(uiFilters) }, + }, + }); + } + return Promise.resolve(null); + }, + [start, end, uiFilters] + ); + + return ( + + + + +

{I18LABELS.userExperienceMetrics}

+
+ + +
+
+ + + + + +

{I18LABELS.coreWebVitals}

+
+ + +
+
+
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts index ec135168729b48..1fafb7d1ed4d01 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts @@ -64,6 +64,9 @@ export const I18LABELS = { defaultMessage: 'Operating system', } ), + userExperienceMetrics: i18n.translate('xpack.apm.rum.userExperienceMetrics', { + defaultMessage: 'User experience metrics', + }), avgPageLoadDuration: i18n.translate( 'xpack.apm.rum.visitorBreakdownMap.avgPageLoadDuration', { diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js b/x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js index f6f64db164f9a4..1de98515e1aac6 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js +++ b/x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js @@ -11,6 +11,7 @@ const { promisify } = require('util'); const path = require('path'); const json5 = require('json5'); const execa = require('execa'); +const { omit } = require('lodash'); const readFile = promisify(fs.readFile); const writeFile = promisify(fs.writeFile); @@ -27,6 +28,7 @@ function prepareParentTsConfigs() { return Promise.all( [ path.resolve(xpackRoot, 'tsconfig.json'), + path.resolve(kibanaRoot, 'tsconfig.base.json'), path.resolve(kibanaRoot, 'tsconfig.json'), ].map(async (filename) => { const config = json5.parse(await readFile(filename, 'utf-8')); @@ -35,7 +37,11 @@ function prepareParentTsConfigs() { filename, JSON.stringify( { - ...config, + ...omit(config, 'references'), + compilerOptions: { + ...config.compilerOptions, + incremental: false, + }, include: [], }, null, diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js b/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js index c75e16f74b9325..b32bb9b20796eb 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js +++ b/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js @@ -13,6 +13,7 @@ const tsconfigTpl = path.resolve(__dirname, './tsconfig.json'); const filesToIgnore = [ path.resolve(xpackRoot, 'tsconfig.json'), path.resolve(kibanaRoot, 'tsconfig.json'), + path.resolve(kibanaRoot, 'tsconfig.base.json'), ]; module.exports = { diff --git a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap index 2cb28d378e8fd5..32b4343a7f81b6 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap @@ -72,6 +72,64 @@ Object { } `; +exports[`rum client dashboard queries fetches long task metrics 1`] = ` +Object { + "apm": Object { + "events": Array [ + "span", + ], + }, + "body": Object { + "aggs": Object { + "transIds": Object { + "aggs": Object { + "longestLongTask": Object { + "max": Object { + "field": "span.duration.us", + }, + }, + "sumLongTask": Object { + "sum": Object { + "field": "span.duration.us", + }, + }, + }, + "terms": Object { + "field": "transaction.id", + "size": 1000, + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "@timestamp": Object { + "format": "epoch_millis", + "gte": 1528113600000, + "lte": 1528977600000, + }, + }, + }, + Object { + "term": Object { + "span.type": "longtask", + }, + }, + Object { + "term": Object { + "my.custom.ui.filter": "foo-bar", + }, + }, + ], + }, + }, + "size": 0, + }, +} +`; + exports[`rum client dashboard queries fetches page load distribution 1`] = ` Object { "apm": Object { @@ -190,6 +248,126 @@ Object { } `; +exports[`rum client dashboard queries fetches rum core vitals 1`] = ` +Object { + "apm": Object { + "events": Array [ + "transaction", + ], + }, + "body": Object { + "aggs": Object { + "cls": Object { + "percentiles": Object { + "field": "transaction.experience.cls", + "percents": Array [ + 50, + ], + }, + }, + "clsRanks": Object { + "percentile_ranks": Object { + "field": "transaction.experience.cls", + "keyed": false, + "values": Array [ + 0.1, + 0.25, + ], + }, + }, + "fcp": Object { + "percentiles": Object { + "field": "transaction.marks.agent.firstContentfulPaint", + "percents": Array [ + 50, + ], + }, + }, + "fid": Object { + "percentiles": Object { + "field": "transaction.experience.fid", + "percents": Array [ + 50, + ], + }, + }, + "fidRanks": Object { + "percentile_ranks": Object { + "field": "transaction.experience.fid", + "keyed": false, + "values": Array [ + 100, + 300, + ], + }, + }, + "lcp": Object { + "percentiles": Object { + "field": "transaction.marks.agent.largestContentfulPaint", + "percents": Array [ + 50, + ], + }, + }, + "lcpRanks": Object { + "percentile_ranks": Object { + "field": "transaction.marks.agent.largestContentfulPaint", + "keyed": false, + "values": Array [ + 2500, + 4000, + ], + }, + }, + "tbt": Object { + "percentiles": Object { + "field": "transaction.experience.tbt", + "percents": Array [ + 50, + ], + }, + }, + }, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "@timestamp": Object { + "format": "epoch_millis", + "gte": 1528113600000, + "lte": 1528977600000, + }, + }, + }, + Object { + "term": Object { + "transaction.type": "page-load", + }, + }, + Object { + "exists": Object { + "field": "transaction.marks.navigationTiming.fetchStart", + }, + }, + Object { + "term": Object { + "my.custom.ui.filter": "foo-bar", + }, + }, + Object { + "term": Object { + "user_agent.name": "Chrome", + }, + }, + ], + }, + }, + "size": 0, + }, +} +`; + exports[`rum client dashboard queries fetches rum services 1`] = ` Object { "apm": Object { diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts new file mode 100644 index 00000000000000..fba1b6a0a634d8 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts @@ -0,0 +1,117 @@ +/* + * 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 { + getRumLongTasksProjection, + getRumOverviewProjection, +} from '../../projections/rum_overview'; +import { mergeProjection } from '../../projections/util/merge_projection'; +import { + Setup, + SetupTimeRange, + SetupUIFilters, +} from '../helpers/setup_request'; +import { SPAN_DURATION } from '../../../common/elasticsearch_fieldnames'; + +export async function getLongTaskMetrics({ + setup, +}: { + setup: Setup & SetupTimeRange & SetupUIFilters; +}) { + const projection = getRumLongTasksProjection({ + setup, + }); + + const params = mergeProjection(projection, { + body: { + size: 0, + query: { + bool: projection.body.query.bool, + }, + aggs: { + transIds: { + terms: { + field: 'transaction.id', + size: 1000, + }, + aggs: { + sumLongTask: { + sum: { + field: SPAN_DURATION, + }, + }, + longestLongTask: { + max: { + field: SPAN_DURATION, + }, + }, + }, + }, + }, + }, + }); + + const { apmEventClient } = setup; + + const response = await apmEventClient.search(params); + const { transIds } = response.aggregations ?? {}; + + const validTransactions: string[] = await filterPageLoadTransactions( + setup, + (transIds?.buckets ?? []).map((bucket) => bucket.key as string) + ); + let noOfLongTasks = 0; + let sumOfLongTasks = 0; + let longestLongTask = 0; + + (transIds?.buckets ?? []).forEach((bucket) => { + if (validTransactions.includes(bucket.key as string)) { + noOfLongTasks += bucket.doc_count; + sumOfLongTasks += bucket.sumLongTask.value ?? 0; + if ((bucket.longestLongTask.value ?? 0) > longestLongTask) { + longestLongTask = bucket.longestLongTask.value!; + } + } + }); + return { + noOfLongTasks, + sumOfLongTasks, + longestLongTask, + }; +} + +async function filterPageLoadTransactions( + setup: Setup & SetupTimeRange & SetupUIFilters, + transactionIds: string[] +) { + const projection = getRumOverviewProjection({ + setup, + }); + + const params = mergeProjection(projection, { + body: { + size: transactionIds.length, + query: { + bool: { + must: [ + { + terms: { + 'transaction.id': transactionIds, + }, + }, + ], + filter: [...projection.body.query.bool.filter], + }, + }, + _source: ['transaction.id'], + }, + }); + + const { apmEventClient } = setup; + + const response = await apmEventClient.search(params); + return response.hits.hits.map((hit) => (hit._source as any).transaction.id)!; +} diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts index 9395e5fe14336a..25d1877cbb1239 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts @@ -13,8 +13,10 @@ import { } from '../helpers/setup_request'; import { CLS_FIELD, + FCP_FIELD, FID_FIELD, LCP_FIELD, + TBT_FIELD, } from '../../../common/elasticsearch_fieldnames'; export async function getWebCoreVitals({ @@ -60,6 +62,18 @@ export async function getWebCoreVitals({ percents: [50], }, }, + tbt: { + percentiles: { + field: TBT_FIELD, + percents: [50], + }, + }, + fcp: { + percentiles: { + field: FCP_FIELD, + percents: [50], + }, + }, lcpRanks: { percentile_ranks: { field: LCP_FIELD, @@ -88,21 +102,13 @@ export async function getWebCoreVitals({ const { apmEventClient } = setup; const response = await apmEventClient.search(params); - const { - lcp, - cls, - fid, - lcpRanks, - fidRanks, - clsRanks, - } = response.aggregations!; + const { lcp, cls, fid, tbt, fcp, lcpRanks, fidRanks, clsRanks } = + response.aggregations ?? {}; const getRanksPercentages = ( ranks: Array<{ key: number; value: number }> ) => { - const ranksVal = (ranks ?? [0, 0]).map( - ({ value }) => value?.toFixed(0) ?? 0 - ); + const ranksVal = ranks.map(({ value }) => value?.toFixed(0) ?? 0); return [ Number(ranksVal?.[0]), Number(ranksVal?.[1]) - Number(ranksVal?.[0]), @@ -110,14 +116,21 @@ export async function getWebCoreVitals({ ]; }; + const defaultRanks = [ + { value: 0, key: 0 }, + { value: 0, key: 0 }, + ]; + // Divide by 1000 to convert ms into seconds return { - cls: String(cls.values['50.0'] || 0), - fid: ((fid.values['50.0'] || 0) / 1000).toFixed(2), - lcp: ((lcp.values['50.0'] || 0) / 1000).toFixed(2), + cls: String(cls?.values['50.0'] || 0), + fid: ((fid?.values['50.0'] || 0) / 1000).toFixed(2), + lcp: ((lcp?.values['50.0'] || 0) / 1000).toFixed(2), + tbt: ((tbt?.values['50.0'] || 0) / 1000).toFixed(2), + fcp: fcp?.values['50.0'] || 0, - lcpRanks: getRanksPercentages(lcpRanks.values), - fidRanks: getRanksPercentages(fidRanks.values), - clsRanks: getRanksPercentages(clsRanks.values), + lcpRanks: getRanksPercentages(lcpRanks?.values ?? defaultRanks), + fidRanks: getRanksPercentages(fidRanks?.values ?? defaultRanks), + clsRanks: getRanksPercentages(clsRanks?.values ?? defaultRanks), }; } diff --git a/x-pack/plugins/apm/server/lib/rum_client/queries.test.ts b/x-pack/plugins/apm/server/lib/rum_client/queries.test.ts index 986e99f79904a7..14cec21cceb794 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/queries.test.ts @@ -12,6 +12,8 @@ import { getClientMetrics } from './get_client_metrics'; import { getPageViewTrends } from './get_page_view_trends'; import { getPageLoadDistribution } from './get_page_load_distribution'; import { getRumServices } from './get_rum_services'; +import { getLongTaskMetrics } from './get_long_task_metrics'; +import { getWebCoreVitals } from './get_web_core_vitals'; describe('rum client dashboard queries', () => { let mock: SearchParamsMock; @@ -59,4 +61,22 @@ describe('rum client dashboard queries', () => { ); expect(mock.params).toMatchSnapshot(); }); + + it('fetches rum core vitals', async () => { + mock = await inspectSearchParams((setup) => + getWebCoreVitals({ + setup, + }) + ); + expect(mock.params).toMatchSnapshot(); + }); + + it('fetches long task metrics', async () => { + mock = await inspectSearchParams((setup) => + getLongTaskMetrics({ + setup, + }) + ); + expect(mock.params).toMatchSnapshot(); + }); }); diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts index f6e331a09fa651..a7771d14532d63 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { find, uniqBy } from 'lodash'; +import { ENVIRONMENT_NOT_DEFINED } from '../../../common/environment_filter_values'; import { SERVICE_ENVIRONMENT, SERVICE_NAME, @@ -35,7 +36,7 @@ export function getConnections( SERVICE_NAME in node && (node as ServiceConnectionNode)[SERVICE_NAME] === serviceName; } - if (environment) { + if (environment && environment !== ENVIRONMENT_NOT_DEFINED.value) { matches = matches && SERVICE_ENVIRONMENT in node && diff --git a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts index d6d681f24ab859..dfc4e02c25a7f5 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_trace_sample_ids.ts @@ -14,6 +14,7 @@ import { TRACE_ID, SPAN_DESTINATION_SERVICE_RESOURCE, } from '../../../common/elasticsearch_fieldnames'; +import { getEnvironmentUiFilterES } from '../helpers/convert_ui_filters/get_environment_ui_filter_es'; const MAX_TRACES_TO_INSPECT = 1000; @@ -47,9 +48,7 @@ export async function getTraceSampleIds({ query.bool.filter.push({ term: { [SERVICE_NAME]: serviceName } }); } - if (environment) { - query.bool.filter.push({ term: { [SERVICE_ENVIRONMENT]: environment } }); - } + query.bool.filter.push(...getEnvironmentUiFilterES(environment)); const fingerprintBucketSize = serviceName ? config['xpack.apm.serviceMapFingerprintBucketSize'] diff --git a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts index 17f9743ae9f006..9aebadba23b2a5 100644 --- a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts +++ b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts @@ -88,7 +88,7 @@ export async function getTraceItems( // explicit intermediary types to avoid TS "excessively deep" error PromiseValueType, PromiseValueType - ] = await Promise.all([errorResponsePromise, traceResponsePromise]); + ] = (await Promise.all([errorResponsePromise, traceResponsePromise])) as any; const exceedsMax = traceResponse.hits.total.value > maxTraceItems; diff --git a/x-pack/plugins/apm/server/projections/rum_overview.ts b/x-pack/plugins/apm/server/projections/rum_overview.ts index 4588ec2a0451f4..ac5a7df2352676 100644 --- a/x-pack/plugins/apm/server/projections/rum_overview.ts +++ b/x-pack/plugins/apm/server/projections/rum_overview.ts @@ -9,7 +9,10 @@ import { SetupTimeRange, SetupUIFilters, } from '../../server/lib/helpers/setup_request'; -import { TRANSACTION_TYPE } from '../../common/elasticsearch_fieldnames'; +import { + SPAN_TYPE, + TRANSACTION_TYPE, +} from '../../common/elasticsearch_fieldnames'; import { rangeFilter } from '../../common/utils/range_filter'; import { ProcessorEvent } from '../../common/processor_event'; @@ -45,3 +48,30 @@ export function getRumOverviewProjection({ }, }; } + +export function getRumLongTasksProjection({ + setup, +}: { + setup: Setup & SetupTimeRange & SetupUIFilters; +}) { + const { start, end, uiFiltersES } = setup; + + const bool = { + filter: [ + { range: rangeFilter(start, end) }, + { term: { [SPAN_TYPE]: 'longtask' } }, + ...uiFiltersES, + ], + }; + + return { + apm: { + events: [ProcessorEvent.span], + }, + body: { + query: { + bool, + }, + }, + }; +} diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index cf7a02cde975c6..a15b37945ce01a 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -78,6 +78,7 @@ import { rumServicesRoute, rumVisitorsBreakdownRoute, rumWebCoreVitals, + rumLongTaskMetrics, } from './rum_client'; import { observabilityOverviewHasDataRoute, @@ -174,6 +175,7 @@ const createApmApi = () => { .add(rumServicesRoute) .add(rumVisitorsBreakdownRoute) .add(rumWebCoreVitals) + .add(rumLongTaskMetrics) // Observability dashboard .add(observabilityOverviewHasDataRoute) diff --git a/x-pack/plugins/apm/server/routes/rum_client.ts b/x-pack/plugins/apm/server/routes/rum_client.ts index e17791f56eef27..d4adca6a6658f2 100644 --- a/x-pack/plugins/apm/server/routes/rum_client.ts +++ b/x-pack/plugins/apm/server/routes/rum_client.ts @@ -15,6 +15,7 @@ import { getPageLoadDistBreakdown } from '../lib/rum_client/get_pl_dist_breakdow import { getRumServices } from '../lib/rum_client/get_rum_services'; import { getVisitorBreakdown } from '../lib/rum_client/get_visitor_breakdown'; import { getWebCoreVitals } from '../lib/rum_client/get_web_core_vitals'; +import { getLongTaskMetrics } from '../lib/rum_client/get_long_task_metrics'; export const percentileRangeRt = t.partial({ minPercentile: t.string, @@ -130,3 +131,15 @@ export const rumWebCoreVitals = createRoute(() => ({ return getWebCoreVitals({ setup }); }, })); + +export const rumLongTaskMetrics = createRoute(() => ({ + path: '/api/apm/rum-client/long-task-metrics', + params: { + query: t.intersection([uiFiltersRt, rangeRt]), + }, + handler: async ({ context, request }) => { + const setup = await setupRequest(context, request); + + return getLongTaskMetrics({ setup }); + }, +})); diff --git a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts index bbd2c9eb86249d..44211601204cd9 100644 --- a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts +++ b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts @@ -94,6 +94,9 @@ export interface AggregationOptionsByType { percents?: number[]; hdr?: { number_of_significant_value_digits: number }; } & AggregationSourceOptions; + stats: { + field: string; + }; extended_stats: { field: string; }; @@ -223,6 +226,13 @@ interface AggregationResponsePart< percentiles: { values: Record; }; + stats: { + count: number; + min: number | null; + max: number | null; + avg: number | null; + sum: number | null; + }; extended_stats: { count: number; min: number | null; diff --git a/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js b/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js index 83be5520943b10..75569f472e2198 100644 --- a/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js +++ b/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { isEmpty } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import isEmpty from 'lodash/isEmpty'; import './brace_imports'; - import { EuiForm, EuiButton, @@ -25,6 +25,17 @@ import { GrokdebuggerRequest } from '../../models/grokdebugger_request'; import { withKibana } from '../../../../../../src/plugins/kibana_react/public'; import { FormattedMessage } from '@kbn/i18n/react'; +const i18nTexts = { + simulate: { + errorTitle: i18n.translate('xpack.grokDebugger.simulate.errorTitle', { + defaultMessage: 'Simulate error', + }), + unknownErrorTitle: i18n.translate('xpack.grokDebugger.unknownErrorTitle', { + defaultMessage: 'Something went wrong', + }), + }, +}; + export class GrokDebuggerComponent extends React.Component { constructor(props) { super(props); @@ -84,12 +95,13 @@ export class GrokDebuggerComponent extends React.Component { if (!isEmpty(simulateResponse.error)) { notifications.toasts.addDanger({ - body: simulateResponse.error, + title: i18nTexts.simulate.errorTitle, + text: simulateResponse.error, }); } } catch (e) { - notifications.toasts.addDanger({ - body: e, + notifications.toasts.addError(e, { + title: i18nTexts.simulate.unknownErrorTitle, }); } }; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criteria.tsx index 7a4b2ecaa5e437..627ea2bbef429d 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criteria.tsx +++ b/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criteria.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; +import { EuiFlexItem, EuiFlexGroup, EuiAccordion } from '@elastic/eui'; import { IFieldType } from 'src/plugins/data/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { IErrorObject } from '../../../../../../triggers_actions_ui/public/types'; @@ -44,23 +44,29 @@ export const Criteria: React.FC = ({ {criteria.map((criterion, idx) => { return ( - - 1} - errors={errors[idx.toString()] as IErrorObject} - /> + 1} + errors={errors[idx.toString()] as IErrorObject} + /> + } + key={idx} + arrowDisplay="right" + > - + ); })} diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion.tsx b/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion.tsx index 66bde380a6d9e6..9ee9373bd2c141 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion.tsx +++ b/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion.tsx @@ -162,7 +162,10 @@ export const Criterion: React.FC = ({ value={criterion.field} isActive={isFieldPopoverOpen} color={errors.field.length === 0 ? 'secondary' : 'danger'} - onClick={() => setIsFieldPopoverOpen(true)} + onClick={(e) => { + e.stopPropagation(); + setIsFieldPopoverOpen(true); + }} /> } isOpen={isFieldPopoverOpen} @@ -202,7 +205,10 @@ export const Criterion: React.FC = ({ ? 'secondary' : 'danger' } - onClick={() => setIsComparatorPopoverOpen(true)} + onClick={(e) => { + e.stopPropagation(); + setIsComparatorPopoverOpen(true); + }} /> } isOpen={isComparatorPopoverOpen} @@ -260,7 +266,10 @@ export const Criterion: React.FC = ({ })} color={'danger'} iconType={'trash'} - onClick={() => removeCriterion(idx)} + onClick={(e: React.MouseEvent) => { + e.stopPropagation(); + removeCriterion(idx); + }} />
)} diff --git a/x-pack/plugins/lens/public/assets/axis_bottom.tsx b/x-pack/plugins/lens/public/assets/axis_bottom.tsx new file mode 100644 index 00000000000000..9529a93e4c1cc5 --- /dev/null +++ b/x-pack/plugins/lens/public/assets/axis_bottom.tsx @@ -0,0 +1,30 @@ +/* + * 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 * as React from 'react'; + +export const EuiIconAxisBottom = ({ + title, + titleId, + ...props +}: { + title: string; + titleId: string; +}) => ( + + {title ? {title} : null} + + + +); diff --git a/x-pack/plugins/lens/public/assets/axis_left.tsx b/x-pack/plugins/lens/public/assets/axis_left.tsx new file mode 100644 index 00000000000000..d1ec0b76a1bd51 --- /dev/null +++ b/x-pack/plugins/lens/public/assets/axis_left.tsx @@ -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 * as React from 'react'; + +export const EuiIconAxisLeft = ({ + title, + titleId, + ...props +}: { + title: string; + titleId: string; +}) => ( + + {title ? {title} : null} + + + + +); diff --git a/x-pack/plugins/lens/public/assets/axis_right.tsx b/x-pack/plugins/lens/public/assets/axis_right.tsx new file mode 100644 index 00000000000000..e61f87b963a05c --- /dev/null +++ b/x-pack/plugins/lens/public/assets/axis_right.tsx @@ -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 * as React from 'react'; + +export const EuiIconAxisRight = ({ + title, + titleId, + ...props +}: { + title: string; + titleId: string; +}) => ( + + {title ? {title} : null} + + + + +); diff --git a/x-pack/plugins/lens/public/assets/axis_top.tsx b/x-pack/plugins/lens/public/assets/axis_top.tsx new file mode 100644 index 00000000000000..90fbdc4a215527 --- /dev/null +++ b/x-pack/plugins/lens/public/assets/axis_top.tsx @@ -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 * as React from 'react'; + +export const EuiIconAxisTop = ({ + title, + titleId, + ...props +}: { + title: string; + titleId: string; +}) => ( + + {title ? {title} : null} + + + + + + + +); diff --git a/x-pack/plugins/lens/public/assets/legend.tsx b/x-pack/plugins/lens/public/assets/legend.tsx new file mode 100644 index 00000000000000..d73e68839d9fb4 --- /dev/null +++ b/x-pack/plugins/lens/public/assets/legend.tsx @@ -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 * as React from 'react'; + +export const EuiIconLegend = ({ title, titleId, ...props }: { title: string; titleId: string }) => ( + + {title ? {title} : null} + + + + + + + +); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_workspace_panel_wrapper.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_workspace_panel_wrapper.scss index 90cc049db96eb7..a4d8288d5e6002 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_workspace_panel_wrapper.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_workspace_panel_wrapper.scss @@ -38,5 +38,5 @@ } .lnsWorkspacePanelWrapper__toolbar { - margin-bottom: $euiSizeS; + margin-bottom: 0; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx index 682316a586626e..abbd7e0838bed0 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx @@ -9,7 +9,7 @@ import { EuiPopover, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { NativeRenderer } from '../../../native_renderer'; import { Visualization, VisualizationLayerWidgetProps } from '../../../types'; -import { ToolbarButton } from '../../../toolbar_button'; +import { ToolbarButton } from '../../../shared_components'; export function LayerSettings({ layerId, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index f1dc3fa306d150..e6503cb793a8e3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -338,7 +338,6 @@ export function SuggestionPanel({ if (lastSelectedSuggestion === index) { rollbackToCurrentVisualization(); } else { - trackSuggestionEvent(`position_${index}_of_${suggestions.length}`); setLastSelectedSuggestion(index); switchToSuggestion(dispatch, suggestion); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx index 82983862e7c03c..f4526cac39c8a7 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx @@ -19,7 +19,7 @@ import { Visualization, FramePublicAPI, Datasource } from '../../../types'; import { Action } from '../state_management'; import { getSuggestions, switchToSuggestion, Suggestion } from '../suggestion_helpers'; import { trackUiEvent } from '../../../lens_ui_telemetry'; -import { ToolbarButton } from '../../../toolbar_button'; +import { ToolbarButton } from '../../../shared_components'; interface VisualizationSelection { visualizationId: string; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index 901a86bb56e1dd..8e7d504ff7677a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -67,8 +67,14 @@ export function WorkspacePanelWrapper({ ); return ( <> -
- +
+ {activeVisualization && activeVisualization.renderToolbar && ( - + ({ isQueryValid: () => true, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx index cdfa19f53a13aa..91adbcecaf897d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx @@ -7,11 +7,12 @@ import './filter_popover.scss'; import React, { MouseEventHandler, useState } from 'react'; import { useDebounce } from 'react-use'; -import { EuiPopover, EuiFieldText, EuiSpacer, keys } from '@elastic/eui'; +import { EuiPopover, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FilterValue, defaultLabel, isQueryValid } from '.'; import { IndexPattern } from '../../../types'; import { QueryStringInput, Query } from '../../../../../../../../src/plugins/data/public'; +import { LabelInput } from '../shared_components'; export const FilterPopover = ({ filter, @@ -51,6 +52,7 @@ export const FilterPopover = ({ return ( setPopoverOpen(false)} + dataTestSubj="indexPattern-filters-label" /> ); @@ -141,53 +144,3 @@ export const QueryInput = ({ /> ); }; - -export const LabelInput = ({ - value, - onChange, - placeholder, - inputRef, - onSubmit, -}: { - value: string; - onChange: (value: string) => void; - placeholder: string; - inputRef: React.MutableRefObject; - onSubmit: () => void; -}) => { - const [inputValue, setInputValue] = useState(value); - - React.useEffect(() => { - setInputValue(value); - }, [value, setInputValue]); - - useDebounce(() => onChange(inputValue), 256, [inputValue]); - - const handleInputChange = (e: React.ChangeEvent) => { - const val = String(e.target.value); - setInputValue(val); - }; - - return ( - { - if (node) { - inputRef.current = node; - } - }} - onKeyDown={({ key }: React.KeyboardEvent) => { - if (keys.ENTER === key) { - onSubmit(); - } - }} - prepend={i18n.translate('xpack.lens.indexPattern.filters.label', { - defaultMessage: 'Label', - })} - /> - ); -}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx index 13854d1ca91d6f..6364d3913bf578 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx @@ -231,7 +231,7 @@ describe('filters', () => { expect( instance .find('[data-test-subj="indexPattern-filters-existingFilterContainer"]') - .at(2) + .at(3) .text() ).toEqual('src : 2'); }); @@ -250,7 +250,7 @@ describe('filters', () => { ); instance - .find('[data-test-subj="indexPattern-filters-existingFilterDelete"]') + .find('[data-test-subj="lns-customBucketContainer-remove"]') .at(2) .simulate('click'); expect(setStateSpy).toHaveBeenCalledWith({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx index c740f8466e1b13..bca64d1f2f3625 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx @@ -8,27 +8,14 @@ import './filters.scss'; import React, { MouseEventHandler, useState } from 'react'; import { omit } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { - EuiDragDropContext, - EuiDraggable, - EuiDroppable, - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - euiDragDropReorder, - EuiButtonIcon, - EuiButtonEmpty, - EuiIcon, - EuiFormRow, - EuiLink, - htmlIdGenerator, -} from '@elastic/eui'; +import { EuiFormRow, EuiLink, htmlIdGenerator } from '@elastic/eui'; import { updateColumnParam } from '../../../state_helpers'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { FilterPopover } from './filter_popover'; import { IndexPattern } from '../../../types'; import { Query, esKuery, esQuery } from '../../../../../../../../src/plugins/data/public'; +import { NewBucketButton, DragDropBuckets, DraggableBucketContainer } from '../shared_components'; const generateId = htmlIdGenerator(); @@ -37,10 +24,11 @@ export interface Filter { input: Query; label: string; } + export interface FilterValue { + id: string; input: Query; label: string; - id: string; } const customQueryLabel = i18n.translate('xpack.lens.indexPattern.customQuery', { @@ -73,11 +61,6 @@ export const isQueryValid = (input: Query, indexPattern: IndexPattern) => { } }; -interface DraggableLocation { - droppableId: string; - index: number; -} - export interface FiltersIndexPatternColumn extends FieldBasedIndexPatternColumn { operationType: 'filters'; params: { @@ -219,123 +202,67 @@ export const FilterList = ({ ) ); - const onDragEnd = ({ - source, - destination, - }: { - source?: DraggableLocation; - destination?: DraggableLocation; - }) => { - if (source && destination) { - const items = euiDragDropReorder(localFilters, source.index, destination.index); - updateFilters(items); - } - }; - return ( <> - setIsOpenByCreation(false)}> - - {localFilters?.map((filter: FilterValue, idx: number) => { - const { input, label, id } = filter; - const queryIsValid = isQueryValid(input, indexPattern); + setIsOpenByCreation(false)} + droppableId="FILTERS_DROPPABLE_AREA" + items={localFilters} + > + {localFilters?.map((filter: FilterValue, idx: number) => { + const isInvalid = !isQueryValid(filter.input, indexPattern); - return ( - - {(provided) => ( - - - {/* Empty for spacing */} - - - - - ( - - {label || input.query || defaultLabel} - - )} - setFilter={(f: FilterValue) => { - onChangeValue(f.id, f.input, f.label); - }} - /> - - - { - onRemoveFilter(filter.id); - }} - aria-label={i18n.translate( - 'xpack.lens.indexPattern.filters.removeCustomQuery', - { - defaultMessage: 'Remove custom query', - } - )} - title={i18n.translate('xpack.lens.indexPattern.filters.remove', { - defaultMessage: 'Remove', - })} - /> - - - + return ( + onRemoveFilter(filter.id)} + removeTitle={i18n.translate('xpack.lens.indexPattern.filters.removeCustomQuery', { + defaultMessage: 'Remove custom query', + })} + > + { + onChangeValue(f.id, f.input, f.label); + }} + Button={({ onClick }: { onClick: MouseEventHandler }) => ( + + {filter.label || filter.input.query || defaultLabel} + )} - - ); - })} - - - - + + ); + })} + + { onAddFilter(); setIsOpenByCreation(true); }} - > - {i18n.translate('xpack.lens.indexPattern.filters.addCustomQuery', { + label={i18n.translate('xpack.lens.indexPattern.filters.addCustomQuery', { defaultMessage: 'Add a custom query', })} - + /> ); }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.test.tsx new file mode 100644 index 00000000000000..fc0ea926143700 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.test.tsx @@ -0,0 +1,78 @@ +/* + * 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 { mount, shallow } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { EuiIcon } from '@elastic/eui'; +import { DragDropBuckets, DraggableBucketContainer } from '../shared_components'; + +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + EuiDragDropContext: 'eui-drag-drop-context', + EuiDroppable: 'eui-droppable', + EuiDraggable: (props: any) => props.children(), // eslint-disable-line @typescript-eslint/no-explicit-any + }; +}); + +describe('buckets shared components', () => { + describe('DragDropBuckets', () => { + it('should call onDragEnd when dragging ended with reordered items', () => { + const items = [
first
,
second
,
third
]; + const defaultProps = { + items, + onDragStart: jest.fn(), + onDragEnd: jest.fn(), + droppableId: 'TEST_ID', + children: items, + }; + const instance = shallow(); + act(() => { + // simulate dragging ending + instance.props().onDragEnd({ source: { index: 0 }, destination: { index: 1 } }); + }); + + expect(defaultProps.onDragEnd).toHaveBeenCalledWith([ +
second
, +
first
, +
third
, + ]); + }); + }); + describe('DraggableBucketContainer', () => { + const defaultProps = { + isInvalid: false, + invalidMessage: 'invalid', + onRemoveClick: jest.fn(), + removeTitle: 'remove', + children:
popover
, + id: '0', + idx: 0, + }; + it('should render valid component', () => { + const instance = mount(); + const popover = instance.find('[data-test-subj="popover"]'); + expect(popover).toHaveLength(1); + }); + it('should render invalid component', () => { + const instance = mount(); + const iconProps = instance.find(EuiIcon).first().props(); + expect(iconProps.color).toEqual('danger'); + expect(iconProps.type).toEqual('alert'); + expect(iconProps.title).toEqual('invalid'); + }); + it('should call onRemoveClick when remove icon is clicked', () => { + const instance = mount(); + const removeIcon = instance + .find('[data-test-subj="lns-customBucketContainer-remove"]') + .first(); + removeIcon.simulate('click'); + expect(defaultProps.onRemoveClick).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx new file mode 100644 index 00000000000000..62b5f64fb26f29 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx @@ -0,0 +1,134 @@ +/* + * 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 { + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiButtonIcon, + EuiIcon, + EuiDragDropContext, + euiDragDropReorder, + EuiDraggable, + EuiDroppable, + EuiButtonEmpty, +} from '@elastic/eui'; + +export const NewBucketButton = ({ label, onClick }: { label: string; onClick: () => void }) => ( + + {label} + +); + +interface BucketContainerProps { + isInvalid?: boolean; + invalidMessage: string; + onRemoveClick: () => void; + removeTitle: string; + children: React.ReactNode; + dataTestSubj?: string; +} + +const BucketContainer = ({ + isInvalid, + invalidMessage, + onRemoveClick, + removeTitle, + children, + dataTestSubj, +}: BucketContainerProps) => { + return ( + + + {/* Empty for spacing */} + + + + {children} + + + + + + ); +}; + +export const DraggableBucketContainer = ({ + id, + idx, + children, + ...bucketContainerProps +}: { + id: string; + idx: number; + children: React.ReactNode; +} & BucketContainerProps) => { + return ( + + {(provided) => {children}} + + ); +}; + +interface DraggableLocation { + droppableId: string; + index: number; +} + +export const DragDropBuckets = ({ + items, + onDragStart, + onDragEnd, + droppableId, + children, +}: { + items: any; // eslint-disable-line @typescript-eslint/no-explicit-any + onDragStart: () => void; + onDragEnd: (items: any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any + droppableId: string; + children: React.ReactElement[]; +}) => { + const handleDragEnd = ({ + source, + destination, + }: { + source?: DraggableLocation; + destination?: DraggableLocation; + }) => { + if (source && destination) { + const newItems = euiDragDropReorder(items, source.index, destination.index); + onDragEnd(newItems); + } + }; + return ( + + + {children} + + + ); +}; diff --git a/x-pack/plugins/lens/public/toolbar_button/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/index.tsx similarity index 77% rename from x-pack/plugins/lens/public/toolbar_button/index.tsx rename to x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/index.tsx index ee6489726a0a71..a5cac121969593 100644 --- a/x-pack/plugins/lens/public/toolbar_button/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/index.tsx @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ToolbarButtonProps, ToolbarButton } from './toolbar_button'; +export * from './label_input'; +export * from './buckets'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/label_input.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/label_input.tsx new file mode 100644 index 00000000000000..882169c0675b05 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/label_input.tsx @@ -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 React, { useState, useEffect } from 'react'; +import { useDebounce } from 'react-use'; +import { EuiFieldText, keys } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export const LabelInput = ({ + value, + onChange, + placeholder, + inputRef, + onSubmit, + dataTestSubj, +}: { + value: string; + onChange: (value: string) => void; + placeholder?: string; + inputRef?: React.MutableRefObject; + onSubmit?: () => void; + dataTestSubj?: string; +}) => { + const [inputValue, setInputValue] = useState(value); + + useEffect(() => { + setInputValue(value); + }, [value, setInputValue]); + + useDebounce(() => onChange(inputValue), 256, [inputValue]); + + const handleInputChange = (e: React.ChangeEvent) => { + const val = String(e.target.value); + setInputValue(val); + }; + + return ( + { + if (inputRef && node) { + inputRef.current = node; + } + }} + onKeyDown={({ key }: React.KeyboardEvent) => { + if (keys.ENTER === key && onSubmit) { + onSubmit(); + } + }} + prepend={i18n.translate('xpack.lens.labelInput.label', { + defaultMessage: 'Label', + })} + /> + ); +}; diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx index 9ee37fdf53a921..365e430a460fad 100644 --- a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx @@ -10,22 +10,16 @@ import { useDebounce } from 'react-use'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, - EuiFlexItem, - EuiPopover, - EuiSelect, EuiFormRow, EuiSuperSelect, EuiRange, - EuiSwitch, EuiHorizontalRule, - EuiSpacer, - EuiButtonGroup, } from '@elastic/eui'; import { Position } from '@elastic/charts'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; import { PieVisualizationState, SharedLayerState } from './types'; import { VisualizationToolbarProps } from '../types'; -import { ToolbarButton } from '../toolbar_button'; +import { ToolbarPopover, LegendSettingsPopover } from '../shared_components'; const numberOptions: Array<{ value: SharedLayerState['numberDisplay']; inputDisplay: string }> = [ { @@ -99,179 +93,128 @@ const legendOptions: Array<{ id: 'pieLegendDisplay-default', value: 'default', label: i18n.translate('xpack.lens.pieChart.legendVisibility.auto', { - defaultMessage: 'auto', + defaultMessage: 'Auto', }), }, { id: 'pieLegendDisplay-show', value: 'show', label: i18n.translate('xpack.lens.pieChart.legendVisibility.show', { - defaultMessage: 'show', + defaultMessage: 'Show', }), }, { id: 'pieLegendDisplay-hide', value: 'hide', label: i18n.translate('xpack.lens.pieChart.legendVisibility.hide', { - defaultMessage: 'hide', + defaultMessage: 'Hide', }), }, ]; export function PieToolbar(props: VisualizationToolbarProps) { - const [open, setOpen] = useState(false); const { state, setState } = props; const layer = state.layers[0]; if (!layer) { return null; } return ( - - - { - setOpen(!open); - }} - > - {i18n.translate('xpack.lens.pieChart.settingsLabel', { defaultMessage: 'Settings' })} - - } - isOpen={open} - closePopover={() => { - setOpen(false); - }} - anchorPosition="downRight" + + + - - { - setState({ - ...state, - layers: [{ ...layer, categoryDisplay: option }], - }); - }} - /> - - - { - setState({ - ...state, - layers: [{ ...layer, numberDisplay: option }], - }); - }} - /> - - - - - setState({ - ...state, - layers: [{ ...layer, percentDecimals: value }], - }) - } - /> - - - -
- value === layer.legendDisplay)!.id} - onChange={(optionId) => { - setState({ - ...state, - layers: [ - { - ...layer, - legendDisplay: legendOptions.find(({ id }) => id === optionId)!.value, - }, - ], - }); - }} - buttonSize="compressed" - isFullWidth - /> - - - { - setState({ ...state, layers: [{ ...layer, nestedLegend: !layer.nestedLegend }] }); - }} - /> -
-
- - { - setState({ - ...state, - layers: [{ ...layer, legendPosition: e.target.value as Position }], - }); - }} - /> - -
-
+ { + setState({ + ...state, + layers: [{ ...layer, categoryDisplay: option }], + }); + }} + /> + + + { + setState({ + ...state, + layers: [{ ...layer, numberDisplay: option }], + }); + }} + /> + + + + + setState({ + ...state, + layers: [{ ...layer, percentDecimals: value }], + }) + } + /> + + + { + setState({ + ...state, + layers: [ + { + ...layer, + legendDisplay: legendOptions.find(({ id }) => id === optionId)!.value, + }, + ], + }); + }} + position={layer.legendPosition} + onPositionChange={(id) => { + setState({ + ...state, + layers: [{ ...layer, legendPosition: id as Position }], + }); + }} + renderNestedLegendSwitch + nestedLegend={!!layer.nestedLegend} + onNestedLegendChange={() => { + setState({ + ...state, + layers: [{ ...layer, nestedLegend: !layer.nestedLegend }], + }); + }} + />
); } diff --git a/x-pack/plugins/lens/public/shared_components/index.ts b/x-pack/plugins/lens/public/shared_components/index.ts index ad662fd7a59d92..c0362a5660adb3 100644 --- a/x-pack/plugins/lens/public/shared_components/index.ts +++ b/x-pack/plugins/lens/public/shared_components/index.ts @@ -5,3 +5,6 @@ */ export * from './empty_placeholder'; +export { ToolbarPopoverProps, ToolbarPopover } from './toolbar_popover'; +export { ToolbarButtonProps, ToolbarButton } from './toolbar_button'; +export { LegendSettingsPopover } from './legend_settings_popover'; diff --git a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx new file mode 100644 index 00000000000000..1e0e6b33b6cd4a --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx @@ -0,0 +1,106 @@ +/* + * 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 { Position } from '@elastic/charts'; +import { shallowWithIntl as shallow } from 'test_utils/enzyme_helpers'; +import { LegendSettingsPopover, LegendSettingsPopoverProps } from './legend_settings_popover'; + +describe('Legend Settings', () => { + const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: string }> = [ + { + id: `test_legend_auto`, + value: 'auto', + label: 'Auto', + }, + { + id: `test_legend_show`, + value: 'show', + label: 'Show', + }, + { + id: `test_legend_hide`, + value: 'hide', + label: 'Hide', + }, + ]; + let props: LegendSettingsPopoverProps; + beforeEach(() => { + props = { + legendOptions, + mode: 'auto', + onDisplayChange: jest.fn(), + onPositionChange: jest.fn(), + }; + }); + + it('should have selected the given mode as Display value', () => { + const component = shallow(); + expect(component.find('[data-test-subj="lens-legend-display-btn"]').prop('idSelected')).toEqual( + 'test_legend_auto' + ); + }); + + it('should have called the onDisplayChange function on ButtonGroup change', () => { + const component = shallow(); + component.find('[data-test-subj="lens-legend-display-btn"]').simulate('change'); + expect(props.onDisplayChange).toHaveBeenCalled(); + }); + + it('should have default the Position to right when no position is given', () => { + const component = shallow(); + expect( + component.find('[data-test-subj="lens-legend-position-btn"]').prop('idSelected') + ).toEqual(Position.Right); + }); + + it('should have called the onPositionChange function on ButtonGroup change', () => { + const component = shallow(); + component.find('[data-test-subj="lens-legend-position-btn"]').simulate('change'); + expect(props.onPositionChange).toHaveBeenCalled(); + }); + + it('should disable the position button group on hide mode', () => { + const component = shallow(); + expect( + component.find('[data-test-subj="lens-legend-position-btn"]').prop('isDisabled') + ).toEqual(true); + }); + + it('should enable the Nested Legend Switch when renderNestedLegendSwitch prop is true', () => { + const component = shallow(); + expect(component.find('[data-test-subj="lens-legend-nested-switch"]')).toHaveLength(1); + }); + + it('should set the switch state on nestedLegend prop value', () => { + const component = shallow( + + ); + expect(component.find('[data-test-subj="lens-legend-nested-switch"]').prop('checked')).toEqual( + true + ); + }); + + it('should have called the onNestedLegendChange function on switch change', () => { + const nestedProps = { + ...props, + renderNestedLegendSwitch: true, + onNestedLegendChange: jest.fn(), + }; + const component = shallow(); + component.find('[data-test-subj="lens-legend-nested-switch"]').simulate('change'); + expect(nestedProps.onNestedLegendChange).toHaveBeenCalled(); + }); + + it('should disable switch group on hide mode', () => { + const component = shallow( + + ); + expect(component.find('[data-test-subj="lens-legend-nested-switch"]').prop('disabled')).toEqual( + true + ); + }); +}); diff --git a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx new file mode 100644 index 00000000000000..b3df4814b85f8d --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx @@ -0,0 +1,158 @@ +/* + * 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 { EuiFormRow, EuiButtonGroup, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; +import { Position } from '@elastic/charts'; +import { ToolbarPopover } from '../shared_components'; + +export interface LegendSettingsPopoverProps { + /** + * Determines the legend display options + */ + legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide' | 'default'; label: string }>; + /** + * Determines the legend mode + */ + mode: 'default' | 'show' | 'hide' | 'auto'; + /** + * Callback on display option change + */ + onDisplayChange: (id: string) => void; + /** + * Sets the legend position + */ + position?: Position; + /** + * Callback on position option change + */ + onPositionChange: (id: string) => void; + /** + * If true, nested legend switch is rendered + */ + renderNestedLegendSwitch?: boolean; + /** + * nested legend switch status + */ + nestedLegend?: boolean; + /** + * Callback on nested switch status change + */ + onNestedLegendChange?: (event: EuiSwitchEvent) => void; +} + +const toggleButtonsIcons = [ + { + id: Position.Bottom, + label: i18n.translate('xpack.lens.shared.legendPositionBottom', { + defaultMessage: 'Bottom', + }), + iconType: 'arrowDown', + }, + { + id: Position.Left, + label: i18n.translate('xpack.lens.shared.legendPositionLeft', { + defaultMessage: 'Left', + }), + iconType: 'arrowLeft', + }, + { + id: Position.Right, + label: i18n.translate('xpack.lens.shared.legendPositionRight', { + defaultMessage: 'Right', + }), + iconType: 'arrowRight', + }, + { + id: Position.Top, + label: i18n.translate('xpack.lens.shared.legendPositionTop', { + defaultMessage: 'Top', + }), + iconType: 'arrowUp', + }, +]; + +export const LegendSettingsPopover: React.FunctionComponent = ({ + legendOptions, + mode, + onDisplayChange, + position, + onPositionChange, + renderNestedLegendSwitch, + nestedLegend, + onNestedLegendChange = () => {}, +}) => { + return ( + + + value === mode)!.id} + onChange={onDisplayChange} + /> + + + + + {renderNestedLegendSwitch && ( + + + + )} + + ); +}; diff --git a/x-pack/plugins/lens/public/shared_components/toolbar_button.scss b/x-pack/plugins/lens/public/shared_components/toolbar_button.scss new file mode 100644 index 00000000000000..61b02f47678c3d --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/toolbar_button.scss @@ -0,0 +1,60 @@ +.lnsToolbarButton { + line-height: $euiButtonHeight; // Keeps alignment of text and chart icon + background-color: $euiColorEmptyShade; + + // Some toolbar buttons are just icons, but EuiButton comes with margin and min-width that need to be removed + min-width: 0; + + &[class*='--text'] { + // Lighten the border color for all states + border-color: $euiBorderColor !important; // sass-lint:disable-line no-important + } + + &[class*='isDisabled'] { + // There is a popover `pointer-events: none` that messes with the not-allowed cursor + pointer-events: initial; + } + + .lnsToolbarButton__text > svg { + margin-top: -1px; // Just some weird alignment issue when icon is the child not the `iconType` + } + + .lnsToolbarButton__text:empty { + margin: 0; + } + + // Toolbar buttons don't look good with centered text when fullWidth + &[class*='fullWidth'] { + text-align: left; + + .lnsToolbarButton__content { + justify-content: space-between; + } + } + +} + +.lnsToolbarButton--groupLeft { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.lnsToolbarButton--groupCenter { + border-radius: 0; + border-left: none; +} + +.lnsToolbarButton--groupRight { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: none; +} + +.lnsToolbarButton--bold { + font-weight: $euiFontWeightBold; +} + +.lnsToolbarButton--s { + box-shadow: none !important; // sass-lint:disable-line no-important + font-size: $euiFontSizeS; +} diff --git a/x-pack/plugins/lens/public/toolbar_button/toolbar_button.tsx b/x-pack/plugins/lens/public/shared_components/toolbar_button.tsx similarity index 71% rename from x-pack/plugins/lens/public/toolbar_button/toolbar_button.tsx rename to x-pack/plugins/lens/public/shared_components/toolbar_button.tsx index 0a63781818171e..56647352750a1f 100644 --- a/x-pack/plugins/lens/public/toolbar_button/toolbar_button.tsx +++ b/x-pack/plugins/lens/public/shared_components/toolbar_button.tsx @@ -9,6 +9,13 @@ import React from 'react'; import classNames from 'classnames'; import { EuiButton, PropsOf, EuiButtonProps } from '@elastic/eui'; +const groupPositionToClassMap = { + none: null, + left: 'lnsToolbarButton--groupLeft', + center: 'lnsToolbarButton--groupCenter', + right: 'lnsToolbarButton--groupRight', +}; + export type ToolbarButtonProps = PropsOf & { /** * Determines prominence @@ -18,6 +25,14 @@ export type ToolbarButtonProps = PropsOf & { * Smaller buttons also remove extra shadow for less prominence */ size?: EuiButtonProps['size']; + /** + * Determines if the button will have a down arrow or not + */ + hasArrow?: boolean; + /** + * Adjusts the borders for groupings + */ + groupPosition?: 'none' | 'left' | 'center' | 'right'; }; export const ToolbarButton: React.FunctionComponent = ({ @@ -25,10 +40,13 @@ export const ToolbarButton: React.FunctionComponent = ({ className, fontWeight = 'normal', size = 'm', + hasArrow = true, + groupPosition = 'none', ...rest }) => { const classes = classNames( 'lnsToolbarButton', + groupPositionToClassMap[groupPosition], [`lnsToolbarButton--${fontWeight}`, `lnsToolbarButton--${size}`], className ); @@ -36,7 +54,7 @@ export const ToolbarButton: React.FunctionComponent = ({ = ({ + children, + title, + type, + isDisabled = false, + groupPosition, +}) => { + const [open, setOpen] = useState(false); + + const iconType: string | IconType = typeof type === 'string' ? typeToIconMap[type] : type; + + return ( + + { + setOpen(!open); + }} + hasArrow={false} + isDisabled={isDisabled} + groupPosition={groupPosition} + > + + + } + isOpen={open} + closePopover={() => { + setOpen(false); + }} + anchorPosition="downRight" + > + {title} + {children} + + + ); +}; diff --git a/x-pack/plugins/lens/public/toolbar_button/toolbar_button.scss b/x-pack/plugins/lens/public/toolbar_button/toolbar_button.scss deleted file mode 100644 index f36fdfdf02abaa..00000000000000 --- a/x-pack/plugins/lens/public/toolbar_button/toolbar_button.scss +++ /dev/null @@ -1,30 +0,0 @@ -.lnsToolbarButton { - line-height: $euiButtonHeight; // Keeps alignment of text and chart icon - background-color: $euiColorEmptyShade; - border-color: $euiBorderColor; - - // Some toolbar buttons are just icons, but EuiButton comes with margin and min-width that need to be removed - min-width: 0; - - .lnsToolbarButton__text:empty { - margin: 0; - } - - // Toolbar buttons don't look good with centered text when fullWidth - &[class*='fullWidth'] { - text-align: left; - - .lnsToolbarButton__content { - justify-content: space-between; - } - } -} - -.lnsToolbarButton--bold { - font-weight: $euiFontWeightBold; -} - -.lnsToolbarButton--s { - box-shadow: none !important; // sass-lint:disable-line no-important - font-size: $euiFontSizeS; -} diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index 19ea75239ddb2a..dd8c6377cacdc1 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -5,6 +5,28 @@ Object { "chain": Array [ Object { "arguments": Object { + "axisTitlesVisibilitySettings": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "x": Array [ + true, + ], + "yLeft": Array [ + true, + ], + "yRight": Array [ + true, + ], + }, + "function": "lens_xy_axisTitlesVisibilityConfig", + "type": "function", + }, + ], + "type": "expression", + }, + ], "fittingFunction": Array [ "Carry", ], @@ -16,7 +38,10 @@ Object { "x": Array [ false, ], - "y": Array [ + "yLeft": Array [ + true, + ], + "yRight": Array [ true, ], }, @@ -92,12 +117,6 @@ Object { "type": "expression", }, ], - "showXAxisTitle": Array [ - true, - ], - "showYAxisTitle": Array [ - true, - ], "tickLabelsVisibilitySettings": Array [ Object { "chain": Array [ @@ -106,7 +125,10 @@ Object { "x": Array [ false, ], - "y": Array [ + "yLeft": Array [ + true, + ], + "yRight": Array [ true, ], }, @@ -120,6 +142,9 @@ Object { "xTitle": Array [ "", ], + "yRightTitle": Array [ + "", + ], "yTitle": Array [ "", ], diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts index 7b0edf2b367bed..15c08d17e49c65 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts @@ -203,7 +203,7 @@ describe('axes_configuration', () => { it('should map auto series to left axis', () => { const formatFactory = jest.fn(); - const groups = getAxesConfiguration([sampleLayer], tables, formatFactory, false); + const groups = getAxesConfiguration([sampleLayer], false, tables, formatFactory); expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('left'); expect(groups[0].series[0].accessor).toEqual('yAccessorId'); @@ -213,7 +213,7 @@ describe('axes_configuration', () => { it('should map auto series to right axis if formatters do not match', () => { const formatFactory = jest.fn(); const twoSeriesLayer = { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2'] }; - const groups = getAxesConfiguration([twoSeriesLayer], tables, formatFactory, false); + const groups = getAxesConfiguration([twoSeriesLayer], false, tables, formatFactory); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); expect(groups[1].position).toEqual('right'); @@ -227,7 +227,7 @@ describe('axes_configuration', () => { ...sampleLayer, accessors: ['yAccessorId', 'yAccessorId2', 'yAccessorId3'], }; - const groups = getAxesConfiguration([threeSeriesLayer], tables, formatFactory, false); + const groups = getAxesConfiguration([threeSeriesLayer], false, tables, formatFactory); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); expect(groups[1].position).toEqual('right'); @@ -240,9 +240,9 @@ describe('axes_configuration', () => { const formatFactory = jest.fn(); const groups = getAxesConfiguration( [{ ...sampleLayer, yConfig: [{ forAccessor: 'yAccessorId', axisMode: 'right' }] }], + false, tables, - formatFactory, - false + formatFactory ); expect(groups.length).toEqual(1); expect(groups[0].position).toEqual('right'); @@ -260,9 +260,9 @@ describe('axes_configuration', () => { yConfig: [{ forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], + false, tables, - formatFactory, - false + formatFactory ); expect(groups.length).toEqual(2); expect(groups[0].position).toEqual('left'); @@ -284,9 +284,9 @@ describe('axes_configuration', () => { yConfig: [{ forAccessor: 'yAccessorId', axisMode: 'right' }], }, ], + false, tables, - formatFactory, - false + formatFactory ); expect(formatFactory).toHaveBeenCalledTimes(2); expect(formatFactory).toHaveBeenCalledWith({ id: 'number' }); diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index 006995d92a9268..876baaabb57c5a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -20,7 +20,7 @@ interface FormattedMetric { type GroupsConfiguration = Array<{ groupId: string; position: 'left' | 'right' | 'bottom' | 'top'; - formatter: IFieldFormat; + formatter?: IFieldFormat; series: Array<{ layer: string; accessor: string }>; }>; @@ -33,9 +33,9 @@ export function isFormatterCompatible( export function getAxesConfiguration( layers: LayerConfig[], - tables: Record, - formatFactory: (mapping: SerializedFieldFormat) => IFieldFormat, - shouldRotate: boolean + shouldRotate: boolean, + tables?: Record, + formatFactory?: (mapping: SerializedFieldFormat) => IFieldFormat ): GroupsConfiguration { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; right: FormattedMetric[] } = { auto: [], @@ -43,13 +43,13 @@ export function getAxesConfiguration( right: [], }; - layers.forEach((layer) => { - const table = tables[layer.layerId]; + layers?.forEach((layer) => { + const table = tables?.[layer.layerId]; layer.accessors.forEach((accessor) => { const mode = layer.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode || 'auto'; - let formatter: SerializedFieldFormat = table.columns.find((column) => column.id === accessor) + let formatter: SerializedFieldFormat = table?.columns.find((column) => column.id === accessor) ?.formatHint || { id: 'number' }; if (layer.seriesType.includes('percentage') && formatter.id !== 'percent') { formatter = { @@ -70,16 +70,18 @@ export function getAxesConfiguration( series.auto.forEach((currentSeries) => { if ( series.left.length === 0 || - series.left.every((leftSeries) => - isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) - ) + (tables && + series.left.every((leftSeries) => + isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) + )) ) { series.left.push(currentSeries); } else if ( series.right.length === 0 || - series.right.every((rightSeries) => - isFormatterCompatible(rightSeries.fieldFormat, currentSeries.fieldFormat) - ) + (tables && + series.left.every((leftSeries) => + isFormatterCompatible(leftSeries.fieldFormat, currentSeries.fieldFormat) + )) ) { series.right.push(currentSeries); } else if (series.right.length >= series.left.length) { @@ -95,7 +97,7 @@ export function getAxesConfiguration( axisGroups.push({ groupId: 'left', position: shouldRotate ? 'bottom' : 'left', - formatter: formatFactory(series.left[0].fieldFormat), + formatter: formatFactory?.(series.left[0].fieldFormat), series: series.left.map(({ fieldFormat, ...currentSeries }) => currentSeries), }); } @@ -104,7 +106,7 @@ export function getAxesConfiguration( axisGroups.push({ groupId: 'right', position: shouldRotate ? 'top' : 'right', - formatter: formatFactory(series.right[0].fieldFormat), + formatter: formatFactory?.(series.right[0].fieldFormat), series: series.right.map(({ fieldFormat, ...currentSeries }) => currentSeries), }); } diff --git a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.test.tsx b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.test.tsx new file mode 100644 index 00000000000000..9e71323377c1ad --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.test.tsx @@ -0,0 +1,81 @@ +/* + * 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 { shallowWithIntl as shallow } from 'test_utils/enzyme_helpers'; +import { AxisSettingsPopover, AxisSettingsPopoverProps } from './axis_settings_popover'; +import { ToolbarPopover } from '../shared_components'; + +describe('Axes Settings', () => { + let props: AxisSettingsPopoverProps; + beforeEach(() => { + props = { + layers: [ + { + seriesType: 'bar', + layerId: 'first', + splitAccessor: 'baz', + xAccessor: 'foo', + accessors: ['bar'], + }, + ], + updateTitleState: jest.fn(), + axisTitle: 'My custom X axis title', + axis: 'x', + areTickLabelsVisible: true, + areGridlinesVisible: true, + isAxisTitleVisible: true, + toggleAxisTitleVisibility: jest.fn(), + toggleTickLabelsVisibility: jest.fn(), + toggleGridlinesVisibility: jest.fn(), + }; + }); + + it('should disable the popover if the isDisabled property is true', () => { + const component = shallow(); + expect(component.find(ToolbarPopover).prop('isDisabled')).toEqual(true); + }); + + it('should show the axes title on the corresponding input text', () => { + const component = shallow(); + expect(component.find('[data-test-subj="lnsxAxisTitle"]').prop('value')).toBe( + 'My custom X axis title' + ); + }); + + it('should disable the input text if the switch is off', () => { + const component = shallow(); + expect(component.find('[data-test-subj="lnsxAxisTitle"]').prop('disabled')).toBe(true); + }); + + it('has the tickLabels switch on by default', () => { + const component = shallow(); + expect(component.find('[data-test-subj="lnsshowxAxisTickLabels"]').prop('checked')).toBe(true); + }); + + it('has the tickLabels switch off when tickLabelsVisibilitySettings for this axes are false', () => { + const component = shallow( + + ); + expect(component.find('[data-test-subj="lnsshowyLeftAxisTickLabels"]').prop('checked')).toBe( + false + ); + }); + + it('has the gridlines switch on by default', () => { + const component = shallow(); + expect(component.find('[data-test-subj="lnsshowxAxisGridlines"]').prop('checked')).toBe(true); + }); + + it('has the gridlines switch off when gridlinesVisibilitySettings for this axes are false', () => { + const component = shallow( + + ); + expect(component.find('[data-test-subj="lnsshowyRightAxisGridlines"]').prop('checked')).toBe( + false + ); + }); +}); diff --git a/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx new file mode 100644 index 00000000000000..835f3e2cde7692 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/axis_settings_popover.tsx @@ -0,0 +1,205 @@ +/* + * 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, { useState } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiSwitch, + EuiSpacer, + EuiFieldText, + IconType, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { LayerConfig, AxesSettingsConfig } from './types'; +import { ToolbarPopover, ToolbarButtonProps } from '../shared_components'; +import { isHorizontalChart } from './state_helpers'; +import { EuiIconAxisBottom } from '../assets/axis_bottom'; +import { EuiIconAxisLeft } from '../assets/axis_left'; +import { EuiIconAxisRight } from '../assets/axis_right'; +import { EuiIconAxisTop } from '../assets/axis_top'; + +type AxesSettingsConfigKeys = keyof AxesSettingsConfig; +export interface AxisSettingsPopoverProps { + /** + * Determines the axis + */ + axis: AxesSettingsConfigKeys; + /** + * Contains the chart layers + */ + layers?: LayerConfig[]; + /** + * Determines the axis title + */ + axisTitle: string | undefined; + /** + * Callback to axis title change + */ + updateTitleState: (value: string) => void; + /** + * Determines if the popover is Disabled + */ + isDisabled?: boolean; + /** + * Determines if the ticklabels of the axis are visible + */ + areTickLabelsVisible: boolean; + /** + * Toggles the axis tickLabels visibility + */ + toggleTickLabelsVisibility: (axis: AxesSettingsConfigKeys) => void; + /** + * Determines if the gridlines of the axis are visible + */ + areGridlinesVisible: boolean; + /** + * Toggles the gridlines visibility + */ + toggleGridlinesVisibility: (axis: AxesSettingsConfigKeys) => void; + /** + * Determines if the title visibility switch is on and the input text is disabled + */ + isAxisTitleVisible: boolean; + /** + * Toggles the axis title visibility + */ + toggleAxisTitleVisibility: (axis: AxesSettingsConfigKeys, checked: boolean) => void; +} +const popoverConfig = ( + axis: AxesSettingsConfigKeys, + isHorizontal: boolean +): { icon: IconType; groupPosition: ToolbarButtonProps['groupPosition']; popoverTitle: string } => { + switch (axis) { + case 'yLeft': + return { + icon: (isHorizontal ? EuiIconAxisBottom : EuiIconAxisLeft) as IconType, + groupPosition: 'left', + popoverTitle: isHorizontal + ? i18n.translate('xpack.lens.xyChart.bottomAxisLabel', { + defaultMessage: 'Bottom axis', + }) + : i18n.translate('xpack.lens.xyChart.leftAxisLabel', { + defaultMessage: 'Left axis', + }), + }; + case 'yRight': + return { + icon: (isHorizontal ? EuiIconAxisTop : EuiIconAxisRight) as IconType, + groupPosition: 'right', + popoverTitle: isHorizontal + ? i18n.translate('xpack.lens.xyChart.topAxisLabel', { + defaultMessage: 'Top axis', + }) + : i18n.translate('xpack.lens.xyChart.rightAxisLabel', { + defaultMessage: 'Right axis', + }), + }; + case 'x': + default: + return { + icon: (isHorizontal ? EuiIconAxisLeft : EuiIconAxisBottom) as IconType, + groupPosition: 'center', + popoverTitle: isHorizontal + ? i18n.translate('xpack.lens.xyChart.leftAxisLabel', { + defaultMessage: 'Left axis', + }) + : i18n.translate('xpack.lens.xyChart.bottomAxisLabel', { + defaultMessage: 'Bottom axis', + }), + }; + } +}; + +export const AxisSettingsPopover: React.FunctionComponent = ({ + layers, + axis, + axisTitle, + updateTitleState, + toggleTickLabelsVisibility, + toggleGridlinesVisibility, + isDisabled, + areTickLabelsVisible, + areGridlinesVisible, + isAxisTitleVisible, + toggleAxisTitleVisibility, +}) => { + const [title, setTitle] = useState(axisTitle); + + const isHorizontal = layers?.length ? isHorizontalChart(layers) : false; + const config = popoverConfig(axis, isHorizontal); + + const onTitleChange = (value: string): void => { + setTitle(value); + updateTitleState(value); + }; + return ( + + + + +

+ {i18n.translate('xpack.lens.xyChart.axisNameLabel', { + defaultMessage: 'Axis name', + })} +

+
+
+ + toggleAxisTitleVisibility(axis, target.checked)} + checked={isAxisTitleVisible} + /> + +
+ + onTitleChange(target.value)} + aria-label={i18n.translate('xpack.lens.xyChart.overwriteAxisTitle', { + defaultMessage: 'Overwrite axis title', + })} + /> + + toggleTickLabelsVisibility(axis)} + checked={areTickLabelsVisible} + /> + + toggleGridlinesVisibility(axis)} + checked={areGridlinesVisible} + /> +
+ ); +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index fddcad7989b258..470d197e847eb3 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -10,7 +10,14 @@ import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public' import { UI_SETTINGS } from '../../../../../src/plugins/data/public'; import { xyVisualization } from './xy_visualization'; import { xyChart, getXyChartRenderer } from './xy_expression'; -import { legendConfig, layerConfig, yAxisConfig, tickLabelsConfig, gridlinesConfig } from './types'; +import { + legendConfig, + layerConfig, + yAxisConfig, + tickLabelsConfig, + gridlinesConfig, + axisTitlesVisibilityConfig, +} from './types'; import { EditorFrameSetup, FormatFactory } from '../types'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; @@ -41,6 +48,7 @@ export class XyVisualization { expressions.registerFunction(() => yAxisConfig); expressions.registerFunction(() => tickLabelsConfig); expressions.registerFunction(() => gridlinesConfig); + expressions.registerFunction(() => axisTitlesVisibilityConfig); expressions.registerFunction(() => layerConfig); expressions.registerFunction(() => xyChart); diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts index 825281d6d88c25..d09ba01b32c66a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.test.ts @@ -41,8 +41,8 @@ describe('#toExpression', () => { legend: { position: Position.Bottom, isVisible: true }, preferredSeriesType: 'bar', fittingFunction: 'Carry', - tickLabelsVisibilitySettings: { x: false, y: true }, - gridlinesVisibilitySettings: { x: false, y: true }, + tickLabelsVisibilitySettings: { x: false, yLeft: true, yRight: true }, + gridlinesVisibilitySettings: { x: false, yLeft: true, yRight: true }, layers: [ { layerId: 'first', @@ -79,7 +79,7 @@ describe('#toExpression', () => { ).toEqual('None'); }); - it('should default the showXAxisTitle and showYAxisTitle to true', () => { + it('should default the axisTitles visibility settings to true', () => { const expression = xyVisualization.toExpression( { legend: { position: Position.Bottom, isVisible: true }, @@ -96,8 +96,13 @@ describe('#toExpression', () => { }, frame.datasourceLayers ) as Ast; - expect(expression.chain[0].arguments.showXAxisTitle[0]).toBe(true); - expect(expression.chain[0].arguments.showYAxisTitle[0]).toBe(true); + expect( + (expression.chain[0].arguments.axisTitlesVisibilitySettings[0] as Ast).chain[0].arguments + ).toEqual({ + x: [true], + yLeft: [true], + yRight: [true], + }); }); it('should generate an expression without x accessor', () => { @@ -166,6 +171,7 @@ describe('#toExpression', () => { expect(mockDatasource.publicAPIMock.getOperationForColumnId).toHaveBeenCalledWith('d'); expect(expression.chain[0].arguments.xTitle).toEqual(['']); expect(expression.chain[0].arguments.yTitle).toEqual(['']); + expect(expression.chain[0].arguments.yRightTitle).toEqual(['']); expect( (expression.chain[0].arguments.layers[0] as Ast).chain[0].arguments.columnToLabel ).toEqual([ @@ -198,7 +204,8 @@ describe('#toExpression', () => { (expression.chain[0].arguments.tickLabelsVisibilitySettings[0] as Ast).chain[0].arguments ).toEqual({ x: [true], - y: [true], + yLeft: [true], + yRight: [true], }); }); @@ -223,7 +230,8 @@ describe('#toExpression', () => { (expression.chain[0].arguments.gridlinesVisibilitySettings[0] as Ast).chain[0].arguments ).toEqual({ x: [true], - y: [true], + yLeft: [true], + yRight: [true], }); }); }); diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index f64624776186df..df8d571a1fdf85 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -99,6 +99,7 @@ export const buildExpression = ( arguments: { xTitle: [state.xTitle || ''], yTitle: [state.yTitle || ''], + yRightTitle: [state.yRightTitle || ''], legend: [ { type: 'expression', @@ -118,8 +119,22 @@ export const buildExpression = ( }, ], fittingFunction: [state.fittingFunction || 'None'], - showXAxisTitle: [state.showXAxisTitle ?? true], - showYAxisTitle: [state.showYAxisTitle ?? true], + axisTitlesVisibilitySettings: [ + { + type: 'expression', + chain: [ + { + type: 'function', + function: 'lens_xy_axisTitlesVisibilityConfig', + arguments: { + x: [state?.axisTitlesVisibilitySettings?.x ?? true], + yLeft: [state?.axisTitlesVisibilitySettings?.yLeft ?? true], + yRight: [state?.axisTitlesVisibilitySettings?.yRight ?? true], + }, + }, + ], + }, + ], tickLabelsVisibilitySettings: [ { type: 'expression', @@ -129,7 +144,8 @@ export const buildExpression = ( function: 'lens_xy_tickLabelsConfig', arguments: { x: [state?.tickLabelsVisibilitySettings?.x ?? true], - y: [state?.tickLabelsVisibilitySettings?.y ?? true], + yLeft: [state?.tickLabelsVisibilitySettings?.yLeft ?? true], + yRight: [state?.tickLabelsVisibilitySettings?.yRight ?? true], }, }, ], @@ -144,7 +160,8 @@ export const buildExpression = ( function: 'lens_xy_gridlinesConfig', arguments: { x: [state?.gridlinesVisibilitySettings?.x ?? true], - y: [state?.gridlinesVisibilitySettings?.y ?? true], + yLeft: [state?.gridlinesVisibilitySettings?.yLeft ?? true], + yRight: [state?.gridlinesVisibilitySettings?.yRight ?? true], }, }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/tooltip_wrapper.tsx b/x-pack/plugins/lens/public/xy_visualization/tooltip_wrapper.tsx new file mode 100644 index 00000000000000..cdbec3fd5d6ae8 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/tooltip_wrapper.tsx @@ -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 React from 'react'; +import { EuiToolTip } from '@elastic/eui'; + +export interface TooltipWrapperProps { + tooltipContent: string; + condition: boolean; +} + +export const TooltipWrapper: React.FunctionComponent = ({ + children, + condition, + tooltipContent, +}) => { + return ( + <> + {condition ? ( + + <>{children} + + ) : ( + children + )} + + ); +}; diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 8438b1f27dd0db..185fa20f169eec 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -80,7 +80,8 @@ export const legendConfig: ExpressionFunctionDefinition< export interface AxesSettingsConfig { x: boolean; - y: boolean; + yLeft: boolean; + yRight: boolean; } type TickLabelsConfigResult = AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' }; @@ -103,10 +104,16 @@ export const tickLabelsConfig: ExpressionFunctionDefinition< defaultMessage: 'Specifies whether or not the tick labels of the x-axis are visible.', }), }, - y: { + yLeft: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yAxisTickLabels.help', { - defaultMessage: 'Specifies whether or not the tick labels of the y-axis are visible.', + help: i18n.translate('xpack.lens.xyChart.yLeftAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisTickLabels.help', { + defaultMessage: 'Specifies whether or not the tick labels of the right y-axis are visible.', }), }, }, @@ -138,10 +145,16 @@ export const gridlinesConfig: ExpressionFunctionDefinition< defaultMessage: 'Specifies whether or not the gridlines of the x-axis are visible.', }), }, - y: { + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisgridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.', + }), + }, + yRight: { types: ['boolean'], - help: i18n.translate('xpack.lens.xyChart.yAxisgridlines.help', { - defaultMessage: 'Specifies whether or not the gridlines of the y-axis are visible.', + help: i18n.translate('xpack.lens.xyChart.yRightAxisgridlines.help', { + defaultMessage: 'Specifies whether or not the gridlines of the right y-axis are visible.', }), }, }, @@ -153,6 +166,49 @@ export const gridlinesConfig: ExpressionFunctionDefinition< }, }; +type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { + type: 'lens_xy_axisTitlesVisibilityConfig'; +}; + +export const axisTitlesVisibilityConfig: ExpressionFunctionDefinition< + 'lens_xy_axisTitlesVisibilityConfig', + null, + AxesSettingsConfig, + AxisTitlesVisibilityConfigResult +> = { + name: 'lens_xy_axisTitlesVisibilityConfig', + aliases: [], + type: 'lens_xy_axisTitlesVisibilityConfig', + help: `Configure the xy chart's axis titles appearance`, + inputTypes: ['null'], + args: { + x: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.xAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the x-axis are visible.', + }), + }, + yLeft: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yLeftAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.', + }), + }, + yRight: { + types: ['boolean'], + help: i18n.translate('xpack.lens.xyChart.yRightAxisTitle.help', { + defaultMessage: 'Specifies whether or not the title of the right y-axis are visible.', + }), + }, + }, + fn: function fn(input: unknown, args: AxesSettingsConfig) { + return { + type: 'lens_xy_axisTitlesVisibilityConfig', + ...args, + }; + }, +}; + interface AxisConfig { title: string; hide?: boolean; @@ -329,11 +385,13 @@ export type LayerArgs = LayerConfig & { export interface XYArgs { xTitle: string; yTitle: string; + yRightTitle: string; legend: LegendConfig & { type: 'lens_xy_legendConfig' }; layers: LayerArgs[]; fittingFunction?: FittingFunction; - showXAxisTitle?: boolean; - showYAxisTitle?: boolean; + axisTitlesVisibilitySettings?: AxesSettingsConfig & { + type: 'lens_xy_axisTitlesVisibilityConfig'; + }; tickLabelsVisibilitySettings?: AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' }; gridlinesVisibilitySettings?: AxesSettingsConfig & { type: 'lens_xy_gridlinesConfig' }; } @@ -346,8 +404,8 @@ export interface XYState { layers: LayerConfig[]; xTitle?: string; yTitle?: string; - showXAxisTitle?: boolean; - showYAxisTitle?: boolean; + yRightTitle?: string; + axisTitlesVisibilitySettings?: AxesSettingsConfig; tickLabelsVisibilitySettings?: AxesSettingsConfig; gridlinesVisibilitySettings?: AxesSettingsConfig; } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss index c353f3f370ee56..5b14fca78e65d8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss @@ -1,3 +1,3 @@ .lnsXyToolbar__popover { - width: 400px; -} + width: 320px; +} \ No newline at end of file diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx index 89a2574026ced9..7e2e8f04535885 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.test.tsx @@ -8,6 +8,8 @@ import React from 'react'; import { mountWithIntl as mount, shallowWithIntl as shallow } from 'test_utils/enzyme_helpers'; import { EuiButtonGroupProps, EuiSuperSelect, EuiButtonGroup } from '@elastic/eui'; import { LayerContextMenu, XyToolbar } from './xy_config_panel'; +import { ToolbarPopover } from '../shared_components'; +import { AxisSettingsPopover } from './axis_settings_popover'; import { FramePublicAPI } from '../types'; import { State } from './types'; import { Position } from '@elastic/charts'; @@ -113,7 +115,7 @@ describe('XY Config panels', () => { expect(component.find(EuiSuperSelect).prop('valueOfSelected')).toEqual('Carry'); }); - it('should disable the select if there is no area or line series', () => { + it('should disable the popover if there is no area or line series', () => { const state = testState(); const component = shallow( { /> ); - expect(component.find(EuiSuperSelect).prop('disabled')).toEqual(true); + expect(component.find(ToolbarPopover).at(0).prop('isDisabled')).toEqual(true); }); - it('should show the values of the X and Y axes titles on the corresponding input text', () => { + it('should disable the popover if there is no right axis', () => { + const state = testState(); + const component = shallow(); + + expect(component.find(AxisSettingsPopover).at(2).prop('isDisabled')).toEqual(true); + }); + + it('should enable the popover if there is right axis', () => { const state = testState(); const component = shallow( { setState={jest.fn()} state={{ ...state, - xTitle: 'My custom X axis title', - yTitle: 'My custom Y axis title', + layers: [{ ...state.layers[0], yConfig: [{ axisMode: 'right', forAccessor: 'bar' }] }], }} /> ); - expect(component.find('[data-test-subj="lnsXAxisTitle"]').prop('value')).toBe( - 'My custom X axis title' - ); - expect(component.find('[data-test-subj="lnsYAxisTitle"]').prop('value')).toBe( - 'My custom Y axis title' - ); + expect(component.find(AxisSettingsPopover).at(2).prop('isDisabled')).toEqual(false); }); - it('should disable the input texts if the switch is off', () => { + it('should render the AxisSettingsPopover 3 times', () => { const state = testState(); const component = shallow( { setState={jest.fn()} state={{ ...state, - showXAxisTitle: false, - showYAxisTitle: false, + layers: [{ ...state.layers[0], yConfig: [{ axisMode: 'right', forAccessor: 'foo' }] }], }} /> ); - expect(component.find('[data-test-subj="lnsXAxisTitle"]').prop('disabled')).toBe(true); - expect(component.find('[data-test-subj="lnsYAxisTitle"]').prop('disabled')).toBe(true); - }); - - it('has the tick labels buttons enabled', () => { - const state = testState(); - const component = shallow(); - - const options = component - .find('[data-test-subj="lnsTickLabelsSettings"]') - .prop('options') as EuiButtonGroupProps['options']; - - expect(options!.map(({ label }) => label)).toEqual(['X-axis', 'Y-axis']); - - const selections = component - .find('[data-test-subj="lnsTickLabelsSettings"]') - .prop('idToSelectedMap'); - - expect(selections!).toEqual({ x: true, y: true }); - }); - - it('has the gridlines buttons enabled', () => { - const state = testState(); - const component = shallow(); - - const selections = component - .find('[data-test-subj="lnsGridlinesSettings"]') - .prop('idToSelectedMap'); - - expect(selections!).toEqual({ x: true, y: true }); + expect(component.find(AxisSettingsPopover).length).toEqual(3); }); }); }); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx index 62fd6e013f20d0..bc98bf53d9f122 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx @@ -5,7 +5,7 @@ */ import './xy_config_panel.scss'; -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { Position } from '@elastic/charts'; import { debounce } from 'lodash'; @@ -15,19 +15,13 @@ import { EuiFlexItem, EuiSuperSelect, EuiFormRow, - EuiPopover, EuiText, - EuiSelect, htmlIdGenerator, EuiForm, EuiColorPicker, EuiColorPickerProps, EuiToolTip, EuiIcon, - EuiFieldText, - EuiSwitch, - EuiHorizontalRule, - EuiTitle, } from '@elastic/eui'; import { VisualizationLayerWidgetProps, @@ -38,9 +32,13 @@ import { State, SeriesType, visualizationTypes, YAxisMode, AxesSettingsConfig } import { isHorizontalChart, isHorizontalSeries, getSeriesColor } from './state_helpers'; import { trackUiEvent } from '../lens_ui_telemetry'; import { fittingFunctionDefinitions } from './fitting_functions'; -import { ToolbarButton } from '../toolbar_button'; +import { ToolbarPopover, LegendSettingsPopover } from '../shared_components'; +import { AxisSettingsPopover } from './axis_settings_popover'; +import { TooltipWrapper } from './tooltip_wrapper'; +import { getAxesConfiguration } from './axes_configuration'; type UnwrapArray = T extends Array ? P : T; +type AxesSettingsConfigKeys = keyof AxesSettingsConfig; function updateLayer(state: State, layer: UnwrapArray, index: number): State { const newLayers = [...state.layers]; @@ -57,21 +55,21 @@ const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: id: `xy_legend_auto`, value: 'auto', label: i18n.translate('xpack.lens.xyChart.legendVisibility.auto', { - defaultMessage: 'auto', + defaultMessage: 'Auto', }), }, { id: `xy_legend_show`, value: 'show', label: i18n.translate('xpack.lens.xyChart.legendVisibility.show', { - defaultMessage: 'show', + defaultMessage: 'Show', }), }, { id: `xy_legend_hide`, value: 'hide', label: i18n.translate('xpack.lens.xyChart.legendVisibility.hide', { - defaultMessage: 'hide', + defaultMessage: 'Hide', }), }, ]; @@ -120,86 +118,25 @@ export function LayerContextMenu(props: VisualizationLayerWidgetProps) { } export function XyToolbar(props: VisualizationToolbarProps) { - const axes = [ - { - id: 'x', - label: 'X-axis', - }, - { - id: 'y', - label: 'Y-axis', - }, - ]; + const { state, setState } = props; - const { frame, state, setState } = props; - - const [open, setOpen] = useState(false); const hasNonBarSeries = state?.layers.some(({ seriesType }) => ['area_stacked', 'area', 'line'].includes(seriesType) ); - const [xAxisTitle, setXAxisTitle] = useState(state?.xTitle); - const [yAxisTitle, setYAxisTitle] = useState(state?.yTitle); - - const xyTitles = useCallback(() => { - const defaults = { - xTitle: xAxisTitle, - yTitle: yAxisTitle, - }; - const layer = state?.layers[0]; - if (!layer || !layer.accessors.length) { - return defaults; - } - const datasource = frame.datasourceLayers[layer.layerId]; - if (!datasource) { - return defaults; - } - const x = layer.xAccessor ? datasource.getOperationForColumnId(layer.xAccessor) : null; - const y = layer.accessors[0] ? datasource.getOperationForColumnId(layer.accessors[0]) : null; - - return { - xTitle: defaults.xTitle || x?.label, - yTitle: defaults.yTitle || y?.label, - }; - /* We want this callback to run only if open changes its state. What we want to accomplish here is to give the user a better UX. - By default these input fields have the axis legends. If the user changes the input text, the axis legends should also change. - BUT if the user cleans up the input text, it should remain empty until the user closes and reopens the panel. - In that case, the default axes legend should appear. */ - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [open]); - - useEffect(() => { - const { - xTitle, - yTitle, - }: { xTitle: string | undefined; yTitle: string | undefined } = xyTitles(); - setXAxisTitle(xTitle); - setYAxisTitle(yTitle); - }, [xyTitles]); - - const onXTitleChange = (value: string): void => { - setXAxisTitle(value); - setState({ ...state, xTitle: value }); - }; - - const onYTitleChange = (value: string): void => { - setYAxisTitle(value); - setState({ ...state, yTitle: value }); - }; - - type AxesSettingsConfigKeys = keyof AxesSettingsConfig; + const shouldRotate = state?.layers.length ? isHorizontalChart(state.layers) : false; + const axisGroups = getAxesConfiguration(state?.layers, shouldRotate); const tickLabelsVisibilitySettings = { x: state?.tickLabelsVisibilitySettings?.x ?? true, - y: state?.tickLabelsVisibilitySettings?.y ?? true, + yLeft: state?.tickLabelsVisibilitySettings?.yLeft ?? true, + yRight: state?.tickLabelsVisibilitySettings?.yRight ?? true, }; - - const onTickLabelsVisibilitySettingsChange = (optionId: string): void => { - const id = optionId as AxesSettingsConfigKeys; + const onTickLabelsVisibilitySettingsChange = (optionId: AxesSettingsConfigKeys): void => { const newTickLabelsVisibilitySettings = { ...tickLabelsVisibilitySettings, ...{ - [id]: !tickLabelsVisibilitySettings[id], + [optionId]: !tickLabelsVisibilitySettings[optionId], }, }; setState({ @@ -210,15 +147,15 @@ export function XyToolbar(props: VisualizationToolbarProps) { const gridlinesVisibilitySettings = { x: state?.gridlinesVisibilitySettings?.x ?? true, - y: state?.gridlinesVisibilitySettings?.y ?? true, + yLeft: state?.gridlinesVisibilitySettings?.yLeft ?? true, + yRight: state?.gridlinesVisibilitySettings?.yRight ?? true, }; - const onGridlinesVisibilitySettingsChange = (optionId: string): void => { - const id = optionId as AxesSettingsConfigKeys; + const onGridlinesVisibilitySettingsChange = (optionId: AxesSettingsConfigKeys): void => { const newGridlinesVisibilitySettings = { ...gridlinesVisibilitySettings, ...{ - [id]: !gridlinesVisibilitySettings[id], + [optionId]: !gridlinesVisibilitySettings[optionId], }, }; setState({ @@ -227,6 +164,27 @@ export function XyToolbar(props: VisualizationToolbarProps) { }); }; + const axisTitlesVisibilitySettings = { + x: state?.axisTitlesVisibilitySettings?.x ?? true, + yLeft: state?.axisTitlesVisibilitySettings?.yLeft ?? true, + yRight: state?.axisTitlesVisibilitySettings?.yRight ?? true, + }; + const onAxisTitlesVisibilitySettingsChange = ( + axis: AxesSettingsConfigKeys, + checked: boolean + ): void => { + const newAxisTitlesVisibilitySettings = { + ...axisTitlesVisibilitySettings, + ...{ + [axis]: checked, + }, + }; + setState({ + ...state, + axisTitlesVisibilitySettings: newAxisTitlesVisibilitySettings, + }); + }; + const legendMode = state?.legend.isVisible && !state?.legend.showSingleSeries ? 'auto' @@ -234,243 +192,149 @@ export function XyToolbar(props: VisualizationToolbarProps) { ? 'hide' : 'show'; return ( - - - { - setOpen(!open); - }} - > - {i18n.translate('xpack.lens.xyChart.settingsLabel', { defaultMessage: 'Settings' })} - - } - isOpen={open} - closePopover={() => { - setOpen(false); - }} - anchorPosition="downRight" - > - + + + - - { - return { - value: id, - dropdownDisplay: ( - <> - {title} - -

{description}

-
- - ), - inputDisplay: title, - }; + setState({ ...state, fittingFunction: value })} - itemLayoutAlign="top" - hasDividers - /> - -
- - - value === legendMode)!.id} - onChange={(optionId) => { - const newMode = legendOptions.find(({ id }) => id === optionId)!.value; - if (newMode === 'auto') { - setState({ - ...state, - legend: { ...state.legend, isVisible: true, showSingleSeries: false }, - }); - } else if (newMode === 'show') { - setState({ - ...state, - legend: { ...state.legend, isVisible: true, showSingleSeries: true }, - }); - } else if (newMode === 'hide') { - setState({ - ...state, - legend: { ...state.legend, isVisible: false, showSingleSeries: false }, - }); - } - }} - /> - - - { + > + { + return { + value: id, + dropdownDisplay: ( + <> + {title} + +

{description}

+
+ + ), + inputDisplay: title, + }; + })} + valueOfSelected={state?.fittingFunction || 'None'} + onChange={(value) => setState({ ...state, fittingFunction: value })} + itemLayoutAlign="top" + hasDividers + /> +
+ + + { + const newMode = legendOptions.find(({ id }) => id === optionId)!.value; + if (newMode === 'auto') { setState({ ...state, - legend: { ...state.legend, position: e.target.value as Position }, + legend: { ...state.legend, isVisible: true, showSingleSeries: false }, }); - }} - /> - - - - onTickLabelsVisibilitySettingsChange(id)} - buttonSize="compressed" - isFullWidth - type="multi" - /> - - { + setState({ + ...state, + legend: { ...state.legend, position: id as Position }, + }); + }} + /> +
+
+ + + - onGridlinesVisibilitySettingsChange(id)} - buttonSize="compressed" - isFullWidth - type="multi" - /> - - - - - {i18n.translate('xpack.lens.xyChart.axisTitles', { defaultMessage: 'Axis titles' })} - - - - X-axis - - - setState({ ...state, showXAxisTitle: target.checked }) - } - checked={state?.showXAxisTitle ?? true} - /> - - + condition={ + Object.keys(axisGroups.find((group) => group.groupId === 'left') || {}).length === 0 } > - onXTitleChange(target.value)} - aria-label={i18n.translate('xpack.lens.xyChart.overwriteXaxis', { - defaultMessage: 'Overwrite X-axis title', - })} + setState({ ...state, yTitle: value })} + areTickLabelsVisible={tickLabelsVisibilitySettings.yLeft} + toggleTickLabelsVisibility={onTickLabelsVisibilitySettingsChange} + areGridlinesVisible={gridlinesVisibilitySettings.yLeft} + toggleGridlinesVisibility={onGridlinesVisibilitySettingsChange} + isDisabled={ + Object.keys(axisGroups.find((group) => group.groupId === 'left') || {}).length === 0 + } + isAxisTitleVisible={axisTitlesVisibilitySettings.yLeft} + toggleAxisTitleVisibility={onAxisTitlesVisibilitySettingsChange} /> - - - Y-axis - - - setState({ ...state, showYAxisTitle: target.checked }) - } - checked={state?.showYAxisTitle ?? true} - /> - -
+ + setState({ ...state, xTitle: value })} + areTickLabelsVisible={tickLabelsVisibilitySettings.x} + toggleTickLabelsVisibility={onTickLabelsVisibilitySettingsChange} + areGridlinesVisible={gridlinesVisibilitySettings.x} + toggleGridlinesVisibility={onGridlinesVisibilitySettingsChange} + isAxisTitleVisible={axisTitlesVisibilitySettings.x} + toggleAxisTitleVisibility={onAxisTitlesVisibilitySettingsChange} + /> + group.groupId === 'right') || {}).length === 0 } > - onYTitleChange(target.value)} - aria-label={i18n.translate('xpack.lens.xyChart.overwriteYaxis', { - defaultMessage: 'Overwrite Y-axis title', - })} + setState({ ...state, yRightTitle: value })} + areTickLabelsVisible={tickLabelsVisibilitySettings.yRight} + toggleTickLabelsVisibility={onTickLabelsVisibilitySettingsChange} + areGridlinesVisible={gridlinesVisibilitySettings.yRight} + toggleGridlinesVisibility={onGridlinesVisibilitySettingsChange} + isDisabled={ + Object.keys(axisGroups.find((group) => group.groupId === 'right') || {}).length === + 0 + } + isAxisTitleVisible={axisTitlesVisibilitySettings.yRight} + toggleAxisTitleVisibility={onAxisTitlesVisibilitySettingsChange} /> - - + + ); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx index c9c27193c437e1..1d809f222eb005 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx @@ -215,22 +215,29 @@ const sampleLayer: LayerArgs = { const createArgsWithLayers = (layers: LayerArgs[] = [sampleLayer]): XYArgs => ({ xTitle: '', yTitle: '', + yRightTitle: '', legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top, }, - showXAxisTitle: true, - showYAxisTitle: true, + axisTitlesVisibilitySettings: { + type: 'lens_xy_axisTitlesVisibilityConfig', + x: true, + yLeft: true, + yRight: true, + }, tickLabelsVisibilitySettings: { type: 'lens_xy_tickLabelsConfig', x: true, - y: false, + yLeft: false, + yRight: false, }, gridlinesVisibilitySettings: { type: 'lens_xy_gridlinesConfig', x: true, - y: false, + yLeft: false, + yRight: false, }, layers, }); @@ -291,7 +298,8 @@ describe('xy_expression', () => { test('tickLabelsConfig produces the correct arguments', () => { const args: AxesSettingsConfig = { x: true, - y: false, + yLeft: false, + yRight: false, }; const result = tickLabelsConfig.fn(null, args, createMockExecutionContext()); @@ -305,7 +313,8 @@ describe('xy_expression', () => { test('gridlinesConfig produces the correct arguments', () => { const args: AxesSettingsConfig = { x: true, - y: false, + yLeft: false, + yRight: false, }; const result = gridlinesConfig.fn(null, args, createMockExecutionContext()); @@ -1417,7 +1426,12 @@ describe('xy_expression', () => { test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { const { data, args } = sampleArgs(); - args.tickLabelsVisibilitySettings = { x: false, y: true, type: 'lens_xy_tickLabelsConfig' }; + args.tickLabelsVisibilitySettings = { + x: false, + yLeft: true, + yRight: true, + type: 'lens_xy_tickLabelsConfig', + }; const instance = shallow( { test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { const { data, args } = sampleArgs(); - args.tickLabelsVisibilitySettings = { x: true, y: false, type: 'lens_xy_tickLabelsConfig' }; + args.tickLabelsVisibilitySettings = { + x: true, + yLeft: false, + yRight: false, + type: 'lens_xy_tickLabelsConfig', + }; const instance = shallow( { test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { const { data, args } = sampleArgs(); - args.tickLabelsVisibilitySettings = { x: true, y: true, type: 'lens_xy_tickLabelsConfig' }; + args.tickLabelsVisibilitySettings = { + x: true, + yLeft: true, + yRight: true, + type: 'lens_xy_tickLabelsConfig', + }; const instance = shallow( { test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { const { data, args } = sampleArgs(); - args.tickLabelsVisibilitySettings = { x: false, y: true, type: 'lens_xy_tickLabelsConfig' }; + args.tickLabelsVisibilitySettings = { + x: false, + yLeft: true, + yRight: true, + type: 'lens_xy_tickLabelsConfig', + }; const instance = shallow( { const args: XYArgs = { xTitle: '', yTitle: '', + yRightTitle: '', legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, tickLabelsVisibilitySettings: { type: 'lens_xy_tickLabelsConfig', x: true, - y: true, + yLeft: true, + yRight: true, }, gridlinesVisibilitySettings: { type: 'lens_xy_gridlinesConfig', x: true, - y: false, + yLeft: false, + yRight: false, }, layers: [ { @@ -1635,16 +1667,19 @@ describe('xy_expression', () => { const args: XYArgs = { xTitle: '', yTitle: '', + yRightTitle: '', legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, tickLabelsVisibilitySettings: { type: 'lens_xy_tickLabelsConfig', x: true, - y: false, + yLeft: false, + yRight: false, }, gridlinesVisibilitySettings: { type: 'lens_xy_gridlinesConfig', x: true, - y: false, + yLeft: false, + yRight: false, }, layers: [ { @@ -1701,16 +1736,19 @@ describe('xy_expression', () => { const args: XYArgs = { xTitle: '', yTitle: '', + yRightTitle: '', legend: { type: 'lens_xy_legendConfig', isVisible: true, position: Position.Top }, tickLabelsVisibilitySettings: { type: 'lens_xy_tickLabelsConfig', x: true, - y: false, + yLeft: false, + yRight: false, }, gridlinesVisibilitySettings: { type: 'lens_xy_gridlinesConfig', x: true, - y: false, + yLeft: false, + yRight: false, }, layers: [ { @@ -1894,7 +1932,12 @@ describe('xy_expression', () => { test('it should hide the X axis title if the corresponding switch is off', () => { const { data, args } = sampleArgs(); - args.showXAxisTitle = false; + args.axisTitlesVisibilitySettings = { + x: false, + yLeft: true, + yRight: true, + type: 'lens_xy_axisTitlesVisibilityConfig', + }; const component = shallow( { test('it should show the X axis gridlines if the setting is on', () => { const { data, args } = sampleArgs(); - args.gridlinesVisibilitySettings = { x: true, y: false, type: 'lens_xy_gridlinesConfig' }; + args.gridlinesVisibilitySettings = { + x: true, + yLeft: false, + yRight: false, + type: 'lens_xy_gridlinesConfig', + }; const component = shallow( , - index: number + groupId: string ) => { - if (index > 0 && args.yTitle) return; + const yTitle = groupId === 'right' ? args.yRightTitle : args.yTitle; return ( - args.yTitle || + yTitle || axisSeries .map( (series) => @@ -322,6 +333,24 @@ export function XYChart({ ); }; + const getYAxesStyle = (groupId: string) => { + const style = { + tickLabel: { + visible: + groupId === 'right' + ? tickLabelsVisibilitySettings?.yRight + : tickLabelsVisibilitySettings?.yLeft, + }, + axisTitle: { + visible: + groupId === 'right' + ? axisTitlesVisibilitySettings?.yRight + : axisTitlesVisibilitySettings?.yLeft, + }, + }; + return style; + }; + return ( - {yAxesConfiguration.map((axis, index) => ( + {yAxesConfiguration.map((axis) => ( axis.formatter.convert(d)} - style={{ - tickLabel: { - visible: tickLabelsVisibilitySettings?.y, - }, - axisTitle: { - visible: showYAxisTitle, - }, - }} + tickFormat={(d) => axis.formatter?.convert(d) || ''} + style={getYAxesStyle(axis.groupId)} /> ))} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts index ea5cff80695a3b..09a2cc652a9b30 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts @@ -555,10 +555,9 @@ describe('xy_suggestions', () => { const currentState: XYState = { legend: { isVisible: true, position: 'bottom' }, fittingFunction: 'None', - showXAxisTitle: true, - showYAxisTitle: true, - gridlinesVisibilitySettings: { x: true, y: true }, - tickLabelsVisibilitySettings: { x: true, y: false }, + axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, preferredSeriesType: 'bar', layers: [ { @@ -597,10 +596,9 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, preferredSeriesType: 'bar', fittingFunction: 'None', - showXAxisTitle: true, - showYAxisTitle: true, - gridlinesVisibilitySettings: { x: true, y: true }, - tickLabelsVisibilitySettings: { x: true, y: false }, + axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, layers: [ { accessors: ['price', 'quantity'], @@ -710,10 +708,9 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, preferredSeriesType: 'bar', fittingFunction: 'None', - showXAxisTitle: true, - showYAxisTitle: true, - gridlinesVisibilitySettings: { x: true, y: true }, - tickLabelsVisibilitySettings: { x: true, y: false }, + axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, layers: [ { accessors: ['price', 'quantity'], @@ -753,10 +750,9 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, preferredSeriesType: 'bar', fittingFunction: 'None', - showXAxisTitle: true, - showYAxisTitle: true, - gridlinesVisibilitySettings: { x: true, y: true }, - tickLabelsVisibilitySettings: { x: true, y: false }, + axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, layers: [ { accessors: ['price'], @@ -797,10 +793,9 @@ describe('xy_suggestions', () => { legend: { isVisible: true, position: 'bottom' }, preferredSeriesType: 'bar', fittingFunction: 'None', - showXAxisTitle: true, - showYAxisTitle: true, - gridlinesVisibilitySettings: { x: true, y: true }, - tickLabelsVisibilitySettings: { x: true, y: false }, + axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, + tickLabelsVisibilitySettings: { x: true, yLeft: false, yRight: false }, layers: [ { accessors: ['price', 'quantity'], diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index 42fc538874b93d..e6286523d8e2e2 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -489,15 +489,21 @@ function buildSuggestion({ fittingFunction: currentState?.fittingFunction || 'None', xTitle: currentState?.xTitle, yTitle: currentState?.yTitle, - showXAxisTitle: currentState?.showXAxisTitle ?? true, - showYAxisTitle: currentState?.showYAxisTitle ?? true, + yRightTitle: currentState?.yRightTitle, + axisTitlesVisibilitySettings: currentState?.axisTitlesVisibilitySettings || { + x: true, + yLeft: true, + yRight: true, + }, tickLabelsVisibilitySettings: currentState?.tickLabelsVisibilitySettings || { x: true, - y: true, + yLeft: true, + yRight: true, }, gridlinesVisibilitySettings: currentState?.gridlinesVisibilitySettings || { x: true, - y: true, + yLeft: true, + yRight: true, }, preferredSeriesType: seriesType, layers: Object.keys(existingLayer).length ? keptLayers : [...keptLayers, newLayer], diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 3589ec41e4db37..d72d04d2a18435 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -263,3 +263,7 @@ export enum MB_LOOKUP_FUNCTION { GET = 'get', FEATURE_STATE = 'feature-state', } + +export type RawValue = string | number | boolean | undefined | null; + +export type FieldFormatter = (value: RawValue) => string | number; diff --git a/x-pack/plugins/maps/public/classes/sources/source.ts b/x-pack/plugins/maps/public/classes/sources/source.ts index 4a050cc3d7d198..7e7a7bd8f049dd 100644 --- a/x-pack/plugins/maps/public/classes/sources/source.ts +++ b/x-pack/plugins/maps/public/classes/sources/source.ts @@ -12,7 +12,7 @@ import { Adapters } from 'src/plugins/inspector/public'; import { copyPersistentState } from '../../reducers/util'; import { IField } from '../fields/field'; -import { MAX_ZOOM, MIN_ZOOM } from '../../../common/constants'; +import { FieldFormatter, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants'; import { AbstractSourceDescriptor } from '../../../common/descriptor_types'; import { OnSourceChangeArgs } from '../../connected_components/layer_panel/view'; @@ -37,8 +37,6 @@ export type PreIndexedShape = { path: string; }; -export type FieldFormatter = (value: string | number | null | undefined | boolean) => string; - export interface ISource { destroy(): void; getDisplayName(): Promise; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx index de8f3b5c09175f..c9188a0a19b0d3 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx @@ -15,7 +15,7 @@ import { shallow } from 'enzyme'; import { Feature, Point } from 'geojson'; import { DynamicColorProperty } from './dynamic_color_property'; -import { COLOR_MAP_TYPE, VECTOR_STYLES } from '../../../../../common/constants'; +import { COLOR_MAP_TYPE, RawValue, VECTOR_STYLES } from '../../../../../common/constants'; import { mockField, MockLayer, MockStyle } from './__tests__/test_util'; import { ColorDynamicOptions } from '../../../../../common/descriptor_types'; import { IVectorLayer } from '../../../layers/vector_layer/vector_layer'; @@ -28,7 +28,7 @@ const makeProperty = (options: ColorDynamicOptions, style?: MockStyle, field?: I field ? field : mockField, (new MockLayer(style ? style : new MockStyle()) as unknown) as IVectorLayer, () => { - return (value: string | number | undefined) => value + '_format'; + return (value: RawValue) => value + '_format'; } ); }; @@ -273,7 +273,7 @@ describe('supportsFieldMeta', () => { null, (new MockLayer(new MockStyle()) as unknown) as IVectorLayer, () => { - return (value: string | number | undefined) => value + '_format'; + return (value: RawValue) => value + '_format'; } ); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx index 06987ab8bcc48f..2f9e4709c1c0b9 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx @@ -13,7 +13,7 @@ jest.mock('../components/vector_style_editor', () => ({ })); import React from 'react'; -import { VECTOR_STYLES } from '../../../../../common/constants'; +import { RawValue, VECTOR_STYLES } from '../../../../../common/constants'; // @ts-ignore import { DynamicIconProperty } from './dynamic_icon_property'; import { mockField, MockLayer } from './__tests__/test_util'; @@ -33,7 +33,7 @@ const makeProperty = (options: Partial, field: IField = mock field, mockVectorLayer, () => { - return (value: string | number | undefined) => value + '_format'; + return (value: RawValue) => value + '_format'; } ); }; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_orientation_property.ts b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_orientation_property.ts index dd976027a50f2f..192fa1f4db6e06 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_orientation_property.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_orientation_property.ts @@ -5,25 +5,29 @@ */ import { Map as MbMap } from 'mapbox-gl'; -import { DynamicStyleProperty } from './dynamic_style_property'; -import { getComputedFieldName } from '../style_util'; -import { VECTOR_STYLES } from '../../../../../common/constants'; +import { DynamicStyleProperty, getNumericalMbFeatureStateValue } from './dynamic_style_property'; import { OrientationDynamicOptions } from '../../../../../common/descriptor_types'; +import { RawValue } from '../../../../../common/constants'; export class DynamicOrientationProperty extends DynamicStyleProperty { syncIconRotationWithMb(symbolLayerId: string, mbMap: MbMap) { if (this._field && this._field.isValid()) { - const targetName = this._field.supportsAutoDomain() - ? getComputedFieldName(VECTOR_STYLES.ICON_ORIENTATION, this.getFieldName()) - : this._field.getName(); - // Using property state instead of feature-state because layout properties do not support feature-state - mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', ['coalesce', ['get', targetName], 0]); + const targetName = this.getMbPropertyName(); + mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', [ + 'coalesce', + [this.getMbLookupFunction(), targetName], + 0, + ]); } else { mbMap.setLayoutProperty(symbolLayerId, 'icon-rotate', 0); } } - supportsMbFeatureState() { + supportsMbFeatureState(): boolean { return false; } + + getMbPropertyValue(rawValue: RawValue): RawValue { + return getNumericalMbFeatureStateValue(rawValue); + } } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx index c5298067f6cbe7..b4244cf7829c45 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx @@ -15,7 +15,7 @@ import { shallow } from 'enzyme'; // @ts-ignore import { DynamicSizeProperty } from './dynamic_size_property'; -import { VECTOR_STYLES } from '../../../../../common/constants'; +import { RawValue, VECTOR_STYLES } from '../../../../../common/constants'; import { IField } from '../../../fields/field'; import { Map as MbMap } from 'mapbox-gl'; import { SizeDynamicOptions } from '../../../../../common/descriptor_types'; @@ -48,7 +48,7 @@ const makeProperty = ( field, (new MockLayer(mockStyle) as unknown) as IVectorLayer, () => { - return (value: string | number | undefined) => value + '_format'; + return (value: RawValue) => value + '_format'; }, false ); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.tsx index 35c830f3cb5e37..4e75a61539ad96 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.tsx @@ -7,7 +7,7 @@ import _ from 'lodash'; import React from 'react'; import { Map as MbMap } from 'mapbox-gl'; -import { DynamicStyleProperty, FieldFormatter } from './dynamic_style_property'; +import { DynamicStyleProperty } from './dynamic_style_property'; import { OrdinalLegend } from '../components/legend/ordinal_legend'; import { makeMbClampedNumberExpression } from '../style_util'; import { @@ -16,7 +16,7 @@ import { SMALL_MAKI_ICON_SIZE, // @ts-expect-error } from '../symbol_utils'; -import { MB_LOOKUP_FUNCTION, VECTOR_STYLES } from '../../../../../common/constants'; +import { FieldFormatter, MB_LOOKUP_FUNCTION, VECTOR_STYLES } from '../../../../../common/constants'; import { SizeDynamicOptions } from '../../../../../common/descriptor_types'; import { IField } from '../../../fields/field'; import { IVectorLayer } from '../../../layers/vector_layer/vector_layer'; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx index f6ab052497723e..2bc819daeea905 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx @@ -15,6 +15,8 @@ import { SOURCE_META_DATA_REQUEST_ID, STYLE_TYPE, VECTOR_STYLES, + RawValue, + FieldFormatter, } from '../../../../../common/constants'; import { OrdinalFieldMetaPopover } from '../components/field_meta/ordinal_field_meta_popover'; import { CategoricalFieldMetaPopover } from '../components/field_meta/categorical_field_meta_popover'; @@ -28,6 +30,7 @@ import { IField } from '../../../fields/field'; import { IVectorLayer } from '../../../layers/vector_layer/vector_layer'; import { IJoin } from '../../../joins/join'; import { IVectorStyle } from '../vector_style'; +import { getComputedFieldName } from '../style_util'; export interface IDynamicStyleProperty extends IStyleProperty { getFieldMetaOptions(): FieldMetaOptions; @@ -46,9 +49,16 @@ export interface IDynamicStyleProperty extends IStyleProperty { pluckOrdinalStyleMetaFromFeatures(features: Feature[]): RangeFieldMeta | null; pluckCategoricalStyleMetaFromFeatures(features: Feature[]): CategoryFieldMeta | null; getValueSuggestions(query: string): Promise; -} -export type FieldFormatter = (value: string | number | undefined) => string | number; + // Returns the name that should be used for accessing the data from the mb-style rule + // Depending on + // - whether the field is used for labeling, icon-orientation, or other properties (color, size, ...), `feature-state` and or `get` is used + // - whether the field was run through a field-formatter, a new dynamic field is created with the formatted-value + // The combination of both will inform what field-name (e.g. the "raw" field name from the properties, the "computed field-name" for an on-the-fly created property (e.g. for feature-state or field-formatting). + // todo: There is an existing limitation to .mvt backed sources, where the field-formatters are not applied. Here, the raw-data needs to be accessed. + getMbPropertyName(): string; + getMbPropertyValue(value: RawValue): RawValue; +} export class DynamicStyleProperty extends AbstractStyleProperty @@ -313,7 +323,7 @@ export class DynamicStyleProperty }; } - formatField(value: string | number | undefined): string | number { + formatField(value: RawValue): string | number { if (this.getField()) { const fieldName = this.getFieldName(); const fieldFormatter = this._getFieldFormatter(fieldName); @@ -345,4 +355,43 @@ export class DynamicStyleProperty /> ); } + + getMbPropertyName() { + if (!this._field) { + return ''; + } + + let targetName; + if (this.supportsMbFeatureState()) { + // Base case for any properties that can support feature-state (e.g. color, size, ...) + // They just re-use the original property-name + targetName = this._field.getName(); + } else { + if (this._field.canReadFromGeoJson() && this._field.supportsAutoDomain()) { + // Geojson-sources can support rewrite + // e.g. field-formatters will create duplicate field + targetName = getComputedFieldName(this.getStyleName(), this._field.getName()); + } else { + // Non-geojson sources (e.g. 3rd party mvt or ES-source as mvt) + targetName = this._field.getName(); + } + } + return targetName; + } + + getMbPropertyValue(rawValue: RawValue): RawValue { + // Maps only uses feature-state for numerical values. + // `supportsMbFeatureState` will only return true when the mb-style rule does a feature-state lookup on a numerical value + // Calling `isOrdinal` would be equivalent. + return this.supportsMbFeatureState() ? getNumericalMbFeatureStateValue(rawValue) : rawValue; + } +} + +export function getNumericalMbFeatureStateValue(value: RawValue) { + if (typeof value !== 'string') { + return value; + } + + const valueAsFloat = parseFloat(value); + return isNaN(valueAsFloat) ? null : valueAsFloat; } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_text_property.ts b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_text_property.ts index d55a6e1cfb4441..ec79d71eb7587b 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_text_property.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_text_property.ts @@ -6,18 +6,18 @@ import { Map as MbMap } from 'mapbox-gl'; import { DynamicStyleProperty } from './dynamic_style_property'; -import { getComputedFieldName } from '../style_util'; import { LabelDynamicOptions } from '../../../../../common/descriptor_types'; +import { RawValue } from '../../../../../common/constants'; export class DynamicTextProperty extends DynamicStyleProperty { syncTextFieldWithMb(mbLayerId: string, mbMap: MbMap) { if (this._field && this._field.isValid()) { - // Fields that support auto-domain are normalized with a field-formatter and stored into a computed-field - // Otherwise, the raw value is just carried over and no computed field is created. - const targetName = this._field.supportsAutoDomain() - ? getComputedFieldName(this._styleName, this.getFieldName()) - : this._field.getName(); - mbMap.setLayoutProperty(mbLayerId, 'text-field', ['coalesce', ['get', targetName], '']); + const targetName = this.getMbPropertyName(); + mbMap.setLayoutProperty(mbLayerId, 'text-field', [ + 'coalesce', + [this.getMbLookupFunction(), targetName], + '', + ]); } else { mbMap.setLayoutProperty(mbLayerId, 'text-field', null); } @@ -34,4 +34,8 @@ export class DynamicTextProperty extends DynamicStyleProperty { isDynamic(): boolean; isComplete(): boolean; - formatField(value: string | number | undefined): string | number; + formatField(value: RawValue): string | number; getStyleName(): VECTOR_STYLES; getOptions(): T; renderLegendDetailRow(legendProps: LegendProps): ReactElement | null; @@ -53,9 +53,14 @@ export class AbstractStyleProperty implements IStyleProperty { return true; } - formatField(value: string | number | undefined): string | number { - // eslint-disable-next-line eqeqeq - return value == undefined ? '' : value; + formatField(value: RawValue): string | number { + if (typeof value === 'undefined' || value === null) { + return ''; + } else if (typeof value === 'boolean') { + return value.toString(); + } else { + return value; + } } getStyleName(): VECTOR_STYLES { diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx index 956522524a2ebd..1244c53afe9a6c 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx @@ -29,7 +29,7 @@ import { import { StyleMeta } from './style_meta'; import { VectorIcon } from './components/legend/vector_icon'; import { VectorStyleLegend } from './components/legend/vector_style_legend'; -import { getComputedFieldName, isOnlySingleFeatureType } from './style_util'; +import { isOnlySingleFeatureType } from './style_util'; import { StaticStyleProperty } from './properties/static_style_property'; import { DynamicStyleProperty } from './properties/dynamic_style_property'; import { DynamicSizeProperty } from './properties/dynamic_size_property'; @@ -82,11 +82,6 @@ const POINTS = [GEO_JSON_TYPE.POINT, GEO_JSON_TYPE.MULTI_POINT]; const LINES = [GEO_JSON_TYPE.LINE_STRING, GEO_JSON_TYPE.MULTI_LINE_STRING]; const POLYGONS = [GEO_JSON_TYPE.POLYGON, GEO_JSON_TYPE.MULTI_POLYGON]; -function getNumericalMbFeatureStateValue(value: string) { - const valueAsFloat = parseFloat(value); - return isNaN(valueAsFloat) ? null : valueAsFloat; -} - export interface IVectorStyle extends IStyle { getAllStyleProperties(): Array>; getDynamicPropertiesArray(): Array>; @@ -618,21 +613,17 @@ export class VectorStyle implements IVectorStyle { for (let j = 0; j < dynamicStyleProps.length; j++) { const dynamicStyleProp = dynamicStyleProps[j]; - const name = dynamicStyleProp.getFieldName(); - const computedName = getComputedFieldName(dynamicStyleProp.getStyleName(), name); - const rawValue = feature.properties ? feature.properties[name] : undefined; + const targetMbName = dynamicStyleProp.getMbPropertyName(); + const rawValue = feature.properties + ? feature.properties[dynamicStyleProp.getFieldName()] + : undefined; + const targetMbValue = dynamicStyleProp.getMbPropertyValue(rawValue); if (dynamicStyleProp.supportsMbFeatureState()) { - tmpFeatureState[name] = getNumericalMbFeatureStateValue(rawValue); // the same value will be potentially overridden multiple times, if the name remains identical + tmpFeatureState[targetMbName] = targetMbValue; // the same value will be potentially overridden multiple times, if the name remains identical } else { - // in practice, a new system property will only be created for: - // - label text: this requires the value to be formatted first. - // - icon orientation: this is a lay-out property which do not support feature-state (but we're still coercing to a number) - - const formattedValue = dynamicStyleProp.isOrdinal() - ? getNumericalMbFeatureStateValue(rawValue) - : dynamicStyleProp.formatField(rawValue); - - if (feature.properties) feature.properties[computedName] = formattedValue; + if (feature.properties) { + feature.properties[targetMbName] = targetMbValue; + } } } tmpFeatureIdentifier.source = mbSourceId; diff --git a/x-pack/plugins/ml/public/ui_actions/apply_influencer_filters_action.tsx b/x-pack/plugins/ml/public/ui_actions/apply_influencer_filters_action.tsx index 9e50410751c372..50f00ac53edab5 100644 --- a/x-pack/plugins/ml/public/ui_actions/apply_influencer_filters_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/apply_influencer_filters_action.tsx @@ -26,7 +26,7 @@ export function createApplyInfluencerFiltersAction( }, getDisplayName() { return i18n.translate('xpack.ml.actions.applyInfluencersFiltersTitle', { - defaultMessage: 'Filer for value', + defaultMessage: 'Filter for value', }); }, async execute({ data }: SwimLaneDrilldownContext) { diff --git a/x-pack/plugins/ml/server/routes/indices.ts b/x-pack/plugins/ml/server/routes/indices.ts index ee817c492dbd4e..8a5df1415bde8a 100644 --- a/x-pack/plugins/ml/server/routes/indices.ts +++ b/x-pack/plugins/ml/server/routes/indices.ts @@ -40,7 +40,7 @@ export function indicesRoutes({ router, mlLicense }: RouteInitialization) { requestFields !== undefined && Array.isArray(requestFields) ? requestFields.join(',') : '*'; - const { body } = await client.asInternalUser.fieldCaps({ index, fields }); + const { body } = await client.asCurrentUser.fieldCaps({ index, fields }); return response.ok({ body }); } catch (e) { return response.customError(wrapError(e)); diff --git a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts index 5f2de69689865b..d193330dc54ff4 100644 --- a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts @@ -47,7 +47,8 @@ const defaultHeadersInDefaultEcsCategory = [ { id: 'destination.ip' }, ]; -describe('Events Viewer', () => { +// https://github.com/elastic/kibana/issues/70757 +describe.skip('Events Viewer', () => { context('Fields rendering', () => { before(() => { loginAndWaitForPage(HOSTS_URL); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3e4916ae9caaea..dd0c13c01b1881 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9682,8 +9682,6 @@ "xpack.lens.pieChart.fitInsideOnlyLabel": "内部のみ", "xpack.lens.pieChart.hiddenNumbersLabel": "グラフから非表示", "xpack.lens.pieChart.labelPositionLabel": "ラベル位置", - "xpack.lens.pieChart.legendDisplayLabel": "凡例表示", - "xpack.lens.pieChart.legendDisplayLegend": "凡例表示", "xpack.lens.pieChart.nestedLegendLabel": "ネストされた凡例", "xpack.lens.pieChart.numberLabels": "ラベル値", "xpack.lens.pieChart.showCategoriesLabel": "内部または外部", @@ -9709,7 +9707,6 @@ "xpack.lens.xyChart.chartTypeLegend": "チャートタイプ", "xpack.lens.xyChart.fittingDisabledHelpText": "この設定は折れ線グラフとエリアグラフでのみ適用されます。", "xpack.lens.xyChart.fittingFunction.help": "欠測値の処理方法を定義", - "xpack.lens.xyChart.fittingLabel": "欠測値を埋める", "xpack.lens.xyChart.help": "X/Y チャート", "xpack.lens.xyChart.isVisible.help": "判例の表示・非表示を指定します。", "xpack.lens.xyChart.legend.help": "チャートの凡例を構成します。", @@ -9718,7 +9715,6 @@ "xpack.lens.xyChart.renderer.help": "X/Y チャートを再レンダリング", "xpack.lens.xyChart.seriesColor.auto": "自動", "xpack.lens.xyChart.seriesColor.label": "系列色", - "xpack.lens.xyChart.settingsLabel": "設定", "xpack.lens.xyChart.splitSeries": "系列を分割", "xpack.lens.xyChart.title.help": "軸のタイトル", "xpack.lens.xyChart.xAxisLabel": "X 軸", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e3f2070b21295f..5d15555d403bff 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9688,8 +9688,6 @@ "xpack.lens.pieChart.fitInsideOnlyLabel": "仅内部", "xpack.lens.pieChart.hiddenNumbersLabel": "在图表中隐藏", "xpack.lens.pieChart.labelPositionLabel": "标签位置", - "xpack.lens.pieChart.legendDisplayLabel": "图例显示", - "xpack.lens.pieChart.legendDisplayLegend": "图例显示", "xpack.lens.pieChart.nestedLegendLabel": "嵌套图例", "xpack.lens.pieChart.numberLabels": "标签值", "xpack.lens.pieChart.showCategoriesLabel": "内部或外部", @@ -9715,7 +9713,6 @@ "xpack.lens.xyChart.chartTypeLegend": "图表类型", "xpack.lens.xyChart.fittingDisabledHelpText": "此设置仅适用于折线图和非堆叠面积图。", "xpack.lens.xyChart.fittingFunction.help": "定义处理缺失值的方式", - "xpack.lens.xyChart.fittingLabel": "填充缺失值", "xpack.lens.xyChart.help": "X/Y 图表", "xpack.lens.xyChart.isVisible.help": "指定图例是否可见。", "xpack.lens.xyChart.legend.help": "配置图表图例。", @@ -9724,7 +9721,6 @@ "xpack.lens.xyChart.renderer.help": "X/Y 图表呈现器", "xpack.lens.xyChart.seriesColor.auto": "自动", "xpack.lens.xyChart.seriesColor.label": "系列颜色", - "xpack.lens.xyChart.settingsLabel": "设置", "xpack.lens.xyChart.splitSeries": "拆分序列", "xpack.lens.xyChart.title.help": "轴标题", "xpack.lens.xyChart.xAxisLabel": "X 轴", diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json index 083386480c540d..df61fde6518d0f 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json @@ -3,8 +3,8 @@ "version": "1.0.0", "kibanaVersion": "kibana", "configPath": ["xpack"], - "requiredPlugins": ["taskManager", "features", "actions", "alerts"], - "optionalPlugins": ["spaces"], + "requiredPlugins": ["taskManager", "features", "actions", "alerts", "encryptedSavedObjects"], + "optionalPlugins": ["security", "spaces"], "server": true, "ui": false } diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts index f9fc3d647abd31..525f99587672a1 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts @@ -12,11 +12,15 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../.. import { defineAlertTypes } from './alert_types'; import { defineActionTypes } from './action_types'; import { defineRoutes } from './routes'; +import { SpacesPluginSetup } from '../../../../../../../plugins/spaces/server'; +import { SecurityPluginSetup } from '../../../../../../../plugins/security/server'; export interface FixtureSetupDeps { features: FeaturesPluginSetup; actions: ActionsPluginSetup; alerts: AlertingPluginSetup; + spaces?: SpacesPluginSetup; + security?: SecurityPluginSetup; } export interface FixtureStartDeps { @@ -24,7 +28,10 @@ export interface FixtureStartDeps { } export class FixturePlugin implements Plugin { - public setup(core: CoreSetup, { features, actions, alerts }: FixtureSetupDeps) { + public setup( + core: CoreSetup, + { features, actions, alerts, spaces, security }: FixtureSetupDeps + ) { features.registerKibanaFeature({ id: 'alertsFixture', name: 'Alerts', @@ -97,7 +104,7 @@ export class FixturePlugin implements Plugin, + { spaces, security }: Partial +) { const router = core.http.createRouter(); + router.put( + { + path: '/api/alerts_fixture/{id}/replace_api_key', + validate: { + params: schema.object({ + id: schema.string(), + }), + body: schema.object({ + spaceId: schema.maybe(schema.string()), + }), + }, + }, + async ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> => { + const { id } = req.params; + + if (!security) { + return res.ok({ + body: {}, + }); + } + + const [{ savedObjects }, { encryptedSavedObjects }] = await core.getStartServices(); + const encryptedSavedObjectsWithAlerts = await encryptedSavedObjects.getClient({ + includedHiddenTypes: ['alert'], + }); + const savedObjectsWithAlerts = await savedObjects.getScopedClient(req, { + excludedWrappers: ['security', 'spaces'], + includedHiddenTypes: ['alert'], + }); + + let namespace: string | undefined; + if (spaces && req.body.spaceId) { + namespace = spaces.spacesService.spaceIdToNamespace(req.body.spaceId); + } + + const user = await security.authc.getCurrentUser(req); + if (!user) { + return res.internalError({}); + } + + // Create an API key using the new grant API - in this case the Kibana system user is creating the + // API key for the user, instead of having the user create it themselves, which requires api_key + // privileges + const createAPIKeyResult = await security.authc.grantAPIKeyAsInternalUser(req, { + name: `alert:migrated-to-7.10:${user.username}`, + role_descriptors: {}, + }); + + if (!createAPIKeyResult) { + return res.internalError({}); + } + + const result = await savedObjectsWithAlerts.update( + 'alert', + id, + { + ...( + await encryptedSavedObjectsWithAlerts.getDecryptedAsInternalUser( + 'alert', + id, + { + namespace, + } + ) + ).attributes, + apiKey: Buffer.from(`${createAPIKeyResult.id}:${createAPIKeyResult.api_key}`).toString( + 'base64' + ), + apiKeyOwner: user.username, + }, + { + namespace, + } + ); + return res.ok({ body: result }); + } + ); + router.put( { path: '/api/alerts_fixture/saved_object/{type}/{id}', @@ -54,4 +142,38 @@ export function defineRoutes(core: CoreSetup) { return res.ok({ body: result }); } ); + + router.put( + { + path: '/api/alerts_fixture/{id}/reschedule_task', + validate: { + params: schema.object({ + id: schema.string(), + }), + body: schema.object({ + runAt: schema.string(), + }), + }, + }, + async ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> => { + const { id } = req.params; + const { runAt } = req.body; + + const [{ savedObjects }] = await core.getStartServices(); + const savedObjectsWithTasksAndAlerts = await savedObjects.getScopedClient(req, { + includedHiddenTypes: ['task', 'alert'], + }); + const alert = await savedObjectsWithTasksAndAlerts.get('alert', id); + const result = await savedObjectsWithTasksAndAlerts.update( + 'task', + alert.attributes.scheduledTaskId!, + { runAt } + ); + return res.ok({ body: result }); + } + ); } 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 a9e5d0c832c8ce..797769fd64471c 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 @@ -8,6 +8,7 @@ import { Space, User } from '../types'; import { ObjectRemover } from './object_remover'; import { getUrlPrefix } from './space_test_utils'; import { ES_TEST_INDEX_NAME } from './es_test_index_tool'; +import { getTestAlertData } from './get_test_alert_data'; export interface AlertUtilsOpts { user?: User; @@ -23,6 +24,10 @@ export interface CreateAlertWithActionOpts { overwrites?: Record; reference: string; } +export interface CreateNoopAlertOpts { + objectRemover?: ObjectRemover; + overwrites?: Record; +} interface UpdateAlwaysFiringAction { alertId: string; @@ -265,6 +270,39 @@ export class AlertUtils { } return response; } + + public replaceApiKeys(id: string) { + let request = this.supertestWithoutAuth + .put(`/api/alerts_fixture/${id}/replace_api_key`) + .set('kbn-xsrf', 'foo'); + if (this.user) { + request = request.auth(this.user.username, this.user.password); + } + return request.send({ spaceId: this.space.id }); + } + + public async createNoopAlert({ objectRemover, overwrites = {} }: CreateNoopAlertOpts) { + const objRemover = objectRemover || this.objectRemover; + + if (!objRemover) { + throw new Error('objectRemover is required'); + } + + let request = this.supertestWithoutAuth + .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo'); + if (this.user) { + request = request.auth(this.user.username, this.user.password); + } + const response = await request.send({ + ...getTestAlertData(), + ...overwrites, + }); + if (response.statusCode === 200) { + objRemover.add(this.space.id, response.body.id, 'alert', 'alerts'); + } + return response; + } } export function getConsumerUnauthorizedErrorMessage( diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts index b03a3c8ccf6af9..fa0130780cb692 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts @@ -25,5 +25,8 @@ export default function alertingTests({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./update')); loadTestFile(require.resolve('./update_api_key')); loadTestFile(require.resolve('./alerts')); + + // note that this test will destroy existing spaces + loadTestFile(require.resolve('./rbac_legacy')); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts new file mode 100644 index 00000000000000..513b7fc4490657 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts @@ -0,0 +1,229 @@ +/* + * 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'; +import { UserAtSpaceScenarios, Superuser } from '../../scenarios'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { ESTestIndexTool, getUrlPrefix, ObjectRemover, AlertUtils } from '../../../common/lib'; +import { setupSpacesAndUsers } from '..'; + +// eslint-disable-next-line import/no-default-export +export default function alertTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('legacyEs'); + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const securityService = getService('security'); + const spacesService = getService('spaces'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esTestIndexTool = new ESTestIndexTool(es, retry); + + const MIGRATED_ACTION_ID = '17f38826-5a8d-4a76-975a-b496e7fffe0b'; + const MIGRATED_ALERT_ID: Record = { + space_1_all_alerts_none_actions: '6ee9630a-a20e-44af-9465-217a3717d2ab', + space_1_all_with_restricted_fixture: '5cc59319-74ee-4edc-8646-a79ea91067cd', + space_1_all: 'd41a6abb-b93b-46df-a80a-926221ea847c', + global_read: '362e362b-a137-4aa2-9434-43e3d0d84a34', + superuser: 'b384be60-ec53-4b26-857e-0253ee55b277', + }; + + describe('alerts', () => { + const authorizationIndex = '.kibana-test-authorization'; + const objectRemover = new ObjectRemover(supertest); + + before(async () => { + await esTestIndexTool.destroy(); + await esArchiver.load('alerts_legacy'); + await esTestIndexTool.setup(); + await es.indices.create({ index: authorizationIndex }); + await setupSpacesAndUsers(spacesService, securityService); + }); + + after(async () => { + await esTestIndexTool.destroy(); + await es.indices.delete({ index: authorizationIndex }); + await esArchiver.unload('alerts_legacy'); + }); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + + describe(scenario.id, () => { + let alertUtils: AlertUtils; + + before(async () => { + alertUtils = new AlertUtils({ + user, + space, + supertestWithoutAuth, + indexRecordActionId: MIGRATED_ACTION_ID, + objectRemover, + }); + }); + + it('should schedule actions on legacy alerts', async () => { + const reference = `alert:migrated-to-7.10:${user.username}`; + const migratedAlertId = MIGRATED_ALERT_ID[user.username]; + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + // These cases are not relevant as we're testing the migration of alerts which + // were valid pre 7.10.0 and which become invalid after the introduction of RBAC in 7.10.0 + // these cases were invalid pre 7.10.0 and remain invalid post 7.10.0 + break; + case 'space_1_all at space1': + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + await ensureLegacyAlertHasBeenMigrated(migratedAlertId); + + await updateMigratedAlertToUseApiKeyOfCurrentUser(migratedAlertId); + + await ensureAlertIsRunning(); + + await updateAlertSoThatItIsNoLongerLegacy(migratedAlertId); + + // update alert as user with privileges - so it is no longer a legacy alert + const updatedKeyResponse = await alertUtils.getUpdateApiKeyRequest(migratedAlertId); + expect(updatedKeyResponse.statusCode).to.eql(204); + + await ensureAlertIsRunning(); + break; + case 'global_read at space1': + await ensureLegacyAlertHasBeenMigrated(migratedAlertId); + + await updateMigratedAlertToUseApiKeyOfCurrentUser(migratedAlertId); + + await ensureAlertIsRunning(); + + await updateAlertSoThatItIsNoLongerLegacy(migratedAlertId); + + // attempt to update alert as user with no Alerts privileges - as it is no longer a legacy alert + // this should fail, as the user doesn't have the `updateApiKey` privilege for Alerts + const failedUpdateKeyDueToAlertsPrivilegesResponse = await alertUtils.getUpdateApiKeyRequest( + migratedAlertId + ); + + expect(failedUpdateKeyDueToAlertsPrivilegesResponse.statusCode).to.eql(403); + expect(failedUpdateKeyDueToAlertsPrivilegesResponse.body).to.eql({ + error: 'Forbidden', + message: + 'Unauthorized to updateApiKey a "test.always-firing" alert for "alertsFixture"', + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + await ensureLegacyAlertHasBeenMigrated(migratedAlertId); + + await updateMigratedAlertToUseApiKeyOfCurrentUser(migratedAlertId); + + await ensureAlertIsRunning(); + + await updateAlertSoThatItIsNoLongerLegacy(migratedAlertId); + + // attempt to update alert as user with no Actions privileges - as it is no longer a legacy alert + // this should fail, as the user doesn't have the `execute` privilege for Actions + const failedUpdateKeyDueToActionsPrivilegesResponse = await alertUtils.getUpdateApiKeyRequest( + migratedAlertId + ); + + expect(failedUpdateKeyDueToActionsPrivilegesResponse.statusCode).to.eql(403); + expect(failedUpdateKeyDueToActionsPrivilegesResponse.body).to.eql({ + error: 'Forbidden', + message: 'Unauthorized to execute actions', + statusCode: 403, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + + async function ensureLegacyAlertHasBeenMigrated(alertId: string) { + const getResponse = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${alertId}`) + .auth(user.username, user.password); + expect(getResponse.status).to.eql(200); + } + + async function updateMigratedAlertToUseApiKeyOfCurrentUser(alertId: string) { + // swap out api key to run as the current user + const swapResponse = await alertUtils.replaceApiKeys(alertId); + expect(swapResponse.statusCode).to.eql(200); + // ensure the alert is till marked as legacy despite the update of the Api key + // this is important as proper update *should* update the legacy status of the alert + // and we want to ensure we don't accidentally introduce a change that might break our support of legacy alerts + expect(swapResponse.body.id).to.eql(alertId); + expect(swapResponse.body.attributes.meta.versionApiKeyLastmodified).to.eql( + 'pre-7.10.0' + ); + + // loading the archive likely caused the task to fail so ensure it's rescheduled to run in 2 seconds, + // otherwise this test will stall for 5 minutes + // no other attributes are touched, only runAt, so unless it would have ran when runAt expired, it + // won't run now + await supertest + .put(`${getUrlPrefix(space.id)}/api/alerts_fixture/${alertId}/reschedule_task`) + .set('kbn-xsrf', 'foo') + .send({ + runAt: getRunAt(2000), + }) + .expect(200); + } + + async function ensureAlertIsRunning() { + // ensure the alert still runs and that it can schedule actions + const numberOfAlertExecutions = ( + await esTestIndexTool.search('alert:test.always-firing', reference) + ).hits.total.value; + + const numberOfActionExecutions = ( + await esTestIndexTool.search('action:test.index-record', reference) + ).hits.total.value; + + // wait for alert to execute and for its action to be scheduled and run + await retry.try(async () => { + const alertSearchResult = await esTestIndexTool.search( + 'alert:test.always-firing', + reference + ); + + const actionSearchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + + expect(alertSearchResult.hits.total.value).to.be.greaterThan(numberOfAlertExecutions); + expect(actionSearchResult.hits.total.value).to.be.greaterThan( + numberOfActionExecutions + ); + }); + } + + async function updateAlertSoThatItIsNoLongerLegacy(alertId: string) { + // update the alert as super user (to avoid privilege limitations) so that it is no longer a legacy alert + await alertUtils.updateAlwaysFiringAction({ + alertId, + actionId: MIGRATED_ACTION_ID, + user: Superuser, + reference, + overwrites: { + name: 'Updated Alert', + schedule: { interval: '2s' }, + throttle: '2s', + }, + }); + } + }); + }); + } + }); +} + +function getRunAt(delayInMs: number) { + const runAt = new Date(); + runAt.setMilliseconds(new Date().getMilliseconds() + delayInMs); + return runAt.toISOString(); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts index f544ef2d771ebb..7daa223dc2d436 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts @@ -8,6 +8,47 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; import { isCustomRoleSpecification } from '../../common/types'; import { Spaces, Users } from '../scenarios'; +export async function setupSpacesAndUsers( + spacesService: ReturnType, + securityService: ReturnType +) { + for (const space of Spaces) { + await spacesService.create(space); + } + + for (const user of Users) { + const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; + + await securityService.user.create(user.username, { + password: user.password, + full_name: user.fullName, + roles: roles.map((role) => role.name), + }); + + for (const role of roles) { + if (isCustomRoleSpecification(role)) { + await securityService.role.create(role.name, { + kibana: role.kibana, + elasticsearch: role.elasticsearch, + }); + } + } + } +} + +export async function tearDownUsers(securityService: ReturnType) { + for (const user of Users) { + await securityService.user.delete(user.username); + + const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; + for (const role of roles) { + if (isCustomRoleSpecification(role)) { + await securityService.role.delete(role.name); + } + } + } +} + // eslint-disable-next-line import/no-default-export export default function alertingApiIntegrationTests({ loadTestFile, @@ -21,41 +62,11 @@ export default function alertingApiIntegrationTests({ this.tags('ciGroup5'); before(async () => { - for (const space of Spaces) { - await spacesService.create(space); - } - - for (const user of Users) { - const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; - - await securityService.user.create(user.username, { - password: user.password, - full_name: user.fullName, - roles: roles.map((role) => role.name), - }); - - for (const role of roles) { - if (isCustomRoleSpecification(role)) { - await securityService.role.create(role.name, { - kibana: role.kibana, - elasticsearch: role.elasticsearch, - }); - } - } - } + await setupSpacesAndUsers(spacesService, securityService); }); after(async () => { - for (const user of Users) { - await securityService.user.delete(user.username); - - const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; - for (const role of roles) { - if (isCustomRoleSpecification(role)) { - await securityService.role.delete(role.name); - } - } - } + await tearDownUsers(securityService); await esArchiver.unload('empty_kibana'); }); diff --git a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_8.0.0/data.json.gz b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_8.0.0/data.json.gz index ad3f2351ed30af..35b2243cf06049 100644 Binary files a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_8.0.0/data.json.gz and b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_8.0.0/data.json.gz differ diff --git a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_8.0.0/mappings.json b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_8.0.0/mappings.json index 48ac74d97dfa76..2867292828e8e9 100644 --- a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_8.0.0/mappings.json +++ b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_8.0.0/mappings.json @@ -2,589 +2,7841 @@ "type": "index", "value": { "aliases": { + "apm-8.0.0-span": { + "is_write_index": true + } }, - "index": "apm-8.0.0-transaction-005", + "index": "apm-8.0.0-span-000001", "mappings": { + "_meta": { + "beat": "apm", + "version": "8.0.0" + }, + "date_detection": false, + "dynamic_templates": [ + { + "labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "container.labels.*" + } + }, + { + "dns.answers": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "dns.answers.*" + } + }, + { + "log.syslog": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "log.syslog.*" + } + }, + { + "network.inner": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "network.inner.*" + } + }, + { + "observer.egress": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "observer.egress.*" + } + }, + { + "observer.ingress": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "observer.ingress.*" + } + }, + { + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, + { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, + { + "kubernetes.labels.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.labels.*" + } + }, + { + "kubernetes.annotations.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.annotations.*" + } + }, + { + "labels_string": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "labels_boolean": { + "mapping": { + "type": "boolean" + }, + "match_mapping_type": "boolean", + "path_match": "labels.*" + } + }, + { + "labels_*": { + "mapping": { + "scaling_factor": 1000000, + "type": "scaled_float" + }, + "path_match": "labels.*" + } + }, + { + "transaction.marks": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "transaction.marks.*" + } + }, + { + "transaction.marks.*.*": { + "mapping": { + "scaling_factor": 1000000, + "type": "scaled_float" + }, + "path_match": "transaction.marks.*.*" + } + }, + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], "properties": { "@timestamp": { "type": "date" }, "agent": { + "dynamic": "false", "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "path": "agent.name", + "type": "alias" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, "name": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" }, "version": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" } } }, - "client": { + "as": { "properties": { - "geo": { + "number": { + "type": "long" + }, + "organization": { "properties": { - "continent_name": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "country_iso_code": { + "name": { "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" + "text": { + "norms": false, + "type": "text" } }, - "type": "text" - }, - "location": { - "properties": { - "lat": { - "type": "float" - }, - "lon": { - "type": "float" - } - } - } - } - }, - "ip": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "ecs": { - "properties": { - "version": { - "fields": { - "keyword": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } } } }, - "event": { + "child": { + "dynamic": "false", "properties": { - "ingested": { - "type": "date" + "id": { + "ignore_above": 1024, + "type": "keyword" } } }, - "http": { + "client": { + "dynamic": "false", "properties": { - "request": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { "properties": { - "referrer": { - "fields": { - "keyword": { - "ignore_above": 256, + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } } } }, - "response": { + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { "properties": { - "decoded_body_size": { - "type": "long" + "city_name": { + "ignore_above": 1024, + "type": "keyword" }, - "encoded_body_size": { - "type": "long" + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" }, - "transfer_size": { + "port": { "type": "long" } } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } } } }, - "observer": { + "cloud": { "properties": { - "ephemeral_id": { - "fields": { - "keyword": { - "ignore_above": 256, + "account": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } }, - "hostname": { - "fields": { - "keyword": { - "ignore_above": 256, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "id": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } }, - "id": { - "fields": { - "keyword": { - "ignore_above": 256, + "instance": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } }, - "type": { - "fields": { - "keyword": { - "ignore_above": 256, + "machine": { + "dynamic": "false", + "properties": { + "type": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } }, - "version": { - "fields": { - "keyword": { - "ignore_above": 256, + "project": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } }, - "version_major": { - "type": "long" + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" } } }, - "processor": { + "code_signature": { "properties": { - "event": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "exists": { + "type": "boolean" }, - "name": { - "fields": { - "keyword": { - "ignore_above": 256, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "container": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" } } }, - "service": { + "destination": { "properties": { - "language": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "dynamic": "false", + "properties": { + "response_time": { + "properties": { + "count": { + "type": "long" + }, + "sum": { + "properties": { + "us": { + "type": "long" + } + } + } + } + } + } + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { "fields": { - "keyword": { - "ignore_above": 256, + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, "type": "keyword" } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dll": { + "properties": { + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" } } }, "name": { - "fields": { - "keyword": { - "ignore_above": 256, + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "labels": { + "type": "object" + } + } + } + } + }, + "ecs": { + "properties": { "version": { - "fields": { - "keyword": { - "ignore_above": 256, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "dynamic": "false", + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "culprit": { + "ignore_above": 1024, + "type": "keyword" + }, + "exception": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "handled": { + "type": "boolean" + }, + "message": { + "norms": false, + "type": "text" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, "type": "keyword" } - }, + } + }, + "grouping_key": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "param_message": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message": { + "norms": false, "type": "text" + }, + "stack_trace": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" } } }, - "source": { + "event": { "properties": { - "ip": { - "fields": { - "keyword": { - "ignore_above": 256, + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "experimental": { + "dynamic": "true", + "type": "object" + }, + "fields": { + "type": "object" + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "dynamic": "false", + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "containerized": { + "type": "boolean" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "build": { + "ignore_above": 1024, + "type": "keyword" + }, + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "dynamic": "false", + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "headers": { + "enabled": false, + "type": "object" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "finished": { + "type": "boolean" + }, + "headers": { + "enabled": false, + "type": "object" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kubernetes": { + "dynamic": "false", + "properties": { + "annotations": { + "properties": { + "*": { + "type": "object" + } + } + }, + "container": { + "properties": { + "image": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "deployment": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "properties": { + "*": { + "type": "object" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pod": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "replicaset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "statefulset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "labels": { + "dynamic": "true", + "properties": { + "git_rev": { + "type": "keyword" + }, + "kibana_uuid": { + "type": "keyword" + } + } + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "metricset": { + "properties": { + "period": { + "type": "long" + } + } + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "inner": { + "properties": { + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "observer": { + "dynamic": "false", + "properties": { + "egress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "listening": { + "ignore_above": 1024, + "type": "keyword" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_major": { + "type": "byte" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "parent": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "dynamic": "false", + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "processor": { + "properties": { + "event": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "profile": { + "dynamic": "false", + "properties": { + "alloc_objects": { + "properties": { + "count": { + "type": "long" + } + } + }, + "alloc_space": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "cpu": { + "properties": { + "ns": { + "type": "long" + } + } + }, + "duration": { + "type": "long" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "inuse_objects": { + "properties": { + "count": { + "type": "long" + } + } + }, + "inuse_space": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "samples": { + "properties": { + "count": { + "type": "long" + } + } + }, + "stack": { + "dynamic": "false", + "properties": { + "filename": { + "ignore_above": 1024, + "type": "keyword" + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "line": { + "type": "long" + } + } + }, + "top": { + "dynamic": "false", + "properties": { + "filename": { + "ignore_above": 1024, + "type": "keyword" + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "line": { + "type": "long" + } + } + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "author": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "dynamic": "false", + "properties": { + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "framework": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "language": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "runtime": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "dynamic": "false", + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "sourcemap": { + "dynamic": "false", + "properties": { + "bundle_filepath": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "span": { + "dynamic": "false", + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "db": { + "dynamic": "false", + "properties": { + "link": { + "ignore_above": 1024, + "type": "keyword" + }, + "rows_affected": { + "type": "long" + } + } + }, + "destination": { + "dynamic": "false", + "properties": { + "service": { + "dynamic": "false", + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "duration": { + "properties": { + "us": { + "type": "long" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "dynamic": "false", + "properties": { + "age": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "queue": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "self_time": { + "properties": { + "count": { + "type": "long" + }, + "sum": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "start": { + "properties": { + "us": { + "type": "long" + } + } + }, + "subtype": { + "ignore_above": 1024, + "type": "keyword" + }, + "sync": { + "type": "boolean" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "system": { + "properties": { + "cpu": { + "properties": { + "total": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + } + } + }, + "memory": { + "properties": { + "actual": { + "properties": { + "free": { + "type": "long" + } + } + }, + "total": { + "type": "long" + } + } + }, + "process": { + "properties": { + "cgroup": { + "properties": { + "memory": { + "properties": { + "mem": { + "properties": { + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "stats": { + "properties": { + "inactive_file": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "cpu": { + "properties": { + "total": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + } + } + }, + "memory": { + "properties": { + "rss": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "size": { + "type": "long" + } + } + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "timeseries": { + "properties": { + "instance": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timestamp": { + "properties": { + "us": { + "type": "long" + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "trace": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tracing": { + "properties": { + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "transaction": { + "dynamic": "false", + "properties": { + "breakdown": { + "properties": { + "count": { + "type": "long" + } + } + }, + "duration": { + "properties": { + "count": { + "type": "long" + }, + "histogram": { + "type": "histogram" + }, + "sum": { + "properties": { + "us": { + "type": "long" + } + } + }, + "us": { + "type": "long" + } + } + }, + "experience": { + "properties": { + "cls": { + "scaling_factor": 1000000, + "type": "scaled_float" + }, + "fid": { + "scaling_factor": 1000000, + "type": "scaled_float" + }, + "tbt": { + "scaling_factor": 1000000, + "type": "scaled_float" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "marks": { + "dynamic": "true", + "properties": { + "*": { + "properties": { + "*": { + "dynamic": "true", + "type": "object" + } + } + } + } + }, + "message": { + "dynamic": "false", + "properties": { + "age": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "queue": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "result": { + "ignore_above": 1024, + "type": "keyword" + }, + "root": { + "type": "boolean" + }, + "sampled": { + "type": "boolean" + }, + "self_time": { + "properties": { + "count": { + "type": "long" + }, + "sum": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "span_count": { + "properties": { + "dropped": { + "type": "long" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "dynamic": "false", + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "dynamic": "false", + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "dynamic": "false", + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "view spans": { + "ignore_above": 1024, + "type": "keyword" + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "codec": "best_compression", + "lifecycle": { + "name": "apm-rollover-30-days", + "rollover_alias": "apm-8.0.0-span" + }, + "mapping": { + "total_fields": { + "limit": "2000" + } + }, + "max_docvalue_fields_search": "200", + "number_of_replicas": "0", + "number_of_shards": "1", + "priority": "100", + "refresh_interval": "1ms" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + "apm-8.0.0-transaction": { + "is_write_index": true + } + }, + "index": "apm-8.0.0-transaction-000001", + "mappings": { + "_meta": { + "beat": "apm", + "version": "8.0.0" + }, + "date_detection": false, + "dynamic_templates": [ + { + "labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "container.labels.*" + } + }, + { + "dns.answers": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "dns.answers.*" + } + }, + { + "log.syslog": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "log.syslog.*" + } + }, + { + "network.inner": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "network.inner.*" + } + }, + { + "observer.egress": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "observer.egress.*" + } + }, + { + "observer.ingress": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "observer.ingress.*" + } + }, + { + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, + { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, + { + "kubernetes.labels.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.labels.*" + } + }, + { + "kubernetes.annotations.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.annotations.*" + } + }, + { + "labels_string": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "labels_boolean": { + "mapping": { + "type": "boolean" + }, + "match_mapping_type": "boolean", + "path_match": "labels.*" + } + }, + { + "labels_*": { + "mapping": { + "scaling_factor": 1000000, + "type": "scaled_float" + }, + "path_match": "labels.*" + } + }, + { + "transaction.marks": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "transaction.marks.*" + } + }, + { + "transaction.marks.*.*": { + "mapping": { + "scaling_factor": 1000000, + "type": "scaled_float" + }, + "path_match": "transaction.marks.*.*" + } + }, + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "dynamic": "false", + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "path": "agent.name", + "type": "alias" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "child": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "client": { + "dynamic": "false", + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "instance": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "dynamic": "false", + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "container": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "dynamic": "false", + "properties": { + "response_time": { + "properties": { + "count": { + "type": "long" + }, + "sum": { + "properties": { + "us": { + "type": "long" + } + } + } + } + } + } + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dll": { + "properties": { + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "labels": { + "type": "object" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "dynamic": "false", + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "culprit": { + "ignore_above": 1024, + "type": "keyword" + }, + "exception": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "handled": { + "type": "boolean" + }, + "message": { + "norms": false, + "type": "text" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "grouping_key": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "param_message": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "experimental": { + "dynamic": "true", + "type": "object" + }, + "fields": { + "type": "object" + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "dynamic": "false", + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "containerized": { + "type": "boolean" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "build": { + "ignore_above": 1024, + "type": "keyword" + }, + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "dynamic": "false", + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "headers": { + "enabled": false, + "type": "object" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "finished": { + "type": "boolean" + }, + "headers": { + "enabled": false, + "type": "object" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kubernetes": { + "dynamic": "false", + "properties": { + "annotations": { + "properties": { + "*": { + "type": "object" + } + } + }, + "container": { + "properties": { + "image": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "deployment": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "properties": { + "*": { + "type": "object" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pod": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "replicaset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "statefulset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "labels": { + "dynamic": "true", + "properties": { + "foo": { + "type": "keyword" + }, + "git_rev": { + "type": "keyword" + }, + "kibana_uuid": { + "type": "keyword" + }, + "lorem": { + "type": "keyword" + }, + "multi-line": { + "type": "keyword" + }, + "this-is-a-very-long-tag-name-without-any-spaces": { + "type": "keyword" + } + } + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "metricset": { + "properties": { + "period": { + "type": "long" + } + } + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "inner": { + "properties": { + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "observer": { + "dynamic": "false", + "properties": { + "egress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "listening": { + "ignore_above": 1024, + "type": "keyword" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_major": { + "type": "byte" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "parent": { + "dynamic": "false", + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "dynamic": "false", + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "processor": { + "properties": { + "event": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "profile": { + "dynamic": "false", + "properties": { + "alloc_objects": { + "properties": { + "count": { + "type": "long" + } + } + }, + "alloc_space": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "cpu": { + "properties": { + "ns": { + "type": "long" + } + } + }, + "duration": { + "type": "long" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "inuse_objects": { + "properties": { + "count": { + "type": "long" + } + } + }, + "inuse_space": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "samples": { + "properties": { + "count": { + "type": "long" + } + } + }, + "stack": { + "dynamic": "false", + "properties": { + "filename": { + "ignore_above": 1024, + "type": "keyword" + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "line": { + "type": "long" + } + } + }, + "top": { + "dynamic": "false", + "properties": { + "filename": { + "ignore_above": 1024, + "type": "keyword" + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "line": { + "type": "long" + } + } + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "author": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "dynamic": "false", + "properties": { + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "framework": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "language": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "runtime": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "dynamic": "false", + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "sourcemap": { + "dynamic": "false", + "properties": { + "bundle_filepath": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "span": { + "dynamic": "false", + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "db": { + "dynamic": "false", + "properties": { + "link": { + "ignore_above": 1024, + "type": "keyword" + }, + "rows_affected": { + "type": "long" + } + } + }, + "destination": { + "dynamic": "false", + "properties": { + "service": { + "dynamic": "false", + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "duration": { + "properties": { + "us": { + "type": "long" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "dynamic": "false", + "properties": { + "age": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "queue": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "self_time": { + "properties": { + "count": { + "type": "long" + }, + "sum": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "start": { + "properties": { + "us": { + "type": "long" + } + } + }, + "subtype": { + "ignore_above": 1024, + "type": "keyword" + }, + "sync": { + "type": "boolean" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "system": { + "properties": { + "cpu": { + "properties": { + "total": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + } + } + }, + "memory": { + "properties": { + "actual": { + "properties": { + "free": { + "type": "long" + } + } + }, + "total": { + "type": "long" + } + } + }, + "process": { + "properties": { + "cgroup": { + "properties": { + "memory": { + "properties": { + "mem": { + "properties": { + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "stats": { + "properties": { + "inactive_file": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "cpu": { + "properties": { + "total": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + } + } + }, + "memory": { + "properties": { + "rss": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "size": { + "type": "long" + } + } + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "timeseries": { + "properties": { + "instance": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timestamp": { + "properties": { + "us": { + "type": "long" + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" } } }, - "timestamp": { + "trace": { + "dynamic": "false", "properties": { - "us": { - "type": "long" + "id": { + "ignore_above": 1024, + "type": "keyword" } } }, - "trace": { + "tracing": { "properties": { - "id": { - "fields": { - "keyword": { - "ignore_above": 256, + "trace": { + "properties": { + "id": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } } } }, "transaction": { + "dynamic": "false", "properties": { - "custom": { + "breakdown": { "properties": { - "userConfig": { - "properties": { - "featureFlags": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "showDashboard": { - "type": "boolean" - } - } + "count": { + "type": "long" } } }, "duration": { "properties": { + "count": { + "type": "long" + }, + "histogram": { + "type": "histogram" + }, + "sum": { + "properties": { + "us": { + "type": "long" + } + } + }, "us": { "type": "long" } } }, - "id": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" + "experience": { + "properties": { + "cls": { + "scaling_factor": 1000000, + "type": "scaled_float" + }, + "fid": { + "scaling_factor": 1000000, + "type": "scaled_float" + }, + "tbt": { + "scaling_factor": 1000000, + "type": "scaled_float" } - }, - "type": "text" + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" }, "marks": { + "dynamic": "true", "properties": { + "*": { + "properties": { + "*": { + "dynamic": "true", + "type": "object" + } + } + }, "agent": { "properties": { "domComplete": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "domInteractive": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "firstContentfulPaint": { - "type": "float" + "scaling_factor": 1000000, + "type": "scaled_float" }, "largestContentfulPaint": { - "type": "float" + "scaling_factor": 1000000, + "type": "scaled_float" }, "timeToFirstByte": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" } } }, "navigationTiming": { "properties": { "connectEnd": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "connectStart": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "domComplete": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "domContentLoadedEventEnd": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "domContentLoadedEventStart": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "domInteractive": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "domLoading": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "domainLookupEnd": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "domainLookupStart": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "fetchStart": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "loadEventEnd": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "loadEventStart": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "requestStart": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "responseEnd": { - "type": "long" + "scaling_factor": 1000000, + "type": "scaled_float" }, "responseStart": { + "scaling_factor": 1000000, + "type": "scaled_float" + } + } + } + } + }, + "message": { + "dynamic": "false", + "properties": { + "age": { + "properties": { + "ms": { "type": "long" } } + }, + "queue": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } } } }, "name": { "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" + "text": { + "norms": false, + "type": "text" } }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" + }, + "result": { + "ignore_above": 1024, + "type": "keyword" }, - "page": { + "root": { + "type": "boolean" + }, + "sampled": { + "type": "boolean" + }, + "self_time": { "properties": { - "referer": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "count": { + "type": "long" }, - "url": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" + "sum": { + "properties": { + "us": { + "type": "long" } - }, - "type": "text" + } } } }, - "sampled": { - "type": "boolean" - }, "span_count": { "properties": { - "started": { + "dropped": { "type": "long" } } }, "type": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" } } }, "url": { + "dynamic": "false", "properties": { "domain": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" }, "full": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" }, "original": { "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" + "text": { + "norms": false, + "type": "text" } }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" }, "path": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" }, "port": { "type": "long" }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, "scheme": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" } } }, "user": { + "dynamic": "false", "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" + "text": { + "norms": false, + "type": "text" } }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" }, - "id": { - "fields": { - "keyword": { - "ignore_above": 256, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, "type": "keyword" } - }, - "type": "text" + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" }, "name": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" } } }, "user_agent": { + "dynamic": "false", "properties": { "device": { "properties": { "name": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" } } }, "name": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" }, "original": { "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" + "text": { + "norms": false, + "type": "text" } }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" }, "os": { "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, "full": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" }, "name": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" }, "version": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" } } }, "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "view spans": { + "ignore_above": 1024, + "type": "keyword" + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" + "text": { + "norms": false, + "type": "text" } }, - "type": "text" + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" } } } @@ -592,9 +7844,22 @@ }, "settings": { "index": { - "number_of_replicas": "1", - "number_of_shards": "1" + "codec": "best_compression", + "lifecycle": { + "name": "apm-rollover-30-days", + "rollover_alias": "apm-8.0.0-transaction" + }, + "mapping": { + "total_fields": { + "limit": "2000" + } + }, + "max_docvalue_fields_search": "200", + "number_of_replicas": "0", + "number_of_shards": "1", + "priority": "100", + "refresh_interval": "1ms" } } } -} +} \ No newline at end of file diff --git a/x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts b/x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts similarity index 89% rename from x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts rename to x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts index 088488bc143fda..6235e7abd37ec5 100644 --- a/x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts @@ -12,7 +12,7 @@ export default function rumServicesApiTests({ getService }: FtrProviderContext) const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - describe('RUM Services', () => { + describe('CSM Services', () => { describe('when there is no data', () => { it('returns empty list', async () => { const response = await supertest.get( @@ -41,12 +41,7 @@ export default function rumServicesApiTests({ getService }: FtrProviderContext) expect(response.status).to.be(200); - expectSnapshot(response.body).toMatchInline(` - Array [ - "client", - "opbean-client-rum", - ] - `); + expectSnapshot(response.body).toMatchInline(`Array []`); }); }); }); diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts b/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts new file mode 100644 index 00000000000000..425268264612ff --- /dev/null +++ b/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts @@ -0,0 +1,58 @@ +/* + * 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'; +import { expectSnapshot } from '../../../common/match_snapshot'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + describe('CSM long task metrics', () => { + describe('when there is no data', () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/long-task-metrics?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' + ); + + expect(response.status).to.be(200); + expect(response.body).to.eql({ + longestLongTask: 0, + noOfLongTasks: 0, + sumOfLongTasks: 0, + }); + }); + }); + + describe('when there is data', () => { + before(async () => { + await esArchiver.load('8.0.0'); + await esArchiver.load('rum_8.0.0'); + }); + after(async () => { + await esArchiver.unload('8.0.0'); + await esArchiver.unload('rum_8.0.0'); + }); + + it('returns web core vitals values', async () => { + const response = await supertest.get( + '/api/apm/rum-client/long-task-metrics?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatchInline(` + Object { + "longestLongTask": 109000, + "noOfLongTasks": 2, + "sumOfLongTasks": 168000, + } + `); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts b/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts new file mode 100644 index 00000000000000..654386cb9987b1 --- /dev/null +++ b/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts @@ -0,0 +1,80 @@ +/* + * 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'; +import { expectSnapshot } from '../../../common/match_snapshot'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + describe('CSM web core vitals', () => { + describe('when there is no data', () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/web-core-vitals?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' + ); + + expect(response.status).to.be(200); + expect(response.body).to.eql({ + cls: '0', + fid: '0.00', + lcp: '0.00', + tbt: '0.00', + fcp: 0, + lcpRanks: [0, 0, 100], + fidRanks: [0, 0, 100], + clsRanks: [0, 0, 100], + }); + }); + }); + + describe('when there is data', () => { + before(async () => { + await esArchiver.load('8.0.0'); + await esArchiver.load('rum_8.0.0'); + }); + after(async () => { + await esArchiver.unload('8.0.0'); + await esArchiver.unload('rum_8.0.0'); + }); + + it('returns web core vitals values', async () => { + const response = await supertest.get( + '/api/apm/rum-client/web-core-vitals?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatchInline(` + Object { + "cls": "0", + "clsRanks": Array [ + 100, + 0, + 0, + ], + "fcp": 1072, + "fid": "1.35", + "fidRanks": Array [ + 0, + 0, + 100, + ], + "lcp": "1.27", + "lcpRanks": Array [ + 100, + 0, + 0, + ], + "tbt": "0.00", + } + `); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/trial/tests/index.ts b/x-pack/test/apm_api_integration/trial/tests/index.ts index c5ca086b5f3707..bf32c4661afd5f 100644 --- a/x-pack/test/apm_api_integration/trial/tests/index.ts +++ b/x-pack/test/apm_api_integration/trial/tests/index.ts @@ -15,7 +15,6 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr describe('Services', function () { loadTestFile(require.resolve('./services/annotations')); - loadTestFile(require.resolve('./services/rum_services.ts')); loadTestFile(require.resolve('./services/top_services.ts')); }); @@ -30,5 +29,11 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr describe('Service Maps', function () { loadTestFile(require.resolve('./service_maps/service_maps')); }); + + describe('CSM', function () { + loadTestFile(require.resolve('./csm/csm_services.ts')); + loadTestFile(require.resolve('./csm/web_core_vitals.ts')); + loadTestFile(require.resolve('./csm/long_task_metrics.ts')); + }); }); } diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap b/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap index 2306ca16dd71d6..1424ca42539c08 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap +++ b/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap @@ -1,5 +1,68 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Service Maps with a trial license /api/apm/service-map when there is data returns service map elements filtering by environment not defined 1`] = ` +Object { + "elements": Array [ + Object { + "data": Object { + "agent.name": "nodejs", + "id": "opbeans-node", + "service.environment": "ENVIRONMENT_NOT_DEFINED", + "service.name": "opbeans-node", + }, + }, + Object { + "data": Object { + "agent.name": "python", + "id": "opbeans-python", + "service.environment": "ENVIRONMENT_NOT_DEFINED", + "service.name": "opbeans-python", + }, + }, + Object { + "data": Object { + "agent.name": "ruby", + "id": "opbeans-ruby", + "service.environment": "ENVIRONMENT_NOT_DEFINED", + "service.name": "opbeans-ruby", + }, + }, + Object { + "data": Object { + "agent.name": "java", + "id": "opbeans-java", + "service.environment": "ENVIRONMENT_NOT_DEFINED", + "service.name": "opbeans-java", + }, + }, + Object { + "data": Object { + "agent.name": "go", + "id": "opbeans-go", + "service.environment": "ENVIRONMENT_NOT_DEFINED", + "service.name": "opbeans-go", + }, + }, + Object { + "data": Object { + "agent.name": "rum-js", + "id": "opbeans-rum", + "service.environment": "ENVIRONMENT_NOT_DEFINED", + "service.name": "opbeans-rum", + }, + }, + Object { + "data": Object { + "agent.name": "dotnet", + "id": "opbeans-dotnet", + "service.environment": "ENVIRONMENT_NOT_DEFINED", + "service.name": "opbeans-dotnet", + }, + }, + ], +} +`; + exports[`Service Maps with a trial license /api/apm/service-map when there is data returns the correct data 3`] = ` Array [ Object { diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts index 4d9d4ef9f2cad2..be3301964bd3c2 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts +++ b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts @@ -84,6 +84,21 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) expectSnapshot(elements).toMatch(); }); + + it('returns service map elements filtering by environment not defined', async () => { + const ENVIRONMENT_NOT_DEFINED = 'ENVIRONMENT_NOT_DEFINED'; + const { body, status } = await supertest.get( + `/api/apm/service-map?start=${start}&end=${end}&environment=${ENVIRONMENT_NOT_DEFINED}` + ); + expect(status).to.be(200); + const environments = new Set(); + body.elements.forEach((element: { data: Record }) => { + environments.add(element.data['service.environment']); + }); + expect(environments.size).to.eql(1); + expect(environments.has(ENVIRONMENT_NOT_DEFINED)).to.eql(true); + expectSnapshot(body).toMatch(); + }); }); }); diff --git a/x-pack/test/functional/es_archives/alerts_legacy/data.json b/x-pack/test/functional/es_archives/alerts_legacy/data.json new file mode 100644 index 00000000000000..770e8e7c156177 --- /dev/null +++ b/x-pack/test/functional/es_archives/alerts_legacy/data.json @@ -0,0 +1,491 @@ +{ + "type": "doc", + "value": { + "id": "space1:action:17f38826-5a8d-4a76-975a-b496e7fffe0b", + "index": ".kibana_1", + "source": { + "action": { + "actionTypeId": "test.index-record", + "config": { + "unencrypted": "This value shouldn't get encrypted" + }, + "name": "My action", + "secrets": "/ZW1x3z3KWzPB0H3ociZNVBeWJGQytz2P5OEUCmz4ahO7S8709DDJ+uC9ztGRig7e73GtCgT6XU42Cn8l8rm90INusLWf2oXaXbdGctjMDtTXv5S3b75YdEwIHClhLTLswmraGQHUwMsTMe+EVh9gUhwGsjDLTQAiDqsDonMto8XkTdtyP6C5axn" + }, + "namespace": "space1", + "references": [ + ], + "type": "action", + "updated_at": "2020-09-04T11:50:50.815Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:6ee9630a-a20e-44af-9465-217a3717d2ab", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:space_1_all_alerts_none_actions" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "5kB2ao8TYvwpj+CGsgnAmWvzvuPCt9Wu6q4d4Y56rTKbqO352RjiRG8dbvEzKcRveiMjfKYFOXs6dlh8cMIRBrBnF7z19+A1sq+RhrvMiqXFxbN+udBEwtUXNjgoaFH5Ajvm0t5Yg3f4XSGhaWc/n2URo6eXq/OkVsp/xjHs1rfGow5dojDEtotkFuvzPLstH2mBImYLwwejvw==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:51:04.584Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:space_1_all_alerts_none_actions" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e9c069d0-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:51:05.222Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e9c069d0-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 0, + "params": "{\"alertId\":\"6ee9630a-a20e-44af-9465-217a3717d2ab\",\"spaceId\":\"space1\"}", + "retryAt": null, + "runAt": "2020-09-04T11:51:05.197Z", + "scheduledAt": "2020-09-04T11:51:05.197Z", + "scope": [ + "alerting" + ], + "startedAt": null, + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "idle", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.197Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:b384be60-ec53-4b26-857e-0253ee55b277", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:superuser" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "Aj+qLwjCZ/WUeMUmLbe9xsf3/O4YAGGYg063WHGoPdftE7WKRxhn45ZUGu4SZ45BAloAsX113sCjIjhSwyVFTHG41lv0sGDLheviV/FwxmNnUDwX8Bn5PcbFw6n+eRsgBgXXC1qgNjhd1SfuEPd1BfmtjT58NLPN6G5NMKRAeWA7Tp5U25Aw6JYaPXwyFV5sfNhWi21pLs2fJQ==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:50:54.493Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:superuser" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e39a02f0-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:50:54.902Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e39a02f0-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 0, + "ownerId": null, + "params": "{\"alertId\":\"b384be60-ec53-4b26-857e-0253ee55b277\",\"spaceId\":\"space1\"}", + "retryAt": null, + "runAt": "2020-09-04T11:51:04.804Z", + "scheduledAt": "2020-09-04T11:50:54.879Z", + "scope": [ + "alerting" + ], + "startedAt": null, + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "idle", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:04.273Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:5cc59319-74ee-4edc-8646-a79ea91067cd", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:space_1_all_with_restricted_fixture" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "OFpEg7T+jq0nVoGkqHEN5W0fgcEu4LmCXCz6F5EzQ8mgxr2+270/nqv2+zSTnkef7rK2Xz2MDmQsdrRrsAFb5ItFb5YnR/zfqbq0+QBlvgqChKORE1+ggI1oQsYnjDtPWOqMwbUUNGYQXdyUZVdRqeeoYq847NgSo5UBoCJgEZ9ah1gzPA6XYvzbBtJdZ+QgCjfuXsrBSlImhQ==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:51:02.571Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:space_1_all_with_restricted_fixture" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e8885f00-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:51:03.175Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e8885f00-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 1, + "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", + "params": "{\"alertId\":\"5cc59319-74ee-4edc-8646-a79ea91067cd\",\"spaceId\":\"space1\"}", + "retryAt": "2020-09-04T12:01:05.793Z", + "runAt": "2020-09-04T11:51:03.152Z", + "scheduledAt": "2020-09-04T11:51:03.152Z", + "scope": [ + "alerting" + ], + "startedAt": "2020-09-04T11:51:05.793Z", + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "running", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.794Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:d41a6abb-b93b-46df-a80a-926221ea847c", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:space_1_all" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "5qbDONVFRacd8IukMksh11NZ8OFxSN31sJRgIdRw4GHME2qKqyqczHz/CDANEjKF78M98yc5xSr8vNnco2PDOHTD7YfMdnlrl1/lOdIkJ/IM8YxGBCLZfk5hRx/dD3X84ZtSEZ2hYWjAemFKtD5GpTWtczhkx9jRgUMbPgBzrhyEmDJoHMQQSOEpIJKmQpAcpHGsE5/+SVIcGQ==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:50:58.533Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:space_1_all" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e616c2c0-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:50:59.083Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e616c2c0-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 1, + "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", + "params": "{\"alertId\":\"d41a6abb-b93b-46df-a80a-926221ea847c\",\"spaceId\":\"space1\"}", + "retryAt": "2020-09-04T12:01:05.793Z", + "runAt": "2020-09-04T11:51:04.804Z", + "scheduledAt": "2020-09-04T11:50:59.052Z", + "scope": [ + "alerting" + ], + "startedAt": "2020-09-04T11:51:05.793Z", + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "idle", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.794Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:362e362b-a137-4aa2-9434-43e3d0d84a34", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:global_read" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "asFMeAR9MRRO5bwGiA5z3arqWOs9ZVPHYTvLL2S9JFRZSsA88yauEPxHeri3UpHWqLktDbOSpR4C1bmIQySDt1VOpe13JzGC/Z+/S0I9PaFQ8oHQ2VqjQ26j9hxuS+B9sXtAOhgYu2WBQw1ugeW2Eo86c9wpFux4p3SbKiNhwyC/9WUR5qI63cBnvRs8Zi8ePOMdnTJoiBzduw==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:50:56.513Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:global_read" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e4df5430-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:50:57.048Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e4df5430-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 1, + "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", + "params": "{\"alertId\":\"362e362b-a137-4aa2-9434-43e3d0d84a34\",\"spaceId\":\"space1\"}", + "retryAt": "2020-09-04T12:01:05.793Z", + "runAt": "2020-09-04T11:51:04.804Z", + "scheduledAt": "2020-09-04T11:50:57.011Z", + "scope": [ + "alerting" + ], + "startedAt": "2020-09-04T11:51:05.793Z", + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "running", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.794Z" + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/alerts_legacy/mappings.json b/x-pack/test/functional/es_archives/alerts_legacy/mappings.json new file mode 100644 index 00000000000000..68a60bec854c97 --- /dev/null +++ b/x-pack/test/functional/es_archives/alerts_legacy/mappings.json @@ -0,0 +1,2680 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "action": "6e96ac5e648f57523879661ea72525b7", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "alert": "ee7356e3d77d357fe62a10350eed4b3c", + "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", + "apm-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "app_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_transactional": "43b8830d5d0df85a6823d290885fc9fd", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "canvas-workpad-template": "ae2673f678281e2c055d764b153e9715", + "cases": "32aa96a6d3855ddda53010ae2048ac22", + "cases-comments": "c2061fb929f585df57425102fa928b4b", + "cases-configure": "42711cbb311976c0687853f4c1354572", + "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", + "config": "c63748b75f39d0c54de12d12c1ccbc20", + "dashboard": "74eb4b909f81222fa1ddeaba2881a37e", + "endpoint:user-artifact": "4a11183eee21e6fbad864f7a30b39ad0", + "endpoint:user-artifact-manifest": "4b9c0e7cfaf86d82a7ee9ed68065e50d", + "epm-packages": "8f6e0b09ea0374c4ffe98c3755373cff", + "exception-list": "497afa2f881a675d72d58e20057f3d8b", + "exception-list-agnostic": "497afa2f881a675d72d58e20057f3d8b", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "fleet-agent-actions": "e520c855577170c24481be05c3ae14ec", + "fleet-agent-events": "e20a508b6e805189356be381dbfac8db", + "fleet-agents": "6012d61d15e72564e47fc3402332756e", + "fleet-enrollment-api-keys": "a69ef7ae661dab31561d6c6f052ef2a7", + "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", + "index-pattern": "45915a1ad866812242df474eb0479052", + "infrastructure-ui-source": "2b2809653635caf490c93f090502d04c", + "ingest-agent-policies": "8b0733cce189659593659dad8db426f0", + "ingest-outputs": "8aa988c376e65443fefc26f1075e93a3", + "ingest-package-policies": "f74dfe498e1849267cda41580b2be110", + "ingest_manager_settings": "012cf278ec84579495110bb827d1ed09", + "inventory-view": "88fc7e12fd1b45b6f0787323ce4f18d2", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "lens": "52346cfec69ff7b47d5f0c12361a2797", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "map": "4a05b35c3a3a58fbc72dd0202dc3487f", + "maps-telemetry": "5ef305b18111b77789afefbd36b66171", + "metrics-explorer-view": "a8df1d270ee48c969d22d23812d08187", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "7f9e077078cab612f6a58e3bfdedb71a", + "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "siem-detection-engine-rule-actions": "6569b288c169539db10cb262bf79de18", + "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", + "siem-ui-timeline": "94bc38c7a421d15fbfe8ea565370a421", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-reindex-operation": "215107c281839ea9b3ad5f6419819763", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "uptime-dynamic-settings": "3d1b76c39bfb2cc8296b024d73854724", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "visualization": "44d6bd48a1a653bcb60ea01614b9e3c9", + "workplace_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724" + } + }, + "dynamic": "strict", + "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "enabled": false, + "type": "object" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "secrets": { + "type": "binary" + } + } + }, + "action_task_params": { + "properties": { + "actionId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alert": { + "properties": { + "actions": { + "properties": { + "actionRef": { + "type": "keyword" + }, + "actionTypeId": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + }, + "type": "nested" + }, + "alertTypeId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "apiKeyOwner": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "muteAll": { + "type": "boolean" + }, + "mutedInstanceIds": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "params": { + "enabled": false, + "type": "object" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledTaskId": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "throttle": { + "type": "keyword" + }, + "updatedBy": { + "type": "keyword" + } + } + }, + "apm-indices": { + "properties": { + "apm_oss": { + "properties": { + "errorIndices": { + "type": "keyword" + }, + "metricsIndices": { + "type": "keyword" + }, + "onboardingIndices": { + "type": "keyword" + }, + "sourcemapIndices": { + "type": "keyword" + }, + "spanIndices": { + "type": "keyword" + }, + "transactionIndices": { + "type": "keyword" + } + } + } + } + }, + "apm-telemetry": { + "dynamic": "false", + "type": "object" + }, + "app_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "application_usage_totals": { + "dynamic": "false", + "type": "object" + }, + "application_usage_transactional": { + "dynamic": "false", + "properties": { + "timestamp": { + "type": "date" + } + } + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad-template": { + "dynamic": "false", + "properties": { + "help": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "tags": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "template_key": { + "type": "keyword" + } + } + }, + "cases": { + "properties": { + "closed_at": { + "type": "date" + }, + "closed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "connector_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "description": { + "type": "text" + }, + "external_service": { + "properties": { + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "external_id": { + "type": "keyword" + }, + "external_title": { + "type": "text" + }, + "external_url": { + "type": "text" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "status": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-comments": { + "properties": { + "comment": { + "type": "text" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-configure": { + "properties": { + "closure_type": { + "type": "keyword" + }, + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-user-actions": { + "properties": { + "action": { + "type": "keyword" + }, + "action_at": { + "type": "date" + }, + "action_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "action_field": { + "type": "keyword" + }, + "new_value": { + "type": "text" + }, + "old_value": { + "type": "text" + } + } + }, + "config": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "optionsJSON": { + "index": false, + "type": "text" + }, + "panelsJSON": { + "index": false, + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "pause": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "section": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "value": { + "doc_values": false, + "index": false, + "type": "integer" + } + } + }, + "timeFrom": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "timeRestore": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "timeTo": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "endpoint:user-artifact": { + "properties": { + "body": { + "type": "binary" + }, + "compressionAlgorithm": { + "index": false, + "type": "keyword" + }, + "created": { + "index": false, + "type": "date" + }, + "decodedSha256": { + "index": false, + "type": "keyword" + }, + "decodedSize": { + "index": false, + "type": "long" + }, + "encodedSha256": { + "type": "keyword" + }, + "encodedSize": { + "index": false, + "type": "long" + }, + "encryptionAlgorithm": { + "index": false, + "type": "keyword" + }, + "identifier": { + "type": "keyword" + } + } + }, + "endpoint:user-artifact-manifest": { + "properties": { + "created": { + "index": false, + "type": "date" + }, + "ids": { + "index": false, + "type": "keyword" + }, + "schemaVersion": { + "type": "keyword" + }, + "semanticVersion": { + "index": false, + "type": "keyword" + } + } + }, + "epm-packages": { + "properties": { + "es_index_patterns": { + "enabled": false, + "type": "object" + }, + "installed_es": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "installed_kibana": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "internal": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "removable": { + "type": "boolean" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list-agnostic": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "fleet-agent-actions": { + "properties": { + "agent_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "data": { + "type": "binary" + }, + "sent_at": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agent-events": { + "properties": { + "action_id": { + "type": "keyword" + }, + "agent_id": { + "type": "keyword" + }, + "data": { + "type": "text" + }, + "message": { + "type": "text" + }, + "payload": { + "type": "text" + }, + "policy_id": { + "type": "keyword" + }, + "stream_id": { + "type": "keyword" + }, + "subtype": { + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agents": { + "properties": { + "access_api_key_id": { + "type": "keyword" + }, + "active": { + "type": "boolean" + }, + "current_error_events": { + "index": false, + "type": "text" + }, + "default_api_key": { + "type": "binary" + }, + "default_api_key_id": { + "type": "keyword" + }, + "enrolled_at": { + "type": "date" + }, + "last_checkin": { + "type": "date" + }, + "last_checkin_status": { + "type": "keyword" + }, + "last_updated": { + "type": "date" + }, + "local_metadata": { + "type": "flattened" + }, + "packages": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "shared_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "unenrolled_at": { + "type": "date" + }, + "unenrollment_started_at": { + "type": "date" + }, + "updated_at": { + "type": "date" + }, + "user_provided_metadata": { + "type": "flattened" + }, + "version": { + "type": "keyword" + } + } + }, + "fleet-enrollment-api-keys": { + "properties": { + "active": { + "type": "boolean" + }, + "api_key": { + "type": "binary" + }, + "api_key_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "expire_at": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "dynamic": "false", + "properties": { + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "properties": { + "description": { + "type": "text" + }, + "fields": { + "properties": { + "container": { + "type": "keyword" + }, + "host": { + "type": "keyword" + }, + "pod": { + "type": "keyword" + }, + "tiebreaker": { + "type": "keyword" + }, + "timestamp": { + "type": "keyword" + } + } + }, + "inventoryDefaultView": { + "type": "keyword" + }, + "logAlias": { + "type": "keyword" + }, + "logColumns": { + "properties": { + "fieldColumn": { + "properties": { + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + } + } + }, + "messageColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "timestampColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "metricAlias": { + "type": "keyword" + }, + "metricsExplorerDefaultView": { + "type": "keyword" + }, + "name": { + "type": "text" + } + } + }, + "ingest-agent-policies": { + "properties": { + "description": { + "type": "text" + }, + "is_default": { + "type": "boolean" + }, + "monitoring_enabled": { + "index": false, + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "package_policies": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "status": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest-outputs": { + "properties": { + "ca_sha256": { + "index": false, + "type": "keyword" + }, + "config": { + "type": "flattened" + }, + "fleet_enroll_password": { + "type": "binary" + }, + "fleet_enroll_username": { + "type": "binary" + }, + "hosts": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "ingest-package-policies": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "enabled": { + "type": "boolean" + }, + "inputs": { + "enabled": false, + "properties": { + "config": { + "type": "flattened" + }, + "enabled": { + "type": "boolean" + }, + "streams": { + "properties": { + "compiled_stream": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + }, + "type": "nested" + }, + "type": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + }, + "type": "nested" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "output_id": { + "type": "keyword" + }, + "package": { + "properties": { + "name": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "policy_id": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest_manager_settings": { + "properties": { + "agent_auto_upgrade": { + "type": "keyword" + }, + "has_seen_add_data_notice": { + "index": false, + "type": "boolean" + }, + "kibana_ca_sha256": { + "type": "keyword" + }, + "kibana_url": { + "type": "keyword" + }, + "package_auto_upgrade": { + "type": "keyword" + } + } + }, + "inventory-view": { + "properties": { + "accountId": { + "type": "keyword" + }, + "autoBounds": { + "type": "boolean" + }, + "autoReload": { + "type": "boolean" + }, + "boundsOverride": { + "properties": { + "max": { + "type": "integer" + }, + "min": { + "type": "integer" + } + } + }, + "customMetrics": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "label": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "customOptions": { + "properties": { + "field": { + "type": "keyword" + }, + "text": { + "type": "keyword" + } + }, + "type": "nested" + }, + "filterQuery": { + "properties": { + "expression": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + } + } + }, + "groupBy": { + "properties": { + "field": { + "type": "keyword" + }, + "label": { + "type": "keyword" + } + }, + "type": "nested" + }, + "legend": { + "properties": { + "palette": { + "type": "keyword" + }, + "reverseColors": { + "type": "boolean" + }, + "steps": { + "type": "long" + } + } + }, + "metric": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "label": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "name": { + "type": "keyword" + }, + "nodeType": { + "type": "keyword" + }, + "region": { + "type": "keyword" + }, + "sort": { + "properties": { + "by": { + "type": "keyword" + }, + "direction": { + "type": "keyword" + } + } + }, + "time": { + "type": "long" + }, + "view": { + "type": "keyword" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "lens": { + "properties": { + "description": { + "type": "text" + }, + "expression": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "state": { + "type": "flattened" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "lens-ui-telemetry": { + "properties": { + "count": { + "type": "integer" + }, + "date": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "map": { + "properties": { + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "enabled": false, + "type": "object" + }, + "metrics-explorer-view": { + "properties": { + "chartOptions": { + "properties": { + "stack": { + "type": "boolean" + }, + "type": { + "type": "keyword" + }, + "yAxisMode": { + "type": "keyword" + } + } + }, + "currentTimerange": { + "properties": { + "from": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "to": { + "type": "keyword" + } + } + }, + "name": { + "type": "keyword" + }, + "options": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "filterQuery": { + "type": "keyword" + }, + "forceInterval": { + "type": "boolean" + }, + "groupBy": { + "type": "keyword" + }, + "limit": { + "type": "integer" + }, + "metrics": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "color": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "label": { + "type": "keyword" + } + }, + "type": "nested" + }, + "source": { + "type": "keyword" + } + } + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "alert": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "config": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "space": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "ml-telemetry": { + "properties": { + "file_data_visualizer": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } + } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "sort": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "search-telemetry": { + "dynamic": "false", + "type": "object" + }, + "siem-detection-engine-rule-actions": { + "properties": { + "actions": { + "properties": { + "action_type_id": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alertThrottle": { + "type": "keyword" + }, + "ruleAlertId": { + "type": "keyword" + }, + "ruleThrottle": { + "type": "keyword" + } + } + }, + "siem-detection-engine-rule-status": { + "properties": { + "alertId": { + "type": "keyword" + }, + "bulkCreateTimeDurations": { + "type": "float" + }, + "gap": { + "type": "text" + }, + "lastFailureAt": { + "type": "date" + }, + "lastFailureMessage": { + "type": "text" + }, + "lastLookBackDate": { + "type": "date" + }, + "lastSuccessAt": { + "type": "date" + }, + "lastSuccessMessage": { + "type": "text" + }, + "searchAfterTimeDurations": { + "type": "float" + }, + "status": { + "type": "keyword" + }, + "statusDate": { + "type": "date" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "eventType": { + "type": "keyword" + }, + "excludedRowRendererIds": { + "type": "text" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "filters": { + "properties": { + "exists": { + "type": "text" + }, + "match_all": { + "type": "text" + }, + "meta": { + "properties": { + "alias": { + "type": "text" + }, + "controlledBy": { + "type": "text" + }, + "disabled": { + "type": "boolean" + }, + "field": { + "type": "text" + }, + "formattedValue": { + "type": "text" + }, + "index": { + "type": "keyword" + }, + "key": { + "type": "keyword" + }, + "negate": { + "type": "boolean" + }, + "params": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "text" + } + } + }, + "missing": { + "type": "text" + }, + "query": { + "type": "text" + }, + "range": { + "type": "text" + }, + "script": { + "type": "text" + } + } + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "savedQueryId": { + "type": "keyword" + }, + "sort": { + "properties": { + "columnId": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + }, + "templateTimelineId": { + "type": "text" + }, + "templateTimelineVersion": { + "type": "integer" + }, + "timelineType": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "imageUrl": { + "index": false, + "type": "text" + }, + "initials": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "telemetry": { + "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" + }, + "sendUsageFrom": { + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "tsvb-validation-telemetry": { + "properties": { + "failedRequests": { + "type": "long" + } + } + }, + "type": { + "type": "keyword" + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-reindex-operation": { + "properties": { + "errorMessage": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "indexName": { + "type": "keyword" + }, + "lastCompletedStep": { + "type": "long" + }, + "locked": { + "type": "date" + }, + "newIndexName": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "reindexOptions": { + "properties": { + "openAndClose": { + "type": "boolean" + }, + "queueSettings": { + "properties": { + "queuedAt": { + "type": "long" + }, + "startedAt": { + "type": "long" + } + } + } + } + }, + "reindexTaskId": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "reindexTaskPercComplete": { + "type": "float" + }, + "runningReindexCount": { + "type": "integer" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "null_value": true, + "type": "boolean" + } + } + } + } + }, + "ui_open": { + "properties": { + "cluster": { + "null_value": 0, + "type": "long" + }, + "indices": { + "null_value": 0, + "type": "long" + }, + "overview": { + "null_value": 0, + "type": "long" + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "null_value": 0, + "type": "long" + }, + "open": { + "null_value": 0, + "type": "long" + }, + "start": { + "null_value": 0, + "type": "long" + }, + "stop": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "uptime-dynamic-settings": { + "dynamic": "false", + "type": "object" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "savedSearchRefName": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "index": false, + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "index": false, + "type": "text" + } + } + }, + "workplace_search_telemetry": { + "dynamic": "false", + "type": "object" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + ".kibana_task_manager": { + } + }, + "index": ".kibana_task_manager_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "task": "235412e52d09e7165fac8a67a43ad6b4", + "type": "2f4316de49999235636386fe51dc06c1", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0" + } + }, + "dynamic": "strict", + "properties": { + "migrationVersion": { + "dynamic": "true", + "properties": { + "task": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "task": { + "properties": { + "attempts": { + "type": "integer" + }, + "ownerId": { + "type": "keyword" + }, + "params": { + "type": "text" + }, + "retryAt": { + "type": "date" + }, + "runAt": { + "type": "date" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledAt": { + "type": "date" + }, + "scope": { + "type": "keyword" + }, + "startedAt": { + "type": "date" + }, + "state": { + "type": "text" + }, + "status": { + "type": "keyword" + }, + "taskType": { + "type": "keyword" + }, + "user": { + "type": "keyword" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ba793307e1e971..8bdb06152a7f20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5708,6 +5708,11 @@ ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= +ansi-escapes@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b" + integrity sha1-W65SvkJIeN2Xg+iRDj/Cki6DyBs= + ansi-escapes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" @@ -8048,6 +8053,15 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" +camelcase-keys@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" + integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c= + dependencies: + camelcase "^4.1.0" + map-obj "^2.0.0" + quick-lru "^1.0.0" + camelcase-keys@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" @@ -8072,7 +8086,7 @@ camelcase@^3.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= -camelcase@^4.0.0: +camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= @@ -10336,7 +10350,7 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize-keys@^1.1.0: +decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -11856,6 +11870,17 @@ eslint-config-prettier@^6.11.0: dependencies: get-stdin "^6.0.0" +eslint-formatter-pretty@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-1.3.0.tgz#985d9e41c1f8475f4a090c5dbd2dfcf2821d607e" + integrity sha512-5DY64Y1rYCm7cfFDHEGUn54bvCnK+wSUVF07N8oXeqUJFSd+gnYOTXbzelQ1HurESluY6gnEQPmXOIkB4Wa+gA== + dependencies: + ansi-escapes "^2.0.0" + chalk "^2.1.0" + log-symbols "^2.0.0" + plur "^2.1.2" + string-width "^2.0.0" + eslint-formatter-pretty@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-4.0.0.tgz#dc15f3bf4fb51b7ba5fbedb77f57ba8841140ce2" @@ -14162,7 +14187,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^9.2.0: +globby@^9.1.0, globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== @@ -16174,6 +16199,11 @@ iron@5.x.x: cryptiles "4.x.x" hoek "5.x.x" +irregular-plurals@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766" + integrity sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y= + irregular-plurals@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.2.0.tgz#b19c490a0723798db51b235d7e39add44dab0822" @@ -19006,7 +19036,7 @@ log-ok@^0.1.1: ansi-green "^0.1.1" success-symbol "^0.1.0" -log-symbols@2.2.0, log-symbols@^2.1.0, log-symbols@^2.2.0: +log-symbols@2.2.0, log-symbols@^2.0.0, log-symbols@^2.1.0, log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== @@ -19308,6 +19338,11 @@ map-obj@^1.0.0, map-obj@^1.0.1: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= +map-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" + integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= + map-obj@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" @@ -19611,6 +19646,21 @@ meow@^3.0.0, meow@^3.3.0, meow@^3.7.0: redent "^1.0.0" trim-newlines "^1.0.0" +meow@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" + integrity sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig== + dependencies: + camelcase-keys "^4.0.0" + decamelize-keys "^1.0.0" + loud-rejection "^1.0.0" + minimist-options "^3.0.1" + normalize-package-data "^2.3.4" + read-pkg-up "^3.0.0" + redent "^2.0.0" + trim-newlines "^2.0.0" + yargs-parser "^10.0.0" + meow@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/meow/-/meow-6.1.1.tgz#1ad64c4b76b2a24dfb2f635fddcadf320d251467" @@ -19874,6 +19924,14 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" +minimist-options@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" + integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + minimist-options@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -22363,6 +22421,13 @@ plugin-error@^1.0.1: arr-union "^3.1.0" extend-shallow "^3.0.2" +plur@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a" + integrity sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo= + dependencies: + irregular-plurals "^1.0.0" + plur@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84" @@ -23075,6 +23140,11 @@ queue@6.0.1: dependencies: inherits "~2.0.3" +quick-lru@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" + integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= + quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" @@ -24096,6 +24166,14 @@ read-pkg-up@^2.0.0: find-up "^2.0.0" read-pkg "^2.0.0" +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + read-pkg-up@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" @@ -24325,6 +24403,14 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +redent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" + integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= + dependencies: + indent-string "^3.0.0" + strip-indent "^2.0.0" + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -26961,6 +27047,11 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -28055,6 +28146,11 @@ trim-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= +trim-newlines@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" + integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= + trim-newlines@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" @@ -28161,6 +28257,19 @@ tsd@^0.13.1: read-pkg-up "^7.0.0" update-notifier "^4.1.0" +tsd@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/tsd/-/tsd-0.7.4.tgz#d9aba567f1394641821a6800dcee60746c87bd03" + integrity sha512-cqr1s2GHtVkU3L/4BXDaeJOjFEuZ7iOVC+hwmyx4G7Eo26mSXCFNnwFm4EasK/MW2HdY3AQWux+AjYzDYLzZow== + dependencies: + eslint-formatter-pretty "^1.3.0" + globby "^9.1.0" + meow "^5.0.0" + path-exists "^3.0.0" + read-pkg-up "^4.0.0" + typescript "^3.0.1" + update-notifier "^2.5.0" + tslib@^1, tslib@^1.0.0, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" @@ -28327,7 +28436,7 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" -typescript@4.0.2, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.7.2: +typescript@4.0.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 "4.0.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== @@ -30575,6 +30684,13 @@ yargs-parser@5.0.0-security.0: camelcase "^3.0.0" object.assign "^4.1.0" +yargs-parser@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== + dependencies: + camelcase "^4.1.0" + yargs-parser@^18.1.1, yargs-parser@^18.1.2, yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"