diff --git a/.i18nrc.json b/.i18nrc.json index 68bb343092f259..269b02ecee87a4 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -9,6 +9,7 @@ "data": "src/plugins/data", "embeddableApi": "src/plugins/embeddable", "embeddableExamples": "examples/embeddable_examples", + "fieldFormats": "src/plugins/field_formats", "uiActionsExamples": "examples/ui_action_examples", "share": "src/plugins/share", "home": "src/plugins/home", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index eaaab3386a7951..5c47d0f46bbfe9 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -115,6 +115,10 @@ for use in their own application. |Expression Shape plugin adds a shape function to the expression plugin and an associated renderer. The renderer will display the given shape with selected decorations. +|{kib-repo}blob/{branch}/src/plugins/field_formats/README.md[fieldFormats] +|Index pattern fields formatters + + |{kib-repo}blob/{branch}/src/plugins/home/README.md[home] |Moves the legacy ui/registry/feature_catalogue module for registering "features" that should be shown in the home page's feature catalogue to a service within a "home" plugin. The feature catalogue refered to here should not be confused with the "feature" plugin for registering features used to derive UI capabilities for feature controls. diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.tojson.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.tojson.md index 6e9f12d98d1491..2c93ae6143b445 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.tojson.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.tojson.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Use serialize() instead. 8.0 +> Use serialize() instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.baseformatterspublic.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.baseformatterspublic.md deleted file mode 100644 index 1aa9f460c4fac8..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.baseformatterspublic.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [baseFormattersPublic](./kibana-plugin-plugins-data-public.baseformatterspublic.md) - -## baseFormattersPublic variable - -Signature: - -```typescript -baseFormattersPublic: (import("../../common").FieldFormatInstanceType | typeof DateFormat | typeof DateNanosFormat)[] -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.castestokbnfieldtypename.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.castestokbnfieldtypename.md index c6135d9313b237..90aa0b0a8a3134 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.castestokbnfieldtypename.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.castestokbnfieldtypename.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/field-types" package directly instead. 8.0 +> Import from the "@kbn/field-types" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.customfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.customfilter.md index 3d08df8544d6ca..6addd931ce22d7 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.customfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.customfilter.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.md index 4b2cad7b428821..b970a408e51302 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.md @@ -20,7 +20,7 @@ export declare class DataPublicPlugin implements PluginSignature: ```typescript -setup(core: CoreSetup, { bfetch, expressions, uiActions, usageCollection, inspector }: DataSetupDependencies): DataPublicPluginSetup; +setup(core: CoreSetup, { bfetch, expressions, uiActions, usageCollection, inspector, fieldFormats, }: DataSetupDependencies): DataPublicPluginSetup; ``` ## Parameters @@ -15,7 +15,7 @@ setup(core: CoreSetup, { bfetch, e | Parameter | Type | Description | | --- | --- | --- | | core | CoreSetup<DataStartDependencies, DataPublicPluginStart> | | -| { bfetch, expressions, uiActions, usageCollection, inspector } | DataSetupDependencies | | +| { bfetch, expressions, uiActions, usageCollection, inspector, fieldFormats, } | DataSetupDependencies | | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.start.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.start.md index 4ea7ec8cd4f65f..c7611ac761bb9d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.start.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.start.md @@ -7,7 +7,7 @@ Signature: ```typescript -start(core: CoreStart, { uiActions }: DataStartDependencies): DataPublicPluginStart; +start(core: CoreStart, { uiActions, fieldFormats }: DataStartDependencies): DataPublicPluginStart; ``` ## Parameters @@ -15,7 +15,7 @@ start(core: CoreStart, { uiActions }: DataStartDependencies): DataPublicPluginSt | Parameter | Type | Description | | --- | --- | --- | | core | CoreStart | | -| { uiActions } | DataStartDependencies | | +| { uiActions, fieldFormats } | DataStartDependencies | | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginsetup.fieldformats.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginsetup.fieldformats.md index 993634023c20c2..54e64c309351ec 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginsetup.fieldformats.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginsetup.fieldformats.md @@ -4,6 +4,11 @@ ## DataPublicPluginSetup.fieldFormats property +> Warning: This API is now obsolete. +> +> Use fieldFormats plugin instead +> + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md index 344044b38f7de9..a60e631835ea49 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md @@ -4,7 +4,10 @@ ## DataPublicPluginStart.fieldFormats property -field formats service [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) +> Warning: This API is now obsolete. +> +> Use fieldFormats plugin instead +> Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.md index c7810b18c55a9e..341ec0d7e514c0 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.md @@ -18,7 +18,7 @@ export interface DataPublicPluginStart | --- | --- | --- | | [actions](./kibana-plugin-plugins-data-public.datapublicpluginstart.actions.md) | DataPublicPluginStartActions | filter creation utilities [DataPublicPluginStartActions](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.md) | | [autocomplete](./kibana-plugin-plugins-data-public.datapublicpluginstart.autocomplete.md) | AutocompleteStart | autocomplete service [AutocompleteStart](./kibana-plugin-plugins-data-public.autocompletestart.md) | -| [fieldFormats](./kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md) | FieldFormatsStart | field formats service [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) | +| [fieldFormats](./kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md) | FieldFormatsStart | | | [indexPatterns](./kibana-plugin-plugins-data-public.datapublicpluginstart.indexpatterns.md) | IndexPatternsContract | index patterns service [IndexPatternsContract](./kibana-plugin-plugins-data-public.indexpatternscontract.md) | | [nowProvider](./kibana-plugin-plugins-data-public.datapublicpluginstart.nowprovider.md) | NowProviderPublicContract | | | [query](./kibana-plugin-plugins-data-public.datapublicpluginstart.query.md) | QueryStart | query service [QueryStart](./kibana-plugin-plugins-data-public.querystart.md) | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md index 24d60613d913aa..b5d5c5cfee5ada 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import helpers from the "@kbn/es-query" package directly instead. 8.0 +> Import helpers from the "@kbn/es-query" package directly instead. 8.1 > Filter helpers namespace: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md index e569733f15e7f7..3b768404aab952 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import helpers from the "@kbn/es-query" package directly instead. 8.0 +> Import helpers from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md index 447e387f90eb5f..2a26b009d74476 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import helpers from the "@kbn/es-query" package directly instead. 8.0 +> Import helpers from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.md index f877fa26b1b253..48a32cd9abe612 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.existsfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.existsfilter.md index 5c98ba4131ac35..79c92cfe52dd71 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.existsfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.existsfilter.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat._constructor_.md deleted file mode 100644 index e38da6600696c9..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat._constructor_.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [(constructor)](./kibana-plugin-plugins-data-public.fieldformat._constructor_.md) - -## FieldFormat.(constructor) - -Constructs a new instance of the `FieldFormat` class - -Signature: - -```typescript -constructor(_params?: IFieldFormatMetaParams, getConfig?: FieldFormatsGetConfigFn); -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| \_params | IFieldFormatMetaParams | | -| getConfig | FieldFormatsGetConfigFn | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat._params.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat._params.md deleted file mode 100644 index ac3f256a9afc30..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat._params.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [\_params](./kibana-plugin-plugins-data-public.fieldformat._params.md) - -## FieldFormat.\_params property - -Signature: - -```typescript -protected readonly _params: any; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.allowsnumericalaggregations.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.allowsnumericalaggregations.md deleted file mode 100644 index 454a816a601710..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.allowsnumericalaggregations.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [allowsNumericalAggregations](./kibana-plugin-plugins-data-public.fieldformat.allowsnumericalaggregations.md) - -## FieldFormat.allowsNumericalAggregations property - -Signature: - -```typescript -allowsNumericalAggregations?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.convert.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.convert.md deleted file mode 100644 index 0535585cb4718c..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.convert.md +++ /dev/null @@ -1,26 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [convert](./kibana-plugin-plugins-data-public.fieldformat.convert.md) - -## FieldFormat.convert() method - -Convert a raw value to a formatted string - -Signature: - -```typescript -convert(value: any, contentType?: FieldFormatsContentType, options?: HtmlContextTypeOptions | TextContextTypeOptions): string; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| value | any | | -| contentType | FieldFormatsContentType | | -| options | HtmlContextTypeOptions | TextContextTypeOptions | | - -Returns: - -`string` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.convertobject.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.convertobject.md deleted file mode 100644 index 436124ac083871..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.convertobject.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [convertObject](./kibana-plugin-plugins-data-public.fieldformat.convertobject.md) - -## FieldFormat.convertObject property - - {FieldFormatConvert} have to remove the private because of https://github.com/Microsoft/TypeScript/issues/17293 - -Signature: - -```typescript -convertObject: FieldFormatConvert | undefined; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.fieldtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.fieldtype.md deleted file mode 100644 index 1d109a599d2d9c..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.fieldtype.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [fieldType](./kibana-plugin-plugins-data-public.fieldformat.fieldtype.md) - -## FieldFormat.fieldType property - - {string} - Field Format Type - -Signature: - -```typescript -static fieldType: string | string[]; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.from.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.from.md deleted file mode 100644 index ec497de59d2368..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.from.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [from](./kibana-plugin-plugins-data-public.fieldformat.from.md) - -## FieldFormat.from() method - -Signature: - -```typescript -static from(convertFn: FieldFormatConvertFunction): FieldFormatInstanceType; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| convertFn | FieldFormatConvertFunction | | - -Returns: - -`FieldFormatInstanceType` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.getconfig.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.getconfig.md deleted file mode 100644 index 446e0c237ce132..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.getconfig.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [getConfig](./kibana-plugin-plugins-data-public.fieldformat.getconfig.md) - -## FieldFormat.getConfig property - -Signature: - -```typescript -protected getConfig: FieldFormatsGetConfigFn | undefined; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.getconverterfor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.getconverterfor.md deleted file mode 100644 index f4eeb5eed06a09..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.getconverterfor.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [getConverterFor](./kibana-plugin-plugins-data-public.fieldformat.getconverterfor.md) - -## FieldFormat.getConverterFor() method - -Get a convert function that is bound to a specific contentType - -Signature: - -```typescript -getConverterFor(contentType?: FieldFormatsContentType): FieldFormatConvertFunction; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| contentType | FieldFormatsContentType | | - -Returns: - -`FieldFormatConvertFunction` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.getparamdefaults.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.getparamdefaults.md deleted file mode 100644 index 59afdc25df350a..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.getparamdefaults.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [getParamDefaults](./kibana-plugin-plugins-data-public.fieldformat.getparamdefaults.md) - -## FieldFormat.getParamDefaults() method - -Get parameter defaults {object} - parameter defaults - -Signature: - -```typescript -getParamDefaults(): Record; -``` -Returns: - -`Record` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.hidden.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.hidden.md deleted file mode 100644 index 2032f0fc01112f..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.hidden.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [hidden](./kibana-plugin-plugins-data-public.fieldformat.hidden.md) - -## FieldFormat.hidden property - -Hidden field formats can only be accessed directly by id, They won't appear in field format editor UI, But they can be accessed and used from code internally. - - {boolean} - Is this a hidden field format - -Signature: - -```typescript -static hidden: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.htmlconvert.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.htmlconvert.md deleted file mode 100644 index 945ac7ededff64..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.htmlconvert.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [htmlConvert](./kibana-plugin-plugins-data-public.fieldformat.htmlconvert.md) - -## FieldFormat.htmlConvert property - - {htmlConvert} have to remove the protected because of https://github.com/Microsoft/TypeScript/issues/17293 - -Signature: - -```typescript -htmlConvert: HtmlContextTypeConvert | undefined; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.id.md deleted file mode 100644 index 91c3ff4f2d9a30..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.id.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [id](./kibana-plugin-plugins-data-public.fieldformat.id.md) - -## FieldFormat.id property - - {string} - Field Format Id - -Signature: - -```typescript -static id: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.isinstanceoffieldformat.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.isinstanceoffieldformat.md deleted file mode 100644 index c6afa27fe59528..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.isinstanceoffieldformat.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [isInstanceOfFieldFormat](./kibana-plugin-plugins-data-public.fieldformat.isinstanceoffieldformat.md) - -## FieldFormat.isInstanceOfFieldFormat() method - -Signature: - -```typescript -static isInstanceOfFieldFormat(fieldFormat: any): fieldFormat is FieldFormat; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| fieldFormat | any | | - -Returns: - -`fieldFormat is FieldFormat` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.md deleted file mode 100644 index 640064af4be126..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.md +++ /dev/null @@ -1,48 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) - -## FieldFormat class - -Signature: - -```typescript -export declare abstract class FieldFormat -``` - -## Constructors - -| Constructor | Modifiers | Description | -| --- | --- | --- | -| [(constructor)(\_params, getConfig)](./kibana-plugin-plugins-data-public.fieldformat._constructor_.md) | | Constructs a new instance of the FieldFormat class | - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [\_params](./kibana-plugin-plugins-data-public.fieldformat._params.md) | | any | | -| [allowsNumericalAggregations](./kibana-plugin-plugins-data-public.fieldformat.allowsnumericalaggregations.md) | | boolean | | -| [convertObject](./kibana-plugin-plugins-data-public.fieldformat.convertobject.md) | | FieldFormatConvert | undefined | {FieldFormatConvert} have to remove the private because of https://github.com/Microsoft/TypeScript/issues/17293 | -| [fieldType](./kibana-plugin-plugins-data-public.fieldformat.fieldtype.md) | static | string | string[] | {string} - Field Format Type | -| [getConfig](./kibana-plugin-plugins-data-public.fieldformat.getconfig.md) | | FieldFormatsGetConfigFn | undefined | | -| [hidden](./kibana-plugin-plugins-data-public.fieldformat.hidden.md) | static | boolean | Hidden field formats can only be accessed directly by id, They won't appear in field format editor UI, But they can be accessed and used from code internally. {boolean} - Is this a hidden field format | -| [htmlConvert](./kibana-plugin-plugins-data-public.fieldformat.htmlconvert.md) | | HtmlContextTypeConvert | undefined | {htmlConvert} have to remove the protected because of https://github.com/Microsoft/TypeScript/issues/17293 | -| [id](./kibana-plugin-plugins-data-public.fieldformat.id.md) | static | string | {string} - Field Format Id | -| [textConvert](./kibana-plugin-plugins-data-public.fieldformat.textconvert.md) | | TextContextTypeConvert | undefined | {textConvert} have to remove the protected because of https://github.com/Microsoft/TypeScript/issues/17293 | -| [title](./kibana-plugin-plugins-data-public.fieldformat.title.md) | static | string | {string} - Field Format Title | -| [type](./kibana-plugin-plugins-data-public.fieldformat.type.md) | | any | {Function} - ref to child class | - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [convert(value, contentType, options)](./kibana-plugin-plugins-data-public.fieldformat.convert.md) | | Convert a raw value to a formatted string | -| [from(convertFn)](./kibana-plugin-plugins-data-public.fieldformat.from.md) | static | | -| [getConverterFor(contentType)](./kibana-plugin-plugins-data-public.fieldformat.getconverterfor.md) | | Get a convert function that is bound to a specific contentType | -| [getParamDefaults()](./kibana-plugin-plugins-data-public.fieldformat.getparamdefaults.md) | | Get parameter defaults {object} - parameter defaults | -| [isInstanceOfFieldFormat(fieldFormat)](./kibana-plugin-plugins-data-public.fieldformat.isinstanceoffieldformat.md) | static | | -| [param(name)](./kibana-plugin-plugins-data-public.fieldformat.param.md) | | Get the value of a param. This value may be a default value. | -| [params()](./kibana-plugin-plugins-data-public.fieldformat.params.md) | | Get all of the params in a single object {object} | -| [setupContentType()](./kibana-plugin-plugins-data-public.fieldformat.setupcontenttype.md) | | | -| [toJSON()](./kibana-plugin-plugins-data-public.fieldformat.tojson.md) | | Serialize this format to a simple POJO, with only the params that are not default {object} | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.param.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.param.md deleted file mode 100644 index 1e7fd9d161429b..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.param.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [param](./kibana-plugin-plugins-data-public.fieldformat.param.md) - -## FieldFormat.param() method - -Get the value of a param. This value may be a default value. - -Signature: - -```typescript -param(name: string): any; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| name | string | | - -Returns: - -`any` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.params.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.params.md deleted file mode 100644 index 5825af4925d066..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.params.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [params](./kibana-plugin-plugins-data-public.fieldformat.params.md) - -## FieldFormat.params() method - -Get all of the params in a single object {object} - -Signature: - -```typescript -params(): Record; -``` -Returns: - -`Record` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.setupcontenttype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.setupcontenttype.md deleted file mode 100644 index 41f5f2446f22aa..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.setupcontenttype.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [setupContentType](./kibana-plugin-plugins-data-public.fieldformat.setupcontenttype.md) - -## FieldFormat.setupContentType() method - -Signature: - -```typescript -setupContentType(): FieldFormatConvert; -``` -Returns: - -`FieldFormatConvert` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.textconvert.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.textconvert.md deleted file mode 100644 index 57ccca9136081f..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.textconvert.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [textConvert](./kibana-plugin-plugins-data-public.fieldformat.textconvert.md) - -## FieldFormat.textConvert property - - {textConvert} have to remove the protected because of https://github.com/Microsoft/TypeScript/issues/17293 - -Signature: - -```typescript -textConvert: TextContextTypeConvert | undefined; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.title.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.title.md deleted file mode 100644 index b19246758f080b..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.title.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [title](./kibana-plugin-plugins-data-public.fieldformat.title.md) - -## FieldFormat.title property - - {string} - Field Format Title - -Signature: - -```typescript -static title: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.tojson.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.tojson.md deleted file mode 100644 index 48ec9456c56dd8..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.tojson.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [toJSON](./kibana-plugin-plugins-data-public.fieldformat.tojson.md) - -## FieldFormat.toJSON() method - -Serialize this format to a simple POJO, with only the params that are not default - - {object} - -Signature: - -```typescript -toJSON(): { - id: any; - params: any; - }; -``` -Returns: - -`{ - id: any; - params: any; - }` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.type.md deleted file mode 100644 index 394a2e3cc9afbf..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformat.type.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) > [type](./kibana-plugin-plugins-data-public.fieldformat.type.md) - -## FieldFormat.type property - - {Function} - ref to child class - -Signature: - -```typescript -type: any; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.es.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.es.md deleted file mode 100644 index 82441ee41d80dc..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.es.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) > [es](./kibana-plugin-plugins-data-public.fieldformatconfig.es.md) - -## FieldFormatConfig.es property - -Signature: - -```typescript -es?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.id.md deleted file mode 100644 index b179c314a56d3f..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) > [id](./kibana-plugin-plugins-data-public.fieldformatconfig.id.md) - -## FieldFormatConfig.id property - -Signature: - -```typescript -id: FieldFormatId; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.md deleted file mode 100644 index f856a3132eccba..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) - -## FieldFormatConfig interface - -Signature: - -```typescript -export interface FieldFormatConfig -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [es](./kibana-plugin-plugins-data-public.fieldformatconfig.es.md) | boolean | | -| [id](./kibana-plugin-plugins-data-public.fieldformatconfig.id.md) | FieldFormatId | | -| [params](./kibana-plugin-plugins-data-public.fieldformatconfig.params.md) | Record<string, any> | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.params.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.params.md deleted file mode 100644 index aad977643ad2fe..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatconfig.params.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) > [params](./kibana-plugin-plugins-data-public.fieldformatconfig.params.md) - -## FieldFormatConfig.params property - -Signature: - -```typescript -params: Record; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatid.md deleted file mode 100644 index 9f94d50a2001f7..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatid.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormatId](./kibana-plugin-plugins-data-public.fieldformatid.md) - -## FieldFormatId type - - id type is needed for creating custom converters. - -Signature: - -```typescript -export declare type FieldFormatId = FIELD_FORMAT_IDS | string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformats.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformats.md deleted file mode 100644 index 838ea2b5d9f3ad..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformats.md +++ /dev/null @@ -1,39 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [fieldFormats](./kibana-plugin-plugins-data-public.fieldformats.md) - -## fieldFormats variable - -Signature: - -```typescript -fieldFormats: { - FieldFormat: typeof FieldFormat; - FieldFormatsRegistry: typeof FieldFormatsRegistry; - DEFAULT_CONVERTER_COLOR: { - range: string; - regex: string; - text: string; - background: string; - }; - HTML_CONTEXT_TYPE: import("../common").FieldFormatsContentType; - TEXT_CONTEXT_TYPE: import("../common").FieldFormatsContentType; - FIELD_FORMAT_IDS: typeof FIELD_FORMAT_IDS; - BoolFormat: typeof BoolFormat; - BytesFormat: typeof BytesFormat; - ColorFormat: typeof ColorFormat; - DateFormat: typeof DateFormat; - DateNanosFormat: typeof DateNanosFormat; - DurationFormat: typeof DurationFormat; - IpFormat: typeof IpFormat; - NumberFormat: typeof NumberFormat; - PercentFormat: typeof PercentFormat; - RelativeDateFormat: typeof RelativeDateFormat; - SourceFormat: typeof SourceFormat; - StaticLookupFormat: typeof StaticLookupFormat; - UrlFormat: typeof UrlFormat; - StringFormat: typeof StringFormat; - TruncateFormat: typeof TruncateFormat; - HistogramFormat: typeof HistogramFormat; -} -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatscontenttype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatscontenttype.md deleted file mode 100644 index 8e89a0262b438e..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatscontenttype.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormatsContentType](./kibana-plugin-plugins-data-public.fieldformatscontenttype.md) - -## FieldFormatsContentType type - -\* - -Signature: - -```typescript -export declare type FieldFormatsContentType = 'html' | 'text'; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md deleted file mode 100644 index c54e549c42a441..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md) - -## FieldFormatsGetConfigFn type - -Signature: - -```typescript -export declare type FieldFormatsGetConfigFn = GetConfigFn; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsstart.md deleted file mode 100644 index ac8ddce6e70160..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsstart.md +++ /dev/null @@ -1,14 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) - -## FieldFormatsStart type - - -Signature: - -```typescript -export declare type FieldFormatsStart = Omit & { - deserialize: FormatFactory; -}; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.md index b25d313129634e..247760305db9c3 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getkbntypenames.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getkbntypenames.md index 66effff722f3bd..db741f74f538d1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getkbntypenames.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getkbntypenames.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/field-types" package directly instead. 8.0 +> Import from the "@kbn/field-types" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldformat.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldformat.md deleted file mode 100644 index 4fe738ddef5dc2..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldformat.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IFieldFormat](./kibana-plugin-plugins-data-public.ifieldformat.md) - -## IFieldFormat type - -Signature: - -```typescript -export declare type IFieldFormat = FieldFormat; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldformatsregistry.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldformatsregistry.md deleted file mode 100644 index 0f83e2bb2d4238..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldformatsregistry.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IFieldFormatsRegistry](./kibana-plugin-plugins-data-public.ifieldformatsregistry.md) - -## IFieldFormatsRegistry type - -Signature: - -```typescript -declare type IFieldFormatsRegistry = PublicMethodsOf; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.md index ec723ddc00e723..8fe65d5a86de1f 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldtype.md index b4b31980926049..6a798c3a4add1e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldtype.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldtype.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Use [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) 8.0 +> Use [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md index 3f8048d2913fbe..99caa646c17b00 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> use runtime field instead 8.0 +> use runtime field instead 8.1 > Add scripted field to field list diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md index 70a9b624099cba..7a704b917daaf2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> use runtime field instead 8.0 +> use runtime field instead 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md index 517dec992c090e..cd91bdcebce608 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> use runtime field instead 8.0 +> use runtime field instead 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.intervalname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.intervalname.md index 9d26cf9730f461..774601daf4a87e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.intervalname.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.intervalname.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Used by time range index patterns 8.0 +> Used by time range index patterns 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md index fbd85a7d732300..052ccc2ae97b42 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> use runtime field instead 8.0 +> use runtime field instead 8.1 > Remove scripted field from field list diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilter.md index 8891620ad01efc..39da5c0548da08 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilter.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilters.md index 522a1c66bb80e8..047a9861002b52 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilters.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.md index 6c6904d6625c67..73d82c25228bb4 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.matchallfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.matchallfilter.md index 35ebdb1473c4ee..51d0f8a139da57 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.matchallfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.matchallfilter.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 49c9d1d143e6b6..c13f8282242ff4 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -13,7 +13,6 @@ | [AggParamType](./kibana-plugin-plugins-data-public.aggparamtype.md) | | | [DataPlugin](./kibana-plugin-plugins-data-public.dataplugin.md) | | | [DuplicateIndexPatternError](./kibana-plugin-plugins-data-public.duplicateindexpatternerror.md) | | -| [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) | | | [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) | | | [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) | | | [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) | | @@ -64,7 +63,6 @@ | [DataPublicPluginStart](./kibana-plugin-plugins-data-public.datapublicpluginstart.md) | Data plugin public Start contract | | [DataPublicPluginStartActions](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.md) | utilities to generate filters from action context | | [DataPublicPluginStartUi](./kibana-plugin-plugins-data-public.datapublicpluginstartui.md) | Data plugin prewired UI components | -| [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) | | | [IDataPluginServices](./kibana-plugin-plugins-data-public.idatapluginservices.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-public.iessearchrequest.md) | | | [IFieldType](./kibana-plugin-plugins-data-public.ifieldtype.md) | | @@ -104,7 +102,6 @@ | [AggGroupLabels](./kibana-plugin-plugins-data-public.agggrouplabels.md) | | | [AggGroupNames](./kibana-plugin-plugins-data-public.agggroupnames.md) | | | [APPLY\_FILTER\_TRIGGER](./kibana-plugin-plugins-data-public.apply_filter_trigger.md) | | -| [baseFormattersPublic](./kibana-plugin-plugins-data-public.baseformatterspublic.md) | | | [castEsToKbnFieldTypeName](./kibana-plugin-plugins-data-public.castestokbnfieldtypename.md) | | | [connectToQueryState](./kibana-plugin-plugins-data-public.connecttoquerystate.md) | Helper to setup two-way syncing of global data and a state container | | [createSavedQueryService](./kibana-plugin-plugins-data-public.createsavedqueryservice.md) | | @@ -114,7 +111,6 @@ | [esQuery](./kibana-plugin-plugins-data-public.esquery.md) | | | [exporters](./kibana-plugin-plugins-data-public.exporters.md) | | | [extractSearchSourceReferences](./kibana-plugin-plugins-data-public.extractsearchsourcereferences.md) | | -| [fieldFormats](./kibana-plugin-plugins-data-public.fieldformats.md) | | | [fieldList](./kibana-plugin-plugins-data-public.fieldlist.md) | | | [FilterItem](./kibana-plugin-plugins-data-public.filteritem.md) | | | [FilterLabel](./kibana-plugin-plugins-data-public.filterlabel.md) | | @@ -159,17 +155,11 @@ | [ExpressionFunctionKibana](./kibana-plugin-plugins-data-public.expressionfunctionkibana.md) | | | [ExpressionFunctionKibanaContext](./kibana-plugin-plugins-data-public.expressionfunctionkibanacontext.md) | | | [ExpressionValueSearchContext](./kibana-plugin-plugins-data-public.expressionvaluesearchcontext.md) | | -| [FieldFormatId](./kibana-plugin-plugins-data-public.fieldformatid.md) | id type is needed for creating custom converters. | -| [FieldFormatsContentType](./kibana-plugin-plugins-data-public.fieldformatscontenttype.md) | \* | -| [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md) | | -| [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) | | | [Filter](./kibana-plugin-plugins-data-public.filter.md) | | | [IAggConfig](./kibana-plugin-plugins-data-public.iaggconfig.md) | AggConfig This class represents an aggregation, which is displayed in the left-hand nav of the Visualize app. | | [IAggType](./kibana-plugin-plugins-data-public.iaggtype.md) | | | [IEsError](./kibana-plugin-plugins-data-public.ieserror.md) | | | [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) | | -| [IFieldFormat](./kibana-plugin-plugins-data-public.ifieldformat.md) | | -| [IFieldFormatsRegistry](./kibana-plugin-plugins-data-public.ifieldformatsregistry.md) | | | [IFieldParamType](./kibana-plugin-plugins-data-public.ifieldparamtype.md) | | | [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) | | | [IMetricAggType](./kibana-plugin-plugins-data-public.imetricaggtype.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasefilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasefilter.md index 850aff0f6cd85d..cc1007655ecf38 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasefilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasefilter.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasesfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasesfilter.md index a28dba5b1cd3fd..48a41dc34497f7 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasesfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasesfilter.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilter.md index 91dd7c13c472e7..5d452d759c9342 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilter.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefiltermeta.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefiltermeta.md index 291dc90c3971df..eefb773f5727f3 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefiltermeta.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefiltermeta.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.md index 42db2d353befaf..df78be16e6d018 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md index 977f875b214e5e..b00a7f31318eb5 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Use the `fetch$` method instead 8.0 +> Use the `fetch$` method instead 8.1 > Fetch this source and reject the returned Promise on error diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md index bdfc881878efd6..fba9afd6f6648c 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md @@ -24,13 +24,6 @@ UI_SETTINGS: { readonly HISTOGRAM_BAR_TARGET: "histogram:barTarget"; readonly HISTOGRAM_MAX_BARS: "histogram:maxBars"; readonly HISTORY_LIMIT: "history:limit"; - readonly SHORT_DOTS_ENABLE: "shortDots:enable"; - readonly FORMAT_DEFAULT_TYPE_MAP: "format:defaultTypeMap"; - readonly FORMAT_NUMBER_DEFAULT_PATTERN: "format:number:defaultPattern"; - readonly FORMAT_PERCENT_DEFAULT_PATTERN: "format:percent:defaultPattern"; - readonly FORMAT_BYTES_DEFAULT_PATTERN: "format:bytes:defaultPattern"; - readonly FORMAT_CURRENCY_DEFAULT_PATTERN: "format:currency:defaultPattern"; - readonly FORMAT_NUMBER_DEFAULT_LOCALE: "format:number:defaultLocale"; readonly TIMEPICKER_REFRESH_INTERVAL_DEFAULTS: "timepicker:refreshIntervalDefaults"; readonly TIMEPICKER_QUICK_RANGES: "timepicker:quickRanges"; readonly TIMEPICKER_TIME_DEFAULTS: "timepicker:timeDefaults"; diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.castestokbnfieldtypename.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.castestokbnfieldtypename.md index 834fe302ad9f8d..a3f92491e8983c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.castestokbnfieldtypename.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.castestokbnfieldtypename.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/field-types" package directly instead. 8.0 +> Import from the "@kbn/field-types" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.es.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.es.md deleted file mode 100644 index a3c3ddc4e1649b..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.es.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) > [es](./kibana-plugin-plugins-data-server.fieldformatconfig.es.md) - -## FieldFormatConfig.es property - -Signature: - -```typescript -es?: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.id.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.id.md deleted file mode 100644 index c5173fba225331..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) > [id](./kibana-plugin-plugins-data-server.fieldformatconfig.id.md) - -## FieldFormatConfig.id property - -Signature: - -```typescript -id: FieldFormatId; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.md deleted file mode 100644 index 5d5c13784bc57d..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) - -## FieldFormatConfig interface - -Signature: - -```typescript -export interface FieldFormatConfig -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [es](./kibana-plugin-plugins-data-server.fieldformatconfig.es.md) | boolean | | -| [id](./kibana-plugin-plugins-data-server.fieldformatconfig.id.md) | FieldFormatId | | -| [params](./kibana-plugin-plugins-data-server.fieldformatconfig.params.md) | Record<string, any> | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.params.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.params.md deleted file mode 100644 index c5e5faef308600..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatconfig.params.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) > [params](./kibana-plugin-plugins-data-server.fieldformatconfig.params.md) - -## FieldFormatConfig.params property - -Signature: - -```typescript -params: Record; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformats.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformats.md deleted file mode 100644 index 42563051bcdc4b..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformats.md +++ /dev/null @@ -1,28 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [fieldFormats](./kibana-plugin-plugins-data-server.fieldformats.md) - -## fieldFormats variable - -Signature: - -```typescript -fieldFormats: { - FieldFormatsRegistry: typeof FieldFormatsRegistry; - FieldFormat: typeof FieldFormat; - BoolFormat: typeof BoolFormat; - BytesFormat: typeof BytesFormat; - ColorFormat: typeof ColorFormat; - DurationFormat: typeof DurationFormat; - IpFormat: typeof IpFormat; - NumberFormat: typeof NumberFormat; - PercentFormat: typeof PercentFormat; - RelativeDateFormat: typeof RelativeDateFormat; - SourceFormat: typeof SourceFormat; - StaticLookupFormat: typeof StaticLookupFormat; - UrlFormat: typeof UrlFormat; - StringFormat: typeof StringFormat; - TruncateFormat: typeof TruncateFormat; - HistogramFormat: typeof HistogramFormat; -} -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md deleted file mode 100644 index a3f6c709246ea2..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md) - -## FieldFormatsGetConfigFn type - -Signature: - -```typescript -export declare type FieldFormatsGetConfigFn = GetConfigFn; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldformatsregistry.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldformatsregistry.md deleted file mode 100644 index c284d2c87fcaa2..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldformatsregistry.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IFieldFormatsRegistry](./kibana-plugin-plugins-data-server.ifieldformatsregistry.md) - -## IFieldFormatsRegistry type - -Signature: - -```typescript -declare type IFieldFormatsRegistry = PublicMethodsOf; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.md index 9ac9ba1abc637c..7f812fb08fd51a 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldtype.md index 932afceb5d22bb..93f91f6264dbca 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldtype.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldtype.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Use [IndexPatternField](./kibana-plugin-plugins-data-server.indexpatternfield.md) 8.0 +> Use [IndexPatternField](./kibana-plugin-plugins-data-server.indexpatternfield.md) 8.1 > Signature: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md index 9fe169233af13d..829cc9c0752a08 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> use runtime field instead 8.0 +> use runtime field instead 8.1 > Add scripted field to field list diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md index 8a06b394ae5549..deb71a9df8cc59 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> use runtime field instead 8.0 +> use runtime field instead 8.1 > Signature: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md index 15978b64149479..3beef6e08ed3f9 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> use runtime field instead 8.0 +> use runtime field instead 8.1 > Signature: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md index e2810cc21a378e..01367d931a841a 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Used by time range index patterns 8.0 +> Used by time range index patterns 8.1 > Signature: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md index bc96d3596ed12e..c72ad5163d4ec1 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> use runtime field instead 8.0 +> use runtime field instead 8.1 > Remove scripted field from field list diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.md index 0bdb5d6726fa83..f56300757736ac 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Import from the "@kbn/es-query" package directly instead. 8.0 +> Import from the "@kbn/es-query" package directly instead. 8.1 > Signature: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 2e76e3b0c6ad4d..0363aa1ce64424 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -49,7 +49,6 @@ | [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md) | | | [AsyncSearchStatusResponse](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse.md) | | | [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) | | -| [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) | | | [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) | | | [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) | Interface for an index pattern saved object | @@ -80,7 +79,6 @@ | [esKuery](./kibana-plugin-plugins-data-server.eskuery.md) | | | [esQuery](./kibana-plugin-plugins-data-server.esquery.md) | | | [exporters](./kibana-plugin-plugins-data-server.exporters.md) | | -| [fieldFormats](./kibana-plugin-plugins-data-server.fieldformats.md) | | | [INDEX\_PATTERN\_SAVED\_OBJECT\_TYPE](./kibana-plugin-plugins-data-server.index_pattern_saved_object_type.md) | \* | | [indexPatterns](./kibana-plugin-plugins-data-server.indexpatterns.md) | | | [mergeCapabilitiesWithFields](./kibana-plugin-plugins-data-server.mergecapabilitieswithfields.md) | | @@ -100,12 +98,10 @@ | [ExpressionFunctionKibana](./kibana-plugin-plugins-data-server.expressionfunctionkibana.md) | | | [ExpressionFunctionKibanaContext](./kibana-plugin-plugins-data-server.expressionfunctionkibanacontext.md) | | | [ExpressionValueSearchContext](./kibana-plugin-plugins-data-server.expressionvaluesearchcontext.md) | | -| [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md) | | | [Filter](./kibana-plugin-plugins-data-server.filter.md) | | | [IAggConfig](./kibana-plugin-plugins-data-server.iaggconfig.md) | AggConfig This class represents an aggregation, which is displayed in the left-hand nav of the Visualize app. | | [IAggType](./kibana-plugin-plugins-data-server.iaggtype.md) | | | [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) | | -| [IFieldFormatsRegistry](./kibana-plugin-plugins-data-server.ifieldformatsregistry.md) | | | [IFieldParamType](./kibana-plugin-plugins-data-server.ifieldparamtype.md) | | | [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | | | [IMetricAggType](./kibana-plugin-plugins-data-server.imetricaggtype.md) | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md index bd90f23b4ab590..5763cb2dacb7ac 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md @@ -7,12 +7,10 @@ Signature: ```typescript -setup(core: CoreSetup, { bfetch, expressions, usageCollection }: DataPluginSetupDependencies): { +setup(core: CoreSetup, { bfetch, expressions, usageCollection, fieldFormats }: DataPluginSetupDependencies): { __enhance: (enhancements: DataEnhancements) => void; search: ISearchSetup; - fieldFormats: { - register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number; - }; + fieldFormats: FieldFormatsSetup; }; ``` @@ -21,15 +19,13 @@ setup(core: CoreSetup, { bfetch, e | Parameter | Type | Description | | --- | --- | --- | | core | CoreSetup<DataPluginStartDependencies, DataPluginStart> | | -| { bfetch, expressions, usageCollection } | DataPluginSetupDependencies | | +| { bfetch, expressions, usageCollection, fieldFormats } | DataPluginSetupDependencies | | Returns: `{ __enhance: (enhancements: DataEnhancements) => void; search: ISearchSetup; - fieldFormats: { - register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number; - }; + fieldFormats: FieldFormatsSetup; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md index dd1f3806c14081..a31e4512cfcf1b 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md @@ -7,10 +7,8 @@ Signature: ```typescript -start(core: CoreStart): { - fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; - }; +start(core: CoreStart, { fieldFormats }: DataPluginStartDependencies): { + fieldFormats: FieldFormatsStart; indexPatterns: { indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; }; @@ -23,13 +21,12 @@ start(core: CoreStart): { | Parameter | Type | Description | | --- | --- | --- | | core | CoreStart | | +| { fieldFormats } | DataPluginStartDependencies | | Returns: `{ - fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; - }; + fieldFormats: FieldFormatsStart; indexPatterns: { indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; }; diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginsetup.fieldformats.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginsetup.fieldformats.md index 648e23e597f4d1..55badaf2d07bc6 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginsetup.fieldformats.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginsetup.fieldformats.md @@ -4,6 +4,11 @@ ## PluginSetup.fieldFormats property +> Warning: This API is now obsolete. +> +> - use "fieldFormats" plugin directly instead +> + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.fieldformats.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.fieldformats.md index 7a77c6c943cd7e..26182e96a4a7e9 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.fieldformats.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.fieldformats.md @@ -4,6 +4,11 @@ ## PluginStart.fieldFormats property +> Warning: This API is now obsolete. +> +> - use "fieldFormats" plugin directly instead +> + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md index 387dac22e1c83b..e1f002674ef6db 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md @@ -24,13 +24,6 @@ UI_SETTINGS: { readonly HISTOGRAM_BAR_TARGET: "histogram:barTarget"; readonly HISTOGRAM_MAX_BARS: "histogram:maxBars"; readonly HISTORY_LIMIT: "history:limit"; - readonly SHORT_DOTS_ENABLE: "shortDots:enable"; - readonly FORMAT_DEFAULT_TYPE_MAP: "format:defaultTypeMap"; - readonly FORMAT_NUMBER_DEFAULT_PATTERN: "format:number:defaultPattern"; - readonly FORMAT_PERCENT_DEFAULT_PATTERN: "format:percent:defaultPattern"; - readonly FORMAT_BYTES_DEFAULT_PATTERN: "format:bytes:defaultPattern"; - readonly FORMAT_CURRENCY_DEFAULT_PATTERN: "format:currency:defaultPattern"; - readonly FORMAT_NUMBER_DEFAULT_LOCALE: "format:number:defaultLocale"; readonly TIMEPICKER_REFRESH_INTERVAL_DEFAULTS: "timepicker:refreshIntervalDefaults"; readonly TIMEPICKER_QUICK_RANGES: "timepicker:quickRanges"; readonly TIMEPICKER_TIME_DEFAULTS: "timepicker:timeDefaults"; diff --git a/docs/user/monitoring/kibana-alerts.asciidoc b/docs/user/monitoring/kibana-alerts.asciidoc index beaae1fdb71b68..f00a3999ab2775 100644 --- a/docs/user/monitoring/kibana-alerts.asciidoc +++ b/docs/user/monitoring/kibana-alerts.asciidoc @@ -11,8 +11,7 @@ specific needs. [role="screenshot"] image::user/monitoring/images/monitoring-kibana-alerting-notification.png["{kib} alerting notifications in {stack-monitor-app}"] -When you open *{stack-monitor-app}*, you will be ask to create these rules -They are initially configured to detect and notify on various +When you open *{stack-monitor-app}* for the first time, you will be asked to acknowledge the creation of these default rules. They are initially configured to detect and notify on various conditions across your monitored clusters. You can view notifications for: *Cluster health*, *Resource utilization*, and *Errors and exceptions* for {es} in real time. @@ -23,8 +22,8 @@ been recreated as rules in {kib} {alert-features}. For this reason, the existing The default action for all {stack-monitor-app} rules is to write to {kib} logs and display a notification in the UI. -To review and modify all available rules, click *Enter setup mode* on the -*Cluster overview* page. +To review and modify existing *{stack-monitor-app}* rules, click *Enter setup mode* on the *Cluster overview* page. +Alternatively, to manage all rules, including create and delete functionality go to *Stack Management > Rules and Connectors*. [discrete] [[kibana-alerts-cpu-threshold]] diff --git a/package.json b/package.json index b148eae39cbb35..107f7b7df23fee 100644 --- a/package.json +++ b/package.json @@ -438,7 +438,6 @@ "@babel/plugin-proposal-object-rest-spread": "^7.12.1", "@babel/plugin-proposal-optional-chaining": "^7.12.7", "@babel/plugin-proposal-private-methods": "^7.12.1", - "@babel/plugin-transform-modules-commonjs": "^7.12.1", "@babel/plugin-transform-runtime": "^7.12.10", "@babel/preset-env": "^7.12.11", "@babel/preset-react": "^7.12.10", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index e814fe9db5e3a7..3755744d73184a 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -13,7 +13,7 @@ pageLoadAssetSize: dashboard: 374194 dashboardEnhanced: 65646 dashboardMode: 22716 - data: 900000 + data: 824229 dataEnhanced: 50420 devTools: 38637 discover: 99999 @@ -24,6 +24,7 @@ pageLoadAssetSize: esUiShared: 326654 expressions: 224136 features: 21723 + fieldFormats: 92628 globalSearch: 29696 globalSearchBar: 50403 globalSearchProviders: 25554 diff --git a/packages/kbn-rule-data-utils/src/technical_field_names.ts b/packages/kbn-rule-data-utils/src/technical_field_names.ts index c55be6cfe8ff68..76e4a8803fd343 100644 --- a/packages/kbn-rule-data-utils/src/technical_field_names.ts +++ b/packages/kbn-rule-data-utils/src/technical_field_names.ts @@ -74,6 +74,12 @@ const ALERT_RULE_UPDATED_AT = `${ALERT_RULE_NAMESPACE}.updated_at` as const; const ALERT_RULE_UPDATED_BY = `${ALERT_RULE_NAMESPACE}.updated_by` as const; const ALERT_RULE_VERSION = `${ALERT_RULE_NAMESPACE}.version` as const; +const namespaces = { + KIBANA_NAMESPACE, + ALERT_NAMESPACE, + ALERT_RULE_NAMESPACE, +}; + const fields = { CONSUMERS, ECS_VERSION, @@ -142,6 +148,8 @@ export { ALERT_EVALUATION_THRESHOLD, ALERT_EVALUATION_VALUE, ALERT_ID, + ALERT_NAMESPACE, + ALERT_RULE_NAMESPACE, ALERT_OWNER, ALERT_PRODUCER, ALERT_REASON, @@ -185,6 +193,7 @@ export { ECS_VERSION, EVENT_ACTION, EVENT_KIND, + KIBANA_NAMESPACE, RULE_CATEGORY, RULE_CONSUMERS, RULE_ID, @@ -196,4 +205,4 @@ export { VERSION, }; -export type TechnicalRuleDataFieldName = ValuesType; +export type TechnicalRuleDataFieldName = ValuesType; diff --git a/packages/kbn-server-route-repository/.babelrc b/packages/kbn-server-route-repository/.babelrc new file mode 100644 index 00000000000000..7da72d17791281 --- /dev/null +++ b/packages/kbn-server-route-repository/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"] +} diff --git a/packages/kbn-server-route-repository/BUILD.bazel b/packages/kbn-server-route-repository/BUILD.bazel index 3a146086e80bc3..147b72c65f9543 100644 --- a/packages/kbn-server-route-repository/BUILD.bazel +++ b/packages/kbn-server-route-repository/BUILD.bazel @@ -1,5 +1,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-server-route-repository" PKG_REQUIRE_NAME = "@kbn/server-route-repository" @@ -25,23 +26,31 @@ NPM_MODULE_EXTRA_FILES = [ "README.md" ] -SRC_DEPS = [ +RUNTIME_DEPS = [ "//packages/kbn-config-schema", "//packages/kbn-io-ts-utils", "@npm//@hapi/boom", "@npm//fp-ts", - "@npm//io-ts", "@npm//lodash", "@npm//utility-types" ] TYPES_DEPS = [ + "//packages/kbn-config-schema", + "//packages/kbn-io-ts-utils", + "@npm//@hapi/boom", + "@npm//fp-ts", + "@npm//utility-types", "@npm//@types/jest", "@npm//@types/lodash", "@npm//@types/node", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) ts_config( name = "tsconfig", @@ -52,14 +61,15 @@ ts_config( ) ts_project( - name = "tsc", + name = "tsc_types", args = ['--pretty'], srcs = SRCS, - deps = DEPS, + deps = TYPES_DEPS, declaration = True, declaration_map = True, - incremental = True, - out_dir = "target", + emit_declaration_only = True, + incremental = False, + out_dir = "target_types", source_map = True, root_dir = "src", tsconfig = ":tsconfig", @@ -68,7 +78,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = DEPS + [":tsc"], + deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-server-route-repository/package.json b/packages/kbn-server-route-repository/package.json index cfeab275e19cf6..920abc2a3836f9 100644 --- a/packages/kbn-server-route-repository/package.json +++ b/packages/kbn-server-route-repository/package.json @@ -1,7 +1,7 @@ { "name": "@kbn/server-route-repository", - "main": "./target/index.js", - "types": "./target/index.d.ts", + "main": "./target_node/index.js", + "types": "./target_types/index.d.ts", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", "private": true diff --git a/packages/kbn-server-route-repository/tsconfig.json b/packages/kbn-server-route-repository/tsconfig.json index 7614a9411602ef..947744bc68d425 100644 --- a/packages/kbn-server-route-repository/tsconfig.json +++ b/packages/kbn-server-route-repository/tsconfig.json @@ -1,13 +1,15 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "incremental": true, - "outDir": "./target", - "stripInternal": false, "declaration": true, "declarationMap": true, + "emitDeclarationOnly": true, + "incremental": false, + "outDir": "./target_types", + "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-server-route-repository/src", + "stripInternal": false, "types": [ "jest", "node" @@ -15,6 +17,6 @@ "noUnusedLocals": false }, "include": [ - "./src/**/*.ts" + "src/**/*.ts" ] } diff --git a/packages/kbn-telemetry-tools/.babelrc b/packages/kbn-telemetry-tools/.babelrc new file mode 100644 index 00000000000000..7da72d17791281 --- /dev/null +++ b/packages/kbn-telemetry-tools/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@kbn/babel-preset/node_preset"] +} diff --git a/packages/kbn-telemetry-tools/BUILD.bazel b/packages/kbn-telemetry-tools/BUILD.bazel index cf71b3e197dad1..f90ca9b22dc922 100644 --- a/packages/kbn-telemetry-tools/BUILD.bazel +++ b/packages/kbn-telemetry-tools/BUILD.bazel @@ -1,5 +1,6 @@ load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") +load("//src/dev/bazel:index.bzl", "jsts_transpiler") PKG_BASE_NAME = "kbn-telemetry-tools" PKG_REQUIRE_NAME = "@kbn/telemetry-tools" @@ -28,29 +29,30 @@ NPM_MODULE_EXTRA_FILES = [ "GUIDELINE.md", ] -SRC_DEPS = [ +RUNTIME_DEPS = [ "//packages/kbn-dev-utils", "//packages/kbn-utility-types", "@npm//glob", "@npm//listr", "@npm//normalize-path", - "@npm//tslib", ] TYPES_DEPS = [ - "@npm//@types/flot", + "//packages/kbn-dev-utils", + "//packages/kbn-utility-types", "@npm//@types/glob", "@npm//@types/jest", "@npm//@types/listr", "@npm//@types/lodash", "@npm//@types/node", "@npm//@types/normalize-path", - "@npm//@types/testing-library__jest-dom", - "@npm//resize-observer-polyfill", - "@npm//@emotion/react", ] -DEPS = SRC_DEPS + TYPES_DEPS +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) ts_config( name = "tsconfig", @@ -61,14 +63,15 @@ ts_config( ) ts_project( - name = "tsc", + name = "tsc_types", args = ['--pretty'], srcs = SRCS, - deps = DEPS, + deps = TYPES_DEPS, declaration = True, declaration_map = True, - incremental = True, - out_dir = "target", + emit_declaration_only = True, + incremental = False, + out_dir = "target_types", source_map = True, root_dir = "src", tsconfig = ":tsconfig", @@ -77,7 +80,7 @@ ts_project( js_library( name = PKG_BASE_NAME, srcs = NPM_MODULE_EXTRA_FILES, - deps = DEPS + [":tsc"], + deps = RUNTIME_DEPS + [":target_node", ":tsc_types"], package_name = PKG_REQUIRE_NAME, visibility = ["//visibility:public"], ) diff --git a/packages/kbn-telemetry-tools/package.json b/packages/kbn-telemetry-tools/package.json index cd3fd3aa966c77..cb281524abefe5 100644 --- a/packages/kbn-telemetry-tools/package.json +++ b/packages/kbn-telemetry-tools/package.json @@ -2,8 +2,8 @@ "name": "@kbn/telemetry-tools", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", - "main": "./target/index.js", - "types": "./target/index.d.ts", + "main": "./target_node/index.js", + "types": "./target_types/index.d.ts", "private": true, "kibana": { "devOnly": true diff --git a/packages/kbn-telemetry-tools/tsconfig.json b/packages/kbn-telemetry-tools/tsconfig.json index 3c0e04ded359d4..1f1b4aea5d5c6b 100644 --- a/packages/kbn-telemetry-tools/tsconfig.json +++ b/packages/kbn-telemetry-tools/tsconfig.json @@ -1,14 +1,15 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "incremental": true, - "outDir": "./target", "declaration": true, "declarationMap": true, + "emitDeclarationOnly": true, + "incremental": false, + "isolatedModules": true, + "outDir": "./target_types", "rootDir": "src", "sourceMap": true, "sourceRoot": "../../../../packages/kbn-telemetry-tools/src", - "isolatedModules": true, "types": [ "jest", "node" diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 644dc32dd81409..e2d81c5ae17520 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -191,6 +191,7 @@ kibana_vars=( telemetry.enabled telemetry.optIn telemetry.optInStatusUrl + telemetry.sendUsageTo telemetry.sendUsageFrom tilemap.options.attribution tilemap.options.maxZoom diff --git a/src/plugins/charts/public/index.ts b/src/plugins/charts/public/index.ts index cc1a54c2e25b09..0eec6f14eff649 100644 --- a/src/plugins/charts/public/index.ts +++ b/src/plugins/charts/public/index.ts @@ -15,6 +15,8 @@ export { ChartsPluginSetup, ChartsPluginStart } from './plugin'; export * from './static'; export * from './services/palettes/types'; export { lightenColor } from './services/palettes/lighten_color'; +export { useActiveCursor } from './services/active_cursor'; + export { PaletteOutput, CustomPaletteArguments, diff --git a/src/plugins/charts/public/mocks.ts b/src/plugins/charts/public/mocks.ts index c85a91a1ef5636..7460962c3e1fa6 100644 --- a/src/plugins/charts/public/mocks.ts +++ b/src/plugins/charts/public/mocks.ts @@ -8,6 +8,7 @@ import { ChartsPlugin } from './plugin'; import { themeServiceMock } from './services/theme/mock'; +import { activeCursorMock } from './services/active_cursor/mock'; import { colorsServiceMock } from './services/legacy_colors/mock'; import { getPaletteRegistry, paletteServiceMock } from './services/palettes/mock'; @@ -23,6 +24,7 @@ const createSetupContract = (): Setup => ({ const createStartContract = (): Start => ({ legacyColors: colorsServiceMock, theme: themeServiceMock, + activeCursor: activeCursorMock, palettes: paletteServiceMock.setup({} as any), }); diff --git a/src/plugins/charts/public/plugin.ts b/src/plugins/charts/public/plugin.ts index 5bc0b8c84560f4..7802cef3b4934b 100644 --- a/src/plugins/charts/public/plugin.ts +++ b/src/plugins/charts/public/plugin.ts @@ -12,6 +12,7 @@ import { palette, systemPalette } from '../common'; import { ThemeService, LegacyColorsService } from './services'; import { PaletteService } from './services/palettes/service'; +import { ActiveCursor } from './services/active_cursor'; export type Theme = Omit; export type Color = Omit; @@ -28,13 +29,16 @@ export interface ChartsPluginSetup { } /** @public */ -export type ChartsPluginStart = ChartsPluginSetup; +export type ChartsPluginStart = ChartsPluginSetup & { + activeCursor: ActiveCursor; +}; /** @public */ export class ChartsPlugin implements Plugin { private readonly themeService = new ThemeService(); private readonly legacyColorsService = new LegacyColorsService(); private readonly paletteService = new PaletteService(); + private readonly activeCursor = new ActiveCursor(); private palettes: undefined | ReturnType; @@ -45,6 +49,8 @@ export class ChartsPlugin implements Plugin { + let activeCursor: ActiveCursor; + + beforeEach(() => { + activeCursor = new ActiveCursor(); + }); + + test('should initialize activeCursor$ stream on setup hook', () => { + expect(activeCursor.activeCursor$).toBeUndefined(); + + activeCursor.setup(); + + expect(activeCursor.activeCursor$).toMatchInlineSnapshot(` + Subject { + "_isScalar": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [], + "thrownError": null, + } + `); + }); +}); diff --git a/src/plugins/charts/public/services/active_cursor/active_cursor.ts b/src/plugins/charts/public/services/active_cursor/active_cursor.ts new file mode 100644 index 00000000000000..60aefc29169bc2 --- /dev/null +++ b/src/plugins/charts/public/services/active_cursor/active_cursor.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Subject } from 'rxjs'; +import type { ActiveCursorPayload } from './types'; + +export class ActiveCursor { + public activeCursor$?: Subject; + + setup() { + this.activeCursor$ = new Subject(); + } +} diff --git a/src/plugins/charts/public/services/active_cursor/active_cursor_utils.test.ts b/src/plugins/charts/public/services/active_cursor/active_cursor_utils.test.ts new file mode 100644 index 00000000000000..3230c1ec0f32b5 --- /dev/null +++ b/src/plugins/charts/public/services/active_cursor/active_cursor_utils.test.ts @@ -0,0 +1,141 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { parseSyncOptions } from './active_cursor_utils'; +import type { Datatable } from '../../../../expressions/public'; + +describe('active_cursor_utils', () => { + describe('parseSyncOptions', () => { + describe('dateHistogramSyncOption', () => { + test('should return isDateHistogram true in case if that mode is active', () => { + expect(parseSyncOptions({ isDateHistogram: true })).toMatchInlineSnapshot(` + Object { + "isDateHistogram": true, + } + `); + }); + + test('should return isDateHistogram false for other cases', () => { + expect(parseSyncOptions({ datatables: [] as Datatable[] })).toMatchInlineSnapshot(` + Object { + "accessors": Array [], + "isDateHistogram": false, + } + `); + }); + }); + + describe('datatablesSyncOption', () => { + test('should extract accessors', () => { + expect( + parseSyncOptions({ + datatables: ([ + { + columns: [ + { + meta: { + index: 'foo_index', + field: 'foo_field', + }, + }, + ], + }, + ] as unknown) as Datatable[], + }).accessors + ).toMatchInlineSnapshot(` + Array [ + "foo_index:foo_field", + ] + `); + }); + + test('should return isDateHistogram true in case all datatables is time based', () => { + expect( + parseSyncOptions({ + datatables: ([ + { + columns: [ + { + meta: { + index: 'foo_index', + field: 'foo_field', + sourceParams: { + appliedTimeRange: {}, + }, + }, + }, + ], + }, + { + columns: [ + { + meta: { + index: 'foo_index1', + field: 'foo_field1', + sourceParams: { + appliedTimeRange: {}, + }, + }, + }, + ], + }, + ] as unknown) as Datatable[], + }) + ).toMatchInlineSnapshot(` + Object { + "accessors": Array [ + "foo_index:foo_field", + "foo_index1:foo_field1", + ], + "isDateHistogram": true, + } + `); + }); + + test('should return isDateHistogram false in case of not all datatables is time based', () => { + expect( + parseSyncOptions({ + datatables: ([ + { + columns: [ + { + meta: { + index: 'foo_index', + field: 'foo_field', + sourceParams: { + appliedTimeRange: {}, + }, + }, + }, + ], + }, + { + columns: [ + { + meta: { + index: 'foo_index1', + field: 'foo_field1', + }, + }, + ], + }, + ] as unknown) as Datatable[], + }) + ).toMatchInlineSnapshot(` + Object { + "accessors": Array [ + "foo_index:foo_field", + "foo_index1:foo_field1", + ], + "isDateHistogram": false, + } + `); + }); + }); + }); +}); diff --git a/src/plugins/charts/public/services/active_cursor/active_cursor_utils.ts b/src/plugins/charts/public/services/active_cursor/active_cursor_utils.ts new file mode 100644 index 00000000000000..37eeb6bf2d8207 --- /dev/null +++ b/src/plugins/charts/public/services/active_cursor/active_cursor_utils.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { uniq } from 'lodash'; + +import type { Datatable } from '../../../../expressions/public'; +import type { ActiveCursorSyncOption, DateHistogramSyncOption } from './types'; +import type { ActiveCursorPayload } from './types'; + +function isDateHistogramSyncOption( + syncOption?: ActiveCursorSyncOption +): syncOption is DateHistogramSyncOption { + return Boolean(syncOption && 'isDateHistogram' in syncOption); +} + +const parseDatatable = (dataTables: Datatable[]) => { + const isDateHistogram = + Boolean(dataTables.length) && + dataTables.every((dataTable) => + dataTable.columns.some((c) => Boolean(c.meta.sourceParams?.appliedTimeRange)) + ); + + const accessors = uniq( + dataTables + .map((dataTable) => { + const column = dataTable.columns.find((c) => c.meta.index && c.meta.field); + + if (column?.meta.index) { + return `${column.meta.index}:${column.meta.field}`; + } + }) + .filter(Boolean) as string[] + ); + return { isDateHistogram, accessors }; +}; + +/** @internal **/ +export const parseSyncOptions = ( + syncOptions: ActiveCursorSyncOption +): Partial => + isDateHistogramSyncOption(syncOptions) + ? { + isDateHistogram: syncOptions.isDateHistogram, + } + : parseDatatable(syncOptions.datatables); diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/lib/active_cursor.ts b/src/plugins/charts/public/services/active_cursor/index.ts similarity index 71% rename from src/plugins/vis_type_timeseries/public/application/visualizations/lib/active_cursor.ts rename to src/plugins/charts/public/services/active_cursor/index.ts index 7f7f62fd6a9dab..7c1fa88354a7cc 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/lib/active_cursor.ts +++ b/src/plugins/charts/public/services/active_cursor/index.ts @@ -6,7 +6,5 @@ * Side Public License, v 1. */ -import { Subject } from 'rxjs'; -import { PointerEvent } from '@elastic/charts'; - -export const activeCursor$ = new Subject(); +export { ActiveCursor } from './active_cursor'; +export { useActiveCursor } from './use_active_cursor'; diff --git a/src/plugins/charts/public/services/active_cursor/mock.ts b/src/plugins/charts/public/services/active_cursor/mock.ts new file mode 100644 index 00000000000000..118575b9b96fde --- /dev/null +++ b/src/plugins/charts/public/services/active_cursor/mock.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ActiveCursor } from './active_cursor'; + +export const activeCursorMock: ActiveCursor = { + activeCursor$: { + subscribe: jest.fn(), + pipe: jest.fn(() => ({ + subscribe: jest.fn(), + })), + }, + setup: jest.fn(), +} as any; diff --git a/src/plugins/charts/public/services/active_cursor/types.ts b/src/plugins/charts/public/services/active_cursor/types.ts new file mode 100644 index 00000000000000..0b0180a622efbf --- /dev/null +++ b/src/plugins/charts/public/services/active_cursor/types.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { PointerEvent } from '@elastic/charts'; +import type { Datatable } from '../../../../expressions/public'; + +/** @public **/ +export type ActiveCursorSyncOption = DateHistogramSyncOption | DatatablesSyncOption; + +/** @internal **/ +export interface ActiveCursorPayload { + cursor: PointerEvent; + isDateHistogram?: boolean; + accessors?: string[]; +} + +/** @internal **/ +interface BaseSyncOptions { + debounce?: number; +} + +/** @internal **/ +export interface DateHistogramSyncOption extends BaseSyncOptions { + isDateHistogram: boolean; +} + +/** @internal **/ +export interface DatatablesSyncOption extends BaseSyncOptions { + datatables: Datatable[]; +} diff --git a/src/plugins/charts/public/services/active_cursor/use_active_cursor.test.ts b/src/plugins/charts/public/services/active_cursor/use_active_cursor.test.ts new file mode 100644 index 00000000000000..efe5c9b49849f0 --- /dev/null +++ b/src/plugins/charts/public/services/active_cursor/use_active_cursor.test.ts @@ -0,0 +1,183 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { renderHook } from '@testing-library/react-hooks'; +import type { RefObject } from 'react'; + +import { ActiveCursor } from './active_cursor'; +import { useActiveCursor } from './use_active_cursor'; + +import type { ActiveCursorSyncOption, ActiveCursorPayload } from './types'; +import type { Chart, PointerEvent } from '@elastic/charts'; +import type { Datatable } from '../../../../expressions/public'; + +describe('useActiveCursor', () => { + let cursor: ActiveCursorPayload['cursor']; + let dispatchExternalPointerEvent: jest.Mock; + + const act = ( + syncOption: ActiveCursorSyncOption, + events: Array>, + eventsTimeout = 1 + ) => + new Promise(async (resolve) => { + const activeCursor = new ActiveCursor(); + let allEventsExecuted = false; + + activeCursor.setup(); + + dispatchExternalPointerEvent.mockImplementation((pointerEvent) => { + if (allEventsExecuted) { + resolve(pointerEvent); + } + }); + + renderHook(() => + useActiveCursor( + activeCursor, + { + current: { + dispatchExternalPointerEvent: dispatchExternalPointerEvent as ( + pointerEvent: PointerEvent + ) => void, + }, + } as RefObject, + { ...syncOption, debounce: syncOption.debounce ?? 1 } + ) + ); + + for (const e of events) { + await new Promise((eventResolve) => + setTimeout(() => { + if (e === events[events.length - 1]) { + allEventsExecuted = true; + } + activeCursor.activeCursor$!.next({ cursor, ...e }); + eventResolve(null); + }, eventsTimeout) + ); + } + }); + + beforeEach(() => { + cursor = {} as ActiveCursorPayload['cursor']; + dispatchExternalPointerEvent = jest.fn(); + }); + + test('should debounce events', async () => { + await act( + { + debounce: 5, + datatables: [ + { + columns: [ + { + meta: { + index: 'foo_index', + field: 'foo_field', + }, + }, + ], + }, + ] as Datatable[], + }, + [ + { accessors: ['foo_index:foo_field'] }, + { accessors: ['foo_index:foo_field'] }, + { accessors: ['foo_index:foo_field'] }, + { accessors: ['foo_index:foo_field'] }, + ] + ); + + expect(dispatchExternalPointerEvent).toHaveBeenCalledTimes(1); + }); + + test('should trigger cursor pointer update (chart type: time, event type: time)', async () => { + await act({ isDateHistogram: true }, [{ isDateHistogram: true }]); + + expect(dispatchExternalPointerEvent).toHaveBeenCalledTimes(1); + }); + + test('should trigger cursor pointer update (chart type: datatable - time based, event type: time)', async () => { + await act( + { + datatables: ([ + { + columns: [ + { + meta: { + index: 'foo_index', + field: 'foo_field', + sourceParams: { + appliedTimeRange: {}, + }, + }, + }, + ], + }, + ] as unknown) as Datatable[], + }, + [{ isDateHistogram: true }, { accessors: ['foo_index:foo_field'] }] + ); + + expect(dispatchExternalPointerEvent).toHaveBeenCalledTimes(2); + }); + + test('should not trigger cursor pointer update (chart type: datatable, event type: time)', async () => { + await act( + { + datatables: [ + { + columns: [ + { + meta: { + index: 'foo_index', + field: 'foo_field', + }, + }, + ], + }, + ] as Datatable[], + }, + [{ isDateHistogram: true }, { accessors: ['foo_index:foo_field'] }] + ); + + expect(dispatchExternalPointerEvent).toHaveBeenCalledTimes(1); + }); + + test('should works with multi datatables (intersection)', async () => { + await act( + { + datatables: [ + { + columns: [ + { + meta: { + index: 'ia', + field: 'fa', + }, + }, + ], + }, + { + columns: [ + { + meta: { + index: 'ib', + field: 'fb', + }, + }, + ], + }, + ] as Datatable[], + }, + [{ accessors: ['foo_index:foo_field', 'ib:fb'] }] + ); + + expect(dispatchExternalPointerEvent).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/plugins/charts/public/services/active_cursor/use_active_cursor.ts b/src/plugins/charts/public/services/active_cursor/use_active_cursor.ts new file mode 100644 index 00000000000000..b857208e657aee --- /dev/null +++ b/src/plugins/charts/public/services/active_cursor/use_active_cursor.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { intersection } from 'lodash'; +import { animationFrameScheduler } from 'rxjs'; +import { useCallback, useEffect, RefObject } from 'react'; +import { filter, debounceTime } from 'rxjs/operators'; + +import type { Chart } from '@elastic/charts'; + +import { parseSyncOptions } from './active_cursor_utils'; + +import type { ActiveCursor } from './active_cursor'; +import type { ActiveCursorSyncOption } from './types'; + +const DEFAULT_DEBOUNCE_TIME = 40; + +export const useActiveCursor = ( + activeCursor: ActiveCursor, + chartRef: RefObject, + syncOptions: ActiveCursorSyncOption +) => { + const { accessors, isDateHistogram } = parseSyncOptions(syncOptions); + const handleCursorUpdate = useCallback( + (cursor) => { + activeCursor.activeCursor$?.next({ + cursor, + isDateHistogram, + accessors, + }); + }, + [activeCursor.activeCursor$, accessors, isDateHistogram] + ); + + useEffect(() => { + const cursorSubscription = activeCursor.activeCursor$ + ?.pipe( + debounceTime(syncOptions.debounce ?? DEFAULT_DEBOUNCE_TIME, animationFrameScheduler), + filter((payload) => { + if (payload.isDateHistogram && isDateHistogram) { + return true; + } + return intersection(accessors, payload.accessors).length > 0; + }) + ) + .subscribe(({ cursor }) => { + chartRef?.current?.dispatchExternalPointerEvent(cursor); + }); + + return () => { + cursorSubscription?.unsubscribe(); + }; + }, [activeCursor.activeCursor$, syncOptions.debounce, chartRef, accessors, isDateHistogram]); + + return handleCursorUpdate; +}; diff --git a/src/plugins/charts/public/services/index.ts b/src/plugins/charts/public/services/index.ts index aa2326f47377f9..88d92543cb8dd6 100644 --- a/src/plugins/charts/public/services/index.ts +++ b/src/plugins/charts/public/services/index.ts @@ -8,3 +8,4 @@ export { LegacyColorsService } from './legacy_colors'; export { ThemeService } from './theme'; +export { ActiveCursor, useActiveCursor } from './active_cursor'; diff --git a/src/plugins/dashboard/public/application/actions/export_csv_action.tsx b/src/plugins/dashboard/public/application/actions/export_csv_action.tsx index dd6eeb92ef932d..cf643fa7d271fc 100644 --- a/src/plugins/dashboard/public/application/actions/export_csv_action.tsx +++ b/src/plugins/dashboard/public/application/actions/export_csv_action.tsx @@ -8,7 +8,7 @@ import { Datatable } from 'src/plugins/expressions/public'; import { CoreStart } from '../../../../../core/public'; -import { FormatFactory } from '../../../../data/common/field_formats/utils'; +import { FormatFactory } from '../../../../field_formats/common'; import { DataPublicPluginStart, exporters } from '../../services/data'; import { downloadMultipleAs } from '../../services/share'; diff --git a/src/plugins/data/README.mdx b/src/plugins/data/README.mdx index fba5cbd6d48e17..c2da9a686cacbf 100644 --- a/src/plugins/data/README.mdx +++ b/src/plugins/data/README.mdx @@ -49,10 +49,6 @@ This is helpful when you want to provide a user with options, for example when c ``` -## Field Formats - -Coming soon. - ## Index Patterns The Index Patterns API provides a consistent method of structuring and formatting documents diff --git a/src/plugins/data/common/constants.ts b/src/plugins/data/common/constants.ts index 89ccdfcc0479e7..406da4e56d1503 100644 --- a/src/plugins/data/common/constants.ts +++ b/src/plugins/data/common/constants.ts @@ -31,13 +31,6 @@ export const UI_SETTINGS = { HISTOGRAM_BAR_TARGET: 'histogram:barTarget', HISTOGRAM_MAX_BARS: 'histogram:maxBars', HISTORY_LIMIT: 'history:limit', - SHORT_DOTS_ENABLE: 'shortDots:enable', - FORMAT_DEFAULT_TYPE_MAP: 'format:defaultTypeMap', - FORMAT_NUMBER_DEFAULT_PATTERN: 'format:number:defaultPattern', - FORMAT_PERCENT_DEFAULT_PATTERN: 'format:percent:defaultPattern', - FORMAT_BYTES_DEFAULT_PATTERN: 'format:bytes:defaultPattern', - FORMAT_CURRENCY_DEFAULT_PATTERN: 'format:currency:defaultPattern', - FORMAT_NUMBER_DEFAULT_LOCALE: 'format:number:defaultLocale', TIMEPICKER_REFRESH_INTERVAL_DEFAULTS: 'timepicker:refreshIntervalDefaults', TIMEPICKER_QUICK_RANGES: 'timepicker:quickRanges', TIMEPICKER_TIME_DEFAULTS: 'timepicker:timeDefaults', diff --git a/src/plugins/data/common/exports/export_csv.test.ts b/src/plugins/data/common/exports/export_csv.test.ts index 8bf44fe48a5890..f108e69c68d568 100644 --- a/src/plugins/data/common/exports/export_csv.test.ts +++ b/src/plugins/data/common/exports/export_csv.test.ts @@ -7,7 +7,7 @@ */ import { Datatable } from 'src/plugins/expressions'; -import { FieldFormat } from '../../common/field_formats'; +import { FieldFormat } from '../../../field_formats/common'; import { datatableToCSV } from './export_csv'; function getDefaultOptions() { diff --git a/src/plugins/data/common/exports/export_csv.tsx b/src/plugins/data/common/exports/export_csv.tsx index d4477e72b64c41..68f817d1b1484a 100644 --- a/src/plugins/data/common/exports/export_csv.tsx +++ b/src/plugins/data/common/exports/export_csv.tsx @@ -8,8 +8,8 @@ // Inspired by the inspector CSV exporter -import { FormatFactory } from 'src/plugins/data/common/field_formats/utils'; import { Datatable } from 'src/plugins/expressions'; +import { FormatFactory } from '../../../field_formats/common'; import { createEscapeValue } from './escape_value'; export const LINE_FEED_CHARACTER = '\r\n'; diff --git a/src/plugins/data/common/index.ts b/src/plugins/data/common/index.ts index cd05c613f793f6..2bc383db6f5304 100644 --- a/src/plugins/data/common/index.ts +++ b/src/plugins/data/common/index.ts @@ -8,7 +8,6 @@ export * from './constants'; export * from './es_query'; -export * from './field_formats'; export * from './index_patterns'; export * from './kbn_field_types'; export * from './query'; diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts index 73f015ca5517c3..f95de90955b654 100644 --- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts @@ -8,8 +8,9 @@ import { IndexPatternField } from './index_pattern_field'; import { IndexPattern } from '../index_patterns'; -import { KBN_FIELD_TYPES, FieldFormat } from '../../../common'; +import { KBN_FIELD_TYPES } from '../../../common'; import { FieldSpec, RuntimeField } from '../types'; +import { FieldFormat } from '../../../../field_formats/common'; describe('Field', function () { function flatten(obj: Record) { diff --git a/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.test.ts b/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.test.ts index 9a33b0cfa6f1ce..f4f94856c7226a 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.test.ts @@ -12,7 +12,7 @@ import { IndexPattern } from './index_pattern'; import mockLogStashFields from './fixtures/logstash_fields'; import { stubbedSavedObjectIndexPattern } from './fixtures/stubbed_saved_object_index_pattern'; -import { fieldFormatsMock } from '../../field_formats/mocks'; +import { fieldFormatsMock } from '../../../../field_formats/common/mocks'; import { flattenHitWrapper } from './flatten_hit'; class MockFieldFormatter {} diff --git a/src/plugins/data/common/index_patterns/index_patterns/format_hit.ts b/src/plugins/data/common/index_patterns/index_patterns/format_hit.ts index 732b9c7c8eab0b..fe872ae9298999 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/format_hit.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/format_hit.ts @@ -8,7 +8,7 @@ import _ from 'lodash'; import { IndexPattern } from './index_pattern'; -import { FieldFormatsContentType } from '../../../common'; +import { FieldFormatsContentType } from '../../../../field_formats/common'; const formattedCache = new WeakMap(); const partialFormattedCache = new WeakMap(); diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts index 508bddb7a4096f..7c111f7666544a 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts @@ -16,8 +16,8 @@ import mockLogStashFields from './fixtures/logstash_fields'; import { stubbedSavedObjectIndexPattern } from './fixtures/stubbed_saved_object_index_pattern'; import { IndexPatternField } from '../fields'; -import { fieldFormatsMock } from '../../field_formats/mocks'; -import { FieldFormat } from '../..'; +import { fieldFormatsMock } from '../../../../field_formats/common/mocks'; +import { FieldFormat } from '../../../../field_formats/common'; import { RuntimeField } from '../types'; class MockFieldFormatter {} diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index b3f2605723c02a..48bcdf6982b672 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -15,7 +15,7 @@ import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../. import { IndexPatternField, IIndexPatternFieldList, fieldList } from '../fields'; import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; -import { FieldFormatsStartCommon, FieldFormat } from '../../field_formats'; +import { FieldFormatsStartCommon, FieldFormat } from '../../../../field_formats/common'; import { IndexPatternSpec, TypeMeta, SourceFilter, IndexPatternFieldMap } from '../types'; import { SerializedFieldFormat } from '../../../../expressions/common'; import { castEsToKbnFieldTypeName } from '../../kbn_field_types'; 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 8715f8feb067a0..a80e97b4e2caba 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 @@ -8,7 +8,7 @@ import { defaults } from 'lodash'; import { IndexPatternsService, IndexPattern } from '.'; -import { fieldFormatsMock } from '../../field_formats/mocks'; +import { fieldFormatsMock } from '../../../../field_formats/common/mocks'; import { stubbedSavedObjectIndexPattern } from './fixtures/stubbed_saved_object_index_pattern'; import { UiSettingsCommon, SavedObjectsClientCommon, SavedObject } from '../types'; 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 cecf3b8c07d1ac..0f2e4afbdcd288 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 @@ -29,7 +29,7 @@ import { FieldSpec, IndexPatternFieldMap, } from '../types'; -import { FieldFormatsStartCommon } from '../../field_formats'; +import { FieldFormatsStartCommon, FORMATS_UI_SETTINGS } from '../../../../field_formats/common/'; import { UI_SETTINGS, SavedObject } from '../../../common'; import { SavedObjectNotFound } from '../../../../kibana_utils/common'; import { IndexPatternMissingIndices } from '../lib'; @@ -500,7 +500,7 @@ export class IndexPatternsService { * @returns IndexPattern */ async create(spec: IndexPatternSpec, skipFetchFields = false): Promise { - const shortDotsEnable = await this.config.get(UI_SETTINGS.SHORT_DOTS_ENABLE); + const shortDotsEnable = await this.config.get(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE); const metaFields = await this.config.get(UI_SETTINGS.META_FIELDS); const indexPattern = new IndexPattern({ diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index d05a7ea6e2d939..56a79dd9cfada1 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -13,7 +13,8 @@ import type { SavedObject } from 'src/core/server'; import { IFieldType } from './fields'; import { RUNTIME_FIELD_TYPES } from './constants'; import { SerializedFieldFormat } from '../../../expressions/common'; -import { KBN_FIELD_TYPES, IndexPatternField, FieldFormat } from '..'; +import { KBN_FIELD_TYPES, IndexPatternField } from '..'; +import { FieldFormat } from '../../../field_formats/common'; export type FieldFormatMap = Record; diff --git a/src/plugins/data/common/search/aggs/agg_types.ts b/src/plugins/data/common/search/aggs/agg_types.ts index 1db60db507f0f4..375de79f7f0813 100644 --- a/src/plugins/data/common/search/aggs/agg_types.ts +++ b/src/plugins/data/common/search/aggs/agg_types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FieldFormatsStartCommon } from '../../field_formats'; +import { FieldFormatsStartCommon } from '../../../../field_formats/common'; import * as buckets from './buckets'; import * as metrics from './metrics'; diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.test.ts index c147708b5b2f94..0cefd6b73b3360 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../common/field_formats'; +import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../../field_formats/common'; import { AggConfigs } from '../../agg_configs'; import { mockAggTypesRegistry, mockGetFieldFormatsStart } from '../../test_helpers'; import { BUCKET_TYPES } from '../bucket_agg_types'; diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.test.ts index 1e4d97a0073012..51ea6d081d1390 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.test.ts @@ -9,7 +9,7 @@ import { createFilterIpRange } from './ip_range'; import { AggConfigs, CreateAggConfigParams } from '../../agg_configs'; import { mockAggTypesRegistry } from '../../test_helpers'; -import { IpFormat } from '../../../../../common'; +import { IpFormat } from '../../../../../../field_formats/common'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { IBucketAggConfig } from '../bucket_agg_type'; diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/range.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/range.test.ts index 7e4289e23cfaee..c9ab1617929dc1 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/range.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/range.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../common/field_formats'; +import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../../field_formats/common'; import { AggConfigs } from '../../agg_configs'; import { mockAggTypesRegistry, mockGetFieldFormatsStart } from '../../test_helpers'; import { IBucketAggConfig } from '../bucket_agg_type'; diff --git a/src/plugins/data/common/search/aggs/buckets/range.test.ts b/src/plugins/data/common/search/aggs/buckets/range.test.ts index 60a58a68e18e0a..202cc59c83c0f6 100644 --- a/src/plugins/data/common/search/aggs/buckets/range.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/range.test.ts @@ -9,7 +9,7 @@ import { AggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../common/field_formats'; +import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../../field_formats/common'; describe('Range Agg', () => { const getConfig = (() => {}) as FieldFormatsGetConfigFn; diff --git a/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts b/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts index 51c71cd1a2994a..9f7559c6fe34fc 100644 --- a/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts @@ -7,7 +7,7 @@ */ import { AggConfigs } from '../agg_configs'; -import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../common/field_formats'; +import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../../field_formats/common'; import { getShardDelayBucketAgg, SHARD_DELAY_AGG_NAME } from './shard_delay'; describe('Shard Delay Agg', () => { diff --git a/src/plugins/data/common/search/aggs/test_helpers/mock_agg_types_registry.ts b/src/plugins/data/common/search/aggs/test_helpers/mock_agg_types_registry.ts index ff22af720bde61..50c8966d284a07 100644 --- a/src/plugins/data/common/search/aggs/test_helpers/mock_agg_types_registry.ts +++ b/src/plugins/data/common/search/aggs/test_helpers/mock_agg_types_registry.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { fieldFormatsMock } from '../../../field_formats/mocks'; +import { fieldFormatsMock } from '../../../../../field_formats/common/mocks'; import { AggTypesRegistry, AggTypesRegistryStart } from '../agg_types_registry'; import { AggTypesDependencies, getAggTypes } from '../agg_types'; diff --git a/src/plugins/data/common/search/aggs/utils/get_aggs_formats.test.ts b/src/plugins/data/common/search/aggs/utils/get_aggs_formats.test.ts index 1cf80a6d0085c5..76112980c55fbc 100644 --- a/src/plugins/data/common/search/aggs/utils/get_aggs_formats.test.ts +++ b/src/plugins/data/common/search/aggs/utils/get_aggs_formats.test.ts @@ -8,8 +8,11 @@ import { identity } from 'lodash'; -import { SerializedFieldFormat } from 'src/plugins/expressions/common/types'; -import { FieldFormat, IFieldFormat } from '../../../../common'; +import { + FieldFormat, + IFieldFormat, + SerializedFieldFormat, +} from '../../../../../field_formats/common'; import { getAggsFormats } from './get_aggs_formats'; const getAggFormat = ( diff --git a/src/plugins/data/common/search/aggs/utils/get_aggs_formats.ts b/src/plugins/data/common/search/aggs/utils/get_aggs_formats.ts index 175b6832b3c854..2aead866c6b60e 100644 --- a/src/plugins/data/common/search/aggs/utils/get_aggs_formats.ts +++ b/src/plugins/data/common/search/aggs/utils/get_aggs_formats.ts @@ -15,7 +15,7 @@ import { FieldFormatInstanceType, FieldFormatsContentType, IFieldFormat, -} from '../../../../common/field_formats'; +} from '../../../../../field_formats/common'; import { DateRange } from '../../expressions'; import { convertDateRangeToString } from '../buckets/lib/date_range'; import { convertIPRangeToString, IpRangeKey } from '../buckets/lib/ip_range'; diff --git a/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts b/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts index b78980cb5136e1..e200f9bf02536b 100644 --- a/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts +++ b/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts @@ -7,7 +7,7 @@ */ import { isRangeFilter } from '@kbn/es-query'; -import { BytesFormat, FieldFormatsGetConfigFn } from '../../../field_formats'; +import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../field_formats/common'; import { AggConfigs, IAggConfig } from '../../aggs'; import { mockAggTypesRegistry } from '../../aggs/test_helpers'; diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index c982ba1e7a2937..c72976e3412a6c 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -99,7 +99,7 @@ import { isPartialResponse, IKibanaSearchResponse, } from '../../../common'; -import { getHighlightRequest } from '../../../common/field_formats'; +import { getHighlightRequest } from '../../../../field_formats/common'; import { extractReferences } from './extract_references'; /** @internal */ diff --git a/src/plugins/data/kibana.json b/src/plugins/data/kibana.json index e425d0701155bd..e6faa6bd0b1a7a 100644 --- a/src/plugins/data/kibana.json +++ b/src/plugins/data/kibana.json @@ -3,8 +3,8 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["bfetch", "expressions", "uiActions", "share", "inspector"], - "serviceFolders": ["search", "index_patterns", "query", "autocomplete", "ui", "field_formats"], + "requiredPlugins": ["bfetch", "expressions", "uiActions", "share", "inspector", "fieldFormats"], + "serviceFolders": ["search", "index_patterns", "query", "autocomplete", "ui"], "optionalPlugins": ["usageCollection"], "extraPublicDirs": ["common"], "requiredBundles": ["kibanaUtils", "kibanaReact", "inspector"], diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts index ea7d7690fb876c..5c3b016dc631e7 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts @@ -10,14 +10,11 @@ import moment from 'moment'; import { createFiltersFromRangeSelectAction } from './create_filters_from_range_select'; -import { - fieldFormats, - FieldFormatsGetConfigFn, - IndexPatternsContract, - RangeFilter, -} from '../../../public'; +import { IndexPatternsContract, RangeFilter } from '../../../public'; import { dataPluginMock } from '../../../public/mocks'; import { setIndexPatterns, setSearchService } from '../../../public/services'; +import { FieldFormatsGetConfigFn } from '../../../../field_formats/common'; +import { DateFormat } from '../../../../field_formats/public/'; describe('brushEvent', () => { const DAY_IN_MS = 24 * 60 * 60 * 1000; @@ -35,7 +32,7 @@ describe('brushEvent', () => { id: 'logstash-*', }, filterable: true, - format: new fieldFormats.DateFormat({}, (() => {}) as FieldFormatsGetConfigFn), + format: new DateFormat({}, (() => {}) as FieldFormatsGetConfigFn), }; const indexPattern = { diff --git a/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts index 14ae24a2a5626b..563321cf56fefd 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts @@ -6,18 +6,14 @@ * Side Public License, v 1. */ -import { - fieldFormats, - FieldFormatsGetConfigFn, - esFilters, - IndexPatternsContract, -} from '../../../public'; +import { esFilters, IndexPatternsContract } from '../../../public'; import { dataPluginMock } from '../../../public/mocks'; import { setIndexPatterns, setSearchService } from '../../../public/services'; import { createFiltersFromValueClickAction, ValueClickDataContext, } from './create_filters_from_value_click'; +import { FieldFormatsGetConfigFn, BytesFormat } from '../../../../field_formats/common'; const mockField = { name: 'bytes', @@ -72,8 +68,7 @@ describe('createFiltersFromValueClick', () => { getByName: () => mockField, filter: () => [mockField], }, - getFormatterForField: () => - new fieldFormats.BytesFormat({}, (() => {}) as FieldFormatsGetConfigFn), + getFormatterForField: () => new BytesFormat({}, (() => {}) as FieldFormatsGetConfigFn), }), } as unknown) as IndexPatternsContract); }); diff --git a/src/plugins/data/public/field_formats/field_formats_registry.stub.ts b/src/plugins/data/public/field_formats/field_formats_registry.stub.ts deleted file mode 100644 index 566532085059d2..00000000000000 --- a/src/plugins/data/public/field_formats/field_formats_registry.stub.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { CoreSetup } from 'src/core/public'; -import { baseFormattersPublic } from './constants'; -import { fieldFormats } from '..'; - -export const getFieldFormatsRegistry = (core: CoreSetup) => { - const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); - const getConfig = core.uiSettings.get.bind(core.uiSettings); - - fieldFormatsRegistry.init(getConfig, {}, baseFormattersPublic); - - return fieldFormatsRegistry; -}; diff --git a/src/plugins/data/public/index.scss b/src/plugins/data/public/index.scss index c0eebf3402771d..467efa98934eca 100644 --- a/src/plugins/data/public/index.scss +++ b/src/plugins/data/public/index.scss @@ -1,3 +1,2 @@ @import './ui/index'; @import './utils/table_inspector_view/index'; -@import './field_formats/converters/index'; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index e3ecacfbda5ad3..803b93cc7acf59 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -19,75 +19,6 @@ export { getEsQueryConfig } from '../common'; export { FilterLabel, FilterItem } from './ui'; export { getDisplayValueFromFilter, generateFilters, extractTimeRange } from './query'; -/* - * Field Formatters: - */ - -import { - FieldFormat, - FieldFormatsRegistry, - DEFAULT_CONVERTER_COLOR, - HTML_CONTEXT_TYPE, - TEXT_CONTEXT_TYPE, - FIELD_FORMAT_IDS, - BoolFormat, - BytesFormat, - ColorFormat, - DurationFormat, - IpFormat, - NumberFormat, - PercentFormat, - RelativeDateFormat, - SourceFormat, - StaticLookupFormat, - UrlFormat, - StringFormat, - TruncateFormat, - HistogramFormat, -} from '../common/field_formats'; - -import { DateNanosFormat, DateFormat } from './field_formats'; -export { baseFormattersPublic, FieldFormatsStart } from './field_formats'; - -// Field formats helpers namespace: -export const fieldFormats = { - FieldFormat, - FieldFormatsRegistry, // exported only for tests. Consider mock. - - DEFAULT_CONVERTER_COLOR, - HTML_CONTEXT_TYPE, - TEXT_CONTEXT_TYPE, - FIELD_FORMAT_IDS, - - BoolFormat, - BytesFormat, - ColorFormat, - DateFormat, - DateNanosFormat, - DurationFormat, - IpFormat, - NumberFormat, - PercentFormat, - RelativeDateFormat, - SourceFormat, - StaticLookupFormat, - UrlFormat, - StringFormat, - TruncateFormat, - HistogramFormat, -}; - -export { - IFieldFormat, - FieldFormatInstanceType, - IFieldFormatsRegistry, - FieldFormatsContentType, - FieldFormatsGetConfigFn, - FieldFormatConfig, - FieldFormatId, - FieldFormat, -} from '../common'; - /** * Exporters (CSV) */ diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 9e01350add2e3b..ba1cba987c0b96 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -7,7 +7,7 @@ */ import { DataPlugin, IndexPatternsContract } from '.'; -import { fieldFormatsServiceMock } from './field_formats/mocks'; +import { fieldFormatsServiceMock } from '../../field_formats/public/mocks'; import { searchServiceMock } from './search/mocks'; import { queryServiceMock } from './query/mocks'; import { AutocompleteStart, AutocompleteSetup } from './autocomplete'; diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 7a071dbffe696a..46b1d4a14be788 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -19,7 +19,6 @@ import { } from './types'; import { AutocompleteService } from './autocomplete'; import { SearchService } from './search/search_service'; -import { FieldFormatsService } from './field_formats'; import { QueryService } from './query'; import { createIndexPatternSelect } from './ui/index_pattern_select'; import { @@ -62,7 +61,6 @@ export class DataPublicPlugin > { private readonly autocomplete: AutocompleteService; private readonly searchService: SearchService; - private readonly fieldFormatsService: FieldFormatsService; private readonly queryService: QueryService; private readonly storage: IStorageWrapper; private usageCollection: UsageCollectionSetup | undefined; @@ -71,7 +69,6 @@ export class DataPublicPlugin constructor(initializerContext: PluginInitializerContext) { this.searchService = new SearchService(initializerContext); this.queryService = new QueryService(); - this.fieldFormatsService = new FieldFormatsService(); this.autocomplete = new AutocompleteService(initializerContext); this.storage = new Storage(window.localStorage); @@ -80,7 +77,14 @@ export class DataPublicPlugin public setup( core: CoreSetup, - { bfetch, expressions, uiActions, usageCollection, inspector }: DataSetupDependencies + { + bfetch, + expressions, + uiActions, + usageCollection, + inspector, + fieldFormats, + }: DataSetupDependencies ): DataPublicPluginSetup { const startServices = createStartServicesGetter(core.getStartServices); @@ -115,10 +119,9 @@ export class DataPublicPlugin })) ); - const fieldFormats = this.fieldFormatsService.setup(core); fieldFormats.register( getAggsFormats((serializedFieldFormat) => - startServices().self.fieldFormats.deserialize(serializedFieldFormat) + startServices().plugins.fieldFormats.deserialize(serializedFieldFormat) ) ); @@ -133,14 +136,15 @@ export class DataPublicPlugin }; } - public start(core: CoreStart, { uiActions }: DataStartDependencies): DataPublicPluginStart { + public start( + core: CoreStart, + { uiActions, fieldFormats }: DataStartDependencies + ): DataPublicPluginStart { const { uiSettings, http, notifications, savedObjects, overlays, application } = core; setNotifications(notifications); setOverlays(overlays); setUiSettings(uiSettings); - const fieldFormats = this.fieldFormatsService.start(); - const indexPatterns = new IndexPatternsService({ uiSettings: new UiSettingsPublicToCommon(uiSettings), savedObjectsClient: new SavedObjectsClientPublicToCommon(savedObjects.client), diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index ce105b8cc94acd..7e67fdbe667ea8 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -43,9 +43,10 @@ import { ExpressionAstExpression } from 'src/plugins/expressions/common'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { ExpressionsSetup } from 'src/plugins/expressions/public'; import { ExpressionValueBoxed } from 'src/plugins/expressions/common'; +import { FieldFormatsSetup } from 'src/plugins/field_formats/public'; +import { FieldFormatsStart } from 'src/plugins/field_formats/public'; import { Filter as Filter_2 } from '@kbn/es-query'; import { FilterStateStore } from '@kbn/es-query'; -import { FormatFactory as FormatFactory_2 } from 'src/plugins/data/common/field_formats/utils'; import { History } from 'history'; import { Href } from 'history'; import { HttpSetup } from 'kibana/public'; @@ -107,7 +108,7 @@ import { SavedObjectsFindOptions } from 'kibana/public'; import { SavedObjectsFindResponse } from 'kibana/server'; import { SavedObjectsUpdateResponse } from 'kibana/server'; import { SchemaTypeError } from '@kbn/config-schema'; -import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; +import { SerializedFieldFormat as SerializedFieldFormat_3 } from 'src/plugins/expressions/common'; import { StartServicesAccessor } from 'kibana/public'; import { ToastInputFields } from 'src/core/public/notifications'; import { ToastsSetup } from 'kibana/public'; @@ -207,7 +208,7 @@ export class AggConfig { // @deprecated (undocumented) toJSON(): AggConfigSerialized; // Warning: (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts - toSerializedFieldFormat(): {} | Ensure, SerializableState_2>; + toSerializedFieldFormat(): {} | Ensure, SerializableState_2>; // (undocumented) get type(): IAggType; set type(type: IAggType); @@ -573,13 +574,6 @@ export type AutocompleteStart = ReturnType; // @public (undocumented) export type AutoRefreshDoneFn = () => void; -// Warning: (ae-forgotten-export) The symbol "DateFormat" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "DateNanosFormat" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "baseFormattersPublic" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const baseFormattersPublic: (import("../../common").FieldFormatInstanceType | typeof DateFormat | typeof DateNanosFormat)[]; - // Warning: (ae-missing-release-tag) "BUCKET_TYPES" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -644,9 +638,9 @@ export class DataPlugin implements Plugin); // (undocumented) - setup(core: CoreSetup, { bfetch, expressions, uiActions, usageCollection, inspector }: DataSetupDependencies): DataPublicPluginSetup; + setup(core: CoreSetup, { bfetch, expressions, uiActions, usageCollection, inspector, fieldFormats, }: DataSetupDependencies): DataPublicPluginSetup; // (undocumented) - start(core: CoreStart_2, { uiActions }: DataStartDependencies): DataPublicPluginStart; + start(core: CoreStart_2, { uiActions, fieldFormats }: DataStartDependencies): DataPublicPluginStart; // (undocumented) stop(): void; } @@ -659,9 +653,7 @@ export interface DataPublicPluginSetup { // // (undocumented) autocomplete: AutocompleteSetup; - // Warning: (ae-forgotten-export) The symbol "FieldFormatsSetup" needs to be exported by the entry point index.d.ts - // - // (undocumented) + // @deprecated (undocumented) fieldFormats: FieldFormatsSetup; // (undocumented) query: QuerySetup; @@ -675,6 +667,7 @@ export interface DataPublicPluginSetup { export interface DataPublicPluginStart { actions: DataPublicPluginStartActions; autocomplete: AutocompleteStart; + // @deprecated (undocumented) fieldFormats: FieldFormatsStart; indexPatterns: IndexPatternsContract; // Warning: (ae-forgotten-export) The symbol "NowProviderPublicContract" needs to be exported by the entry point index.d.ts @@ -896,124 +889,6 @@ export function extractTimeRange(filters: Filter_2[], timeFieldName?: string): { timeRange?: TimeRange; }; -// Warning: (ae-missing-release-tag) "FieldFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export abstract class FieldFormat { - // Warning: (ae-forgotten-export) The symbol "IFieldFormatMetaParams" needs to be exported by the entry point index.d.ts - constructor(_params?: IFieldFormatMetaParams, getConfig?: FieldFormatsGetConfigFn); - // (undocumented) - allowsNumericalAggregations?: boolean; - // Warning: (ae-forgotten-export) The symbol "HtmlContextTypeOptions" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "TextContextTypeOptions" needs to be exported by the entry point index.d.ts - convert(value: any, contentType?: FieldFormatsContentType, options?: HtmlContextTypeOptions | TextContextTypeOptions): string; - // Warning: (ae-forgotten-export) The symbol "FieldFormatConvert" needs to be exported by the entry point index.d.ts - convertObject: FieldFormatConvert | undefined; - static fieldType: string | string[]; - // Warning: (ae-incompatible-release-tags) The symbol "from" is marked as @public, but its signature references "FieldFormatInstanceType" which is marked as @internal - // - // (undocumented) - static from(convertFn: FieldFormatConvertFunction): FieldFormatInstanceType; - // (undocumented) - protected getConfig: FieldFormatsGetConfigFn | undefined; - // Warning: (ae-forgotten-export) The symbol "FieldFormatConvertFunction" needs to be exported by the entry point index.d.ts - getConverterFor(contentType?: FieldFormatsContentType): FieldFormatConvertFunction; - getParamDefaults(): Record; - static hidden: boolean; - // Warning: (ae-forgotten-export) The symbol "HtmlContextTypeConvert" needs to be exported by the entry point index.d.ts - htmlConvert: HtmlContextTypeConvert | undefined; - static id: string; - // (undocumented) - static isInstanceOfFieldFormat(fieldFormat: any): fieldFormat is FieldFormat; - param(name: string): any; - params(): Record; - // (undocumented) - protected readonly _params: any; - // (undocumented) - setupContentType(): FieldFormatConvert; - // Warning: (ae-forgotten-export) The symbol "TextContextTypeConvert" needs to be exported by the entry point index.d.ts - textConvert: TextContextTypeConvert | undefined; - static title: string; - toJSON(): { - id: any; - params: any; - }; - type: any; -} - -// Warning: (ae-missing-release-tag) "FieldFormatConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface FieldFormatConfig { - // (undocumented) - es?: boolean; - // (undocumented) - id: FieldFormatId; - // (undocumented) - params: Record; -} - -// Warning: (ae-forgotten-export) The symbol "FIELD_FORMAT_IDS" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "FieldFormatId" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public -export type FieldFormatId = FIELD_FORMAT_IDS | string; - -// @internal (undocumented) -export type FieldFormatInstanceType = (new (params?: any, getConfig?: FieldFormatsGetConfigFn) => FieldFormat) & { - id: FieldFormatId; - title: string; - hidden?: boolean; - fieldType: string | string[]; -}; - -// Warning: (ae-missing-release-tag) "fieldFormats" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const fieldFormats: { - FieldFormat: typeof FieldFormat; - FieldFormatsRegistry: typeof FieldFormatsRegistry; - DEFAULT_CONVERTER_COLOR: { - range: string; - regex: string; - text: string; - background: string; - }; - HTML_CONTEXT_TYPE: import("../common").FieldFormatsContentType; - TEXT_CONTEXT_TYPE: import("../common").FieldFormatsContentType; - FIELD_FORMAT_IDS: typeof FIELD_FORMAT_IDS; - BoolFormat: typeof BoolFormat; - BytesFormat: typeof BytesFormat; - ColorFormat: typeof ColorFormat; - DateFormat: typeof DateFormat; - DateNanosFormat: typeof DateNanosFormat; - DurationFormat: typeof DurationFormat; - IpFormat: typeof IpFormat; - NumberFormat: typeof NumberFormat; - PercentFormat: typeof PercentFormat; - RelativeDateFormat: typeof RelativeDateFormat; - SourceFormat: typeof SourceFormat; - StaticLookupFormat: typeof StaticLookupFormat; - UrlFormat: typeof UrlFormat; - StringFormat: typeof StringFormat; - TruncateFormat: typeof TruncateFormat; - HistogramFormat: typeof HistogramFormat; -}; - -// @public (undocumented) -export type FieldFormatsContentType = 'html' | 'text'; - -// Warning: (ae-forgotten-export) The symbol "GetConfigFn" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "FieldFormatsGetConfigFn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type FieldFormatsGetConfigFn = GetConfigFn; - -// @public (undocumented) -export type FieldFormatsStart = Omit & { - deserialize: FormatFactory; -}; - // Warning: (ae-forgotten-export) The symbol "FieldSpec" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "fieldList" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1191,16 +1066,6 @@ export interface IEsSearchRequest extends IKibanaSearchRequest = IKibanaSearchResponse>; -// Warning: (ae-missing-release-tag) "IFieldFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type IFieldFormat = FieldFormat; - -// Warning: (ae-missing-release-tag) "IFieldFormatsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type IFieldFormatsRegistry = PublicMethodsOf; - // Warning: (ae-forgotten-export) The symbol "FieldParamType" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IFieldParamType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1254,6 +1119,7 @@ export interface IIndexPattern extends IndexPatternBase { fieldFormatMap?: Record | undefined>; // (undocumented) fields: IFieldType[]; + // Warning: (ae-forgotten-export) The symbol "FieldFormat" needs to be exported by the entry point index.d.ts getFormatterForField?: (field: IndexPatternField | IndexPatternField['spec'] | IFieldType) => FieldFormat; // (undocumented) getTimeField?(): IFieldType | undefined; @@ -2578,13 +2444,6 @@ export const UI_SETTINGS: { readonly HISTOGRAM_BAR_TARGET: "histogram:barTarget"; readonly HISTOGRAM_MAX_BARS: "histogram:maxBars"; readonly HISTORY_LIMIT: "history:limit"; - readonly SHORT_DOTS_ENABLE: "shortDots:enable"; - readonly FORMAT_DEFAULT_TYPE_MAP: "format:defaultTypeMap"; - readonly FORMAT_NUMBER_DEFAULT_PATTERN: "format:number:defaultPattern"; - readonly FORMAT_PERCENT_DEFAULT_PATTERN: "format:percent:defaultPattern"; - readonly FORMAT_BYTES_DEFAULT_PATTERN: "format:bytes:defaultPattern"; - readonly FORMAT_CURRENCY_DEFAULT_PATTERN: "format:currency:defaultPattern"; - readonly FORMAT_NUMBER_DEFAULT_LOCALE: "format:number:defaultLocale"; readonly TIMEPICKER_REFRESH_INTERVAL_DEFAULTS: "timepicker:refreshIntervalDefaults"; readonly TIMEPICKER_QUICK_RANGES: "timepicker:quickRanges"; readonly TIMEPICKER_TIME_DEFAULTS: "timepicker:timeDefaults"; @@ -2614,45 +2473,30 @@ export interface WaitUntilNextSessionCompletesOptions { // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:139:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:170:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/aggs/types.ts:128:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/search/search_source/fetch/get_search_params.ts:35:19 - (ae-forgotten-export) The symbol "GetConfigFn" needs to be exported by the entry point index.d.ts // src/plugins/data/public/deprecated.ts:98:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/public/deprecated.ts:98:23 - (ae-forgotten-export) The symbol "convertRangeFilterToTimeRangeString" needs to be exported by the entry point index.d.ts // src/plugins/data/public/deprecated.ts:98:23 - (ae-forgotten-export) The symbol "extractTimeFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/field_formats/field_formats_service.ts:51:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:53:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:96:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:123:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:123:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:123:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:123:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:123:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:294:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:294:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:294:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:296:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:297:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:306:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:307:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:308:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:309:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:313:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:314:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:317:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:318:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:321:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:27:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:54:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:54:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:54:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:54:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:54:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:225:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:225:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:225:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:227:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:228:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:237:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:238:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:239:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:240:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:244:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:245:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:248:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:249:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:252:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/search/session/session_service.ts:62:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/public/search/aggs/aggs_service.test.ts b/src/plugins/data/public/search/aggs/aggs_service.test.ts index cd2ee69d33996e..563fd5d0440730 100644 --- a/src/plugins/data/public/search/aggs/aggs_service.test.ts +++ b/src/plugins/data/public/search/aggs/aggs_service.test.ts @@ -11,7 +11,7 @@ import { BehaviorSubject, Subscription } from 'rxjs'; import { coreMock } from '../../../../../core/public/mocks'; import { expressionsPluginMock } from '../../../../../plugins/expressions/public/mocks'; import { BucketAggType, getAggTypes, MetricAggType } from '../../../common'; -import { fieldFormatsServiceMock } from '../../field_formats/mocks'; +import { fieldFormatsServiceMock } from '../../../../field_formats/public/mocks'; import { dataPluginMock } from '../../mocks'; import { diff --git a/src/plugins/data/public/search/aggs/aggs_service.ts b/src/plugins/data/public/search/aggs/aggs_service.ts index f603bd733f6014..3f91ced6de9504 100644 --- a/src/plugins/data/public/search/aggs/aggs_service.ts +++ b/src/plugins/data/public/search/aggs/aggs_service.ts @@ -10,7 +10,7 @@ import { Subscription } from 'rxjs'; import { IUiSettingsClient } from 'src/core/public'; import { ExpressionsServiceSetup } from 'src/plugins/expressions/common'; -import { FieldFormatsStart } from '../../field_formats'; +import { FieldFormatsStart } from '../../../../field_formats/public'; import { calculateBounds, TimeRange } from '../../../common'; import { aggsRequiredUiSettings, diff --git a/src/plugins/data/public/test_utils.ts b/src/plugins/data/public/test_utils.ts index b964ddbd2a379d..613e3850c922ec 100644 --- a/src/plugins/data/public/test_utils.ts +++ b/src/plugins/data/public/test_utils.ts @@ -6,5 +6,5 @@ * Side Public License, v 1. */ -export { getFieldFormatsRegistry } from './field_formats/field_formats_registry.stub'; +export { getFieldFormatsRegistry } from '../../field_formats/public/mocks'; export { getStubIndexPattern, StubIndexPattern } from './index_patterns/index_pattern.stub'; diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts index 5ba4ba2bc48afb..d8bfcfdb6ddb13 100644 --- a/src/plugins/data/public/types.ts +++ b/src/plugins/data/public/types.ts @@ -12,8 +12,8 @@ import { BfetchPublicSetup } from 'src/plugins/bfetch/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { ExpressionsSetup } from 'src/plugins/expressions/public'; import { UiActionsSetup, UiActionsStart } from 'src/plugins/ui_actions/public'; +import { FieldFormatsSetup, FieldFormatsStart } from 'src/plugins/field_formats/public'; import { AutocompleteSetup, AutocompleteStart } from './autocomplete'; -import { FieldFormatsSetup, FieldFormatsStart } from './field_formats'; import { createFiltersFromRangeSelectAction, createFiltersFromValueClickAction } from './actions'; import { ISearchSetup, ISearchStart } from './search'; import { QuerySetup, QueryStart } from './query'; @@ -29,10 +29,12 @@ export interface DataSetupDependencies { uiActions: UiActionsSetup; inspector: InspectorSetup; usageCollection?: UsageCollectionSetup; + fieldFormats: FieldFormatsSetup; } export interface DataStartDependencies { uiActions: UiActionsStart; + fieldFormats: FieldFormatsStart; } /** @@ -41,6 +43,9 @@ export interface DataStartDependencies { export interface DataPublicPluginSetup { autocomplete: AutocompleteSetup; search: ISearchSetup; + /** + * @deprecated Use fieldFormats plugin instead + */ fieldFormats: FieldFormatsSetup; query: QuerySetup; } @@ -86,8 +91,7 @@ export interface DataPublicPluginStart { */ search: ISearchStart; /** - * field formats service - * {@link FieldFormatsStart} + * @deprecated Use fieldFormats plugin instead */ fieldFormats: FieldFormatsStart; /** diff --git a/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx b/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx index 5272834012eab8..de72f163580985 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx +++ b/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { DataViewRow, DataViewColumn } from '../types'; import { IUiSettingsClient } from '../../../../../../core/public'; import { Datatable, DatatableColumn } from '../../../../../expressions/public'; -import { FieldFormatsStart } from '../../../field_formats'; +import { FieldFormatsStart } from '../../../../../field_formats/public'; import { UiActionsStart } from '../../../../../ui_actions/public'; interface DataTableFormatState { diff --git a/src/plugins/data/public/utils/table_inspector_view/components/data_view.tsx b/src/plugins/data/public/utils/table_inspector_view/components/data_view.tsx index 61a4daa3fc36a8..bfdd2d602de199 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/data_view.tsx +++ b/src/plugins/data/public/utils/table_inspector_view/components/data_view.tsx @@ -15,7 +15,7 @@ import { DataTableFormat } from './data_table'; import { IUiSettingsClient } from '../../../../../../core/public'; import { InspectorViewProps, Adapters } from '../../../../../inspector/public'; import { UiActionsStart } from '../../../../../ui_actions/public'; -import { FieldFormatsStart } from '../../../field_formats'; +import { FieldFormatsStart } from '../../../../../field_formats/public'; import { TablesAdapter, Datatable, DatatableColumn } from '../../../../../expressions/public'; import { TableSelector } from './data_table_selector'; import { DataDownloadOptions } from './download_options'; diff --git a/src/plugins/data/public/utils/table_inspector_view/components/data_view_wrapper.tsx b/src/plugins/data/public/utils/table_inspector_view/components/data_view_wrapper.tsx index 4204a48925bfe3..1e0ebe3e3d9076 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/data_view_wrapper.tsx +++ b/src/plugins/data/public/utils/table_inspector_view/components/data_view_wrapper.tsx @@ -9,7 +9,7 @@ import React, { lazy } from 'react'; import { IUiSettingsClient } from 'kibana/public'; import { UiActionsStart } from '../../../../../ui_actions/public'; -import { FieldFormatsStart } from '../../../field_formats'; +import { FieldFormatsStart } from '../../../../../field_formats/public'; import { DatatableColumn } from '../../../../../expressions/common/expression_types/specs'; const DataViewComponent = lazy(() => import('./data_view')); diff --git a/src/plugins/data/public/utils/table_inspector_view/components/download_options.tsx b/src/plugins/data/public/utils/table_inspector_view/components/download_options.tsx index e79a1c2b52e03d..57e586eaf12f84 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/download_options.tsx +++ b/src/plugins/data/public/utils/table_inspector_view/components/download_options.tsx @@ -22,7 +22,7 @@ import { import { CSV_MIME_TYPE, datatableToCSV, tableHasFormulas } from '../../../../common'; import { Datatable } from '../../../../../expressions'; import { downloadMultipleAs } from '../../../../../share/public'; -import { FieldFormatsStart } from '../../../field_formats'; +import { FieldFormatsStart } from '../../../../../field_formats/public'; import { IUiSettingsClient } from '../../../../../../core/public'; interface DataDownloadOptionsState { diff --git a/src/plugins/data/public/utils/table_inspector_view/index.ts b/src/plugins/data/public/utils/table_inspector_view/index.ts index 10c74bbc99dd0a..498fe8d7fcca07 100644 --- a/src/plugins/data/public/utils/table_inspector_view/index.ts +++ b/src/plugins/data/public/utils/table_inspector_view/index.ts @@ -11,7 +11,7 @@ import { IUiSettingsClient } from 'kibana/public'; import { Adapters, InspectorViewDescription } from '../../../../inspector/public'; import { getDataViewComponentWrapper } from './components/data_view_wrapper'; import { UiActionsStart } from '../../../../ui_actions/public'; -import { FieldFormatsStart } from '../../field_formats'; +import { FieldFormatsStart } from '../../../../field_formats/public'; import { DatatableColumn } from '../../../../expressions/common/expression_types/specs'; export const getTableViewDescription = ( diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index e5656ceabc1512..d010819d14e947 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -27,50 +27,7 @@ export const exporters = { * Field Formats: */ -import { - FieldFormatsRegistry, - FieldFormat, - BoolFormat, - BytesFormat, - ColorFormat, - DurationFormat, - IpFormat, - NumberFormat, - PercentFormat, - RelativeDateFormat, - SourceFormat, - StaticLookupFormat, - UrlFormat, - StringFormat, - TruncateFormat, - HistogramFormat, -} from '../common/field_formats'; - -export const fieldFormats = { - FieldFormatsRegistry, - FieldFormat, - BoolFormat, - BytesFormat, - ColorFormat, - DurationFormat, - IpFormat, - NumberFormat, - PercentFormat, - RelativeDateFormat, - SourceFormat, - StaticLookupFormat, - UrlFormat, - StringFormat, - TruncateFormat, - HistogramFormat, -}; - -export { - IFieldFormatsRegistry, - FieldFormatsGetConfigFn, - FieldFormatConfig, - INDEX_PATTERN_SAVED_OBJECT_TYPE, -} from '../common'; +export { INDEX_PATTERN_SAVED_OBJECT_TYPE } from '../common'; /* * Index patterns: diff --git a/src/plugins/data/server/index_patterns/index_patterns_service.ts b/src/plugins/data/server/index_patterns/index_patterns_service.ts index 4269f15127daf3..c3cdc65d3fa040 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_service.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts @@ -22,7 +22,7 @@ import { registerRoutes } from './routes'; import { indexPatternSavedObjectType } from '../saved_objects'; import { capabilitiesProvider } from './capabilities_provider'; import { IndexPatternsCommonService } from '../'; -import { FieldFormatsStart } from '../field_formats'; +import { FieldFormatsStart } from '../../../field_formats/server'; import { getIndexPatternLoad } from './expressions'; import { UiSettingsServerToCommon } from './ui_settings_wrapper'; import { IndexPatternsApiServer } from './index_patterns_api_client'; diff --git a/src/plugins/data/server/mocks.ts b/src/plugins/data/server/mocks.ts index 786dd30dbabd0b..af104dea72af79 100644 --- a/src/plugins/data/server/mocks.ts +++ b/src/plugins/data/server/mocks.ts @@ -11,13 +11,19 @@ import { createSearchStartMock, createSearchRequestHandlerContext, } from './search/mocks'; -import { createFieldFormatsSetupMock, createFieldFormatsStartMock } from './field_formats/mocks'; +import { + createFieldFormatsSetupMock, + createFieldFormatsStartMock, +} from '../../field_formats/server/mocks'; import { createIndexPatternsStartMock } from './index_patterns/mocks'; import { DataRequestHandlerContext } from './search'; function createSetupContract() { return { search: createSearchSetupMock(), + /** + * @deprecated - use directly from "fieldFormats" plugin instead + */ fieldFormats: createFieldFormatsSetupMock(), }; } @@ -25,6 +31,9 @@ function createSetupContract() { function createStartContract() { return { search: createSearchStartMock(), + /** + * @deprecated - use directly from "fieldFormats" plugin instead + */ fieldFormats: createFieldFormatsStartMock(), indexPatterns: createIndexPatternsStartMock(), }; diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index 7b73802f1a34d1..e04095ed26a46b 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -18,7 +18,7 @@ import { ScriptsService } from './scripts'; import { KqlTelemetryService } from './kql_telemetry'; import { UsageCollectionSetup } from '../../usage_collection/server'; import { AutocompleteService } from './autocomplete'; -import { FieldFormatsService, FieldFormatsSetup, FieldFormatsStart } from './field_formats'; +import { FieldFormatsSetup, FieldFormatsStart } from '../../field_formats/server'; import { getUiSettings } from './ui_settings'; export interface DataEnhancements { @@ -27,6 +27,9 @@ export interface DataEnhancements { export interface DataPluginSetup { search: ISearchSetup; + /** + * @deprecated - use "fieldFormats" plugin directly instead + */ fieldFormats: FieldFormatsSetup; /** * @internal @@ -36,6 +39,9 @@ export interface DataPluginSetup { export interface DataPluginStart { search: ISearchStart; + /** + * @deprecated - use "fieldFormats" plugin directly instead + */ fieldFormats: FieldFormatsStart; indexPatterns: IndexPatternsServiceStart; } @@ -44,6 +50,7 @@ export interface DataPluginSetupDependencies { bfetch: BfetchServerSetup; expressions: ExpressionsServerSetup; usageCollection?: UsageCollectionSetup; + fieldFormats: FieldFormatsSetup; } export interface DataPluginStartDependencies { @@ -64,7 +71,6 @@ export class DataServerPlugin private readonly kqlTelemetryService: KqlTelemetryService; private readonly autocompleteService: AutocompleteService; private readonly indexPatterns = new IndexPatternsServiceProvider(); - private readonly fieldFormats = new FieldFormatsService(); private readonly queryService = new QueryService(); private readonly logger: Logger; @@ -78,7 +84,7 @@ export class DataServerPlugin public setup( core: CoreSetup, - { bfetch, expressions, usageCollection }: DataPluginSetupDependencies + { bfetch, expressions, usageCollection, fieldFormats }: DataPluginSetupDependencies ) { this.scriptsService.setup(core); this.queryService.setup(core); @@ -103,12 +109,11 @@ export class DataServerPlugin searchSetup.__enhance(enhancements.search); }, search: searchSetup, - fieldFormats: this.fieldFormats.setup(), + fieldFormats, }; } - public start(core: CoreStart) { - const fieldFormats = this.fieldFormats.start(); + public start(core: CoreStart, { fieldFormats }: DataPluginStartDependencies) { const indexPatterns = this.indexPatterns.start(core, { fieldFormats, logger: this.logger.get('indexPatterns'), diff --git a/src/plugins/data/server/search/aggs/aggs_service.test.ts b/src/plugins/data/server/search/aggs/aggs_service.test.ts index 0a4f54ee339a17..fdff2a4a8118ca 100644 --- a/src/plugins/data/server/search/aggs/aggs_service.test.ts +++ b/src/plugins/data/server/search/aggs/aggs_service.test.ts @@ -11,7 +11,7 @@ import { KibanaRequest, ElasticsearchClient } from 'src/core/server'; import { coreMock } from '../../../../../core/server/mocks'; import { expressionsPluginMock } from '../../../../../plugins/expressions/server/mocks'; import { BucketAggType, getAggTypes, MetricAggType } from '../../../common'; -import { createFieldFormatsStartMock } from '../../field_formats/mocks'; +import { createFieldFormatsStartMock } from '../../../../field_formats/server/mocks'; import { createIndexPatternsStartMock } from '../../index_patterns/mocks'; import { AggsService, AggsSetupDependencies, AggsStartDependencies } from './aggs_service'; diff --git a/src/plugins/data/server/search/aggs/aggs_service.ts b/src/plugins/data/server/search/aggs/aggs_service.ts index 96927728f2f2f5..197d8fd7caa990 100644 --- a/src/plugins/data/server/search/aggs/aggs_service.ts +++ b/src/plugins/data/server/search/aggs/aggs_service.ts @@ -22,7 +22,7 @@ import { calculateBounds, TimeRange, } from '../../../common'; -import { FieldFormatsStart } from '../../field_formats'; +import { FieldFormatsStart } from '../../../../field_formats/server'; import { IndexPatternsServiceStart } from '../../index_patterns'; import { AggsSetup, AggsStart } from './types'; diff --git a/src/plugins/data/server/search/search_service.test.ts b/src/plugins/data/server/search/search_service.test.ts index 314cb2c3acbf87..e6db3c76ef9ba8 100644 --- a/src/plugins/data/server/search/search_service.test.ts +++ b/src/plugins/data/server/search/search_service.test.ts @@ -11,7 +11,7 @@ import { CoreSetup, CoreStart, SavedObject } from '../../../../core/server'; import { coreMock } from '../../../../core/server/mocks'; import { DataPluginStart, DataPluginStartDependencies } from '../plugin'; -import { createFieldFormatsStartMock } from '../field_formats/mocks'; +import { createFieldFormatsStartMock } from '../../../field_formats/server/mocks'; import { createIndexPatternsStartMock } from '../index_patterns/mocks'; import { SearchService, SearchServiceSetupDependencies } from './search_service'; diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index cc7981220fa6ff..5b4ff121f3c77c 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -34,7 +34,7 @@ import type { import { AggsService } from './aggs'; -import { FieldFormatsStart } from '../field_formats'; +import { FieldFormatsStart } from '../../../field_formats/server'; import { IndexPatternsServiceStart } from '../index_patterns'; import { registerMsearchRoute, registerSearchRoute } from './routes'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './strategies/es_search'; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 9d5e598588f25b..51436dabe05a09 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -33,7 +33,6 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { ExpressionValueBoxed } from 'src/plugins/expressions/common'; import { Filter as Filter_2 } from '@kbn/es-query'; -import { FormatFactory as FormatFactory_2 } from 'src/plugins/data/common/field_formats/utils'; import { IAggConfigs as IAggConfigs_2 } from 'src/plugins/data/public'; import { IEsSearchResponse as IEsSearchResponse_2 } from 'src/plugins/data/public'; import { IFieldSubType as IFieldSubType_2 } from '@kbn/es-query'; @@ -60,7 +59,6 @@ import { PathConfigType } from '@kbn/utils'; import { Plugin as Plugin_2 } from 'src/core/server'; import { Plugin as Plugin_3 } from 'kibana/server'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/server'; -import { PublicMethodsOf } from '@kbn/utility-types'; import { Query } from '@kbn/es-query'; import { RecursiveReadonly } from '@kbn/utility-types'; import { RequestAdapter } from 'src/plugins/inspector/common'; @@ -73,7 +71,7 @@ import { SavedObjectsFindOptions } from 'kibana/server'; import { SavedObjectsFindResponse } from 'kibana/server'; import { SavedObjectsUpdateResponse } from 'kibana/server'; import { Search } from '@elastic/elasticsearch/api/requestParams'; -import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; +import { SerializedFieldFormat as SerializedFieldFormat_3 } from 'src/plugins/expressions/common'; import { SharedGlobalConfig as SharedGlobalConfig_2 } from 'kibana/server'; import { ToastInputFields } from 'src/core/public/notifications'; import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; @@ -488,48 +486,6 @@ export { FieldDescriptor } export { FieldDescriptor as IndexPatternFieldDescriptor } -// Warning: (ae-missing-release-tag) "FieldFormatConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface FieldFormatConfig { - // (undocumented) - es?: boolean; - // Warning: (ae-forgotten-export) The symbol "FieldFormatId" needs to be exported by the entry point index.d.ts - // - // (undocumented) - id: FieldFormatId; - // (undocumented) - params: Record; -} - -// Warning: (ae-missing-release-tag) "fieldFormats" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const fieldFormats: { - FieldFormatsRegistry: typeof FieldFormatsRegistry; - FieldFormat: typeof FieldFormat; - BoolFormat: typeof BoolFormat; - BytesFormat: typeof BytesFormat; - ColorFormat: typeof ColorFormat; - DurationFormat: typeof DurationFormat; - IpFormat: typeof IpFormat; - NumberFormat: typeof NumberFormat; - PercentFormat: typeof PercentFormat; - RelativeDateFormat: typeof RelativeDateFormat; - SourceFormat: typeof SourceFormat; - StaticLookupFormat: typeof StaticLookupFormat; - UrlFormat: typeof UrlFormat; - StringFormat: typeof StringFormat; - TruncateFormat: typeof TruncateFormat; - HistogramFormat: typeof HistogramFormat; -}; - -// Warning: (ae-forgotten-export) The symbol "GetConfigFn" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "FieldFormatsGetConfigFn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type FieldFormatsGetConfigFn = GetConfigFn; - // Warning: (ae-missing-release-tag) "Filter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @deprecated (undocumented) @@ -607,11 +563,6 @@ export interface IEsSearchRequest extends IKibanaSearchRequest = IKibanaSearchResponse>; -// Warning: (ae-missing-release-tag) "IFieldFormatsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type IFieldFormatsRegistry = PublicMethodsOf; - // Warning: (ae-forgotten-export) The symbol "FieldParamType" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IFieldParamType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -721,6 +672,7 @@ export class IndexPattern implements IIndexPattern { }; // (undocumented) getFieldByName(name: string): IndexPatternField | undefined; + // Warning: (ae-forgotten-export) The symbol "FieldFormat" needs to be exported by the entry point index.d.ts getFormatterForField(field: IndexPatternField | IndexPatternField['spec'] | IFieldType): FieldFormat; getFormatterForFieldNoDefault(fieldname: string): FieldFormat | undefined; // @deprecated (undocumented) @@ -769,7 +721,7 @@ export class IndexPattern implements IIndexPattern { // Warning: (ae-forgotten-export) The symbol "SerializedFieldFormat" needs to be exported by the entry point index.d.ts // // (undocumented) - readonly setFieldFormat: (fieldName: string, format: SerializedFieldFormat) => void; + readonly setFieldFormat: (fieldName: string, format: SerializedFieldFormat_2) => void; // Warning: (ae-forgotten-export) The symbol "SourceFilter" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1196,18 +1148,14 @@ export function parseInterval(interval: string): moment.Duration | null; export class Plugin implements Plugin_2 { constructor(initializerContext: PluginInitializerContext_2); // (undocumented) - setup(core: CoreSetup, { bfetch, expressions, usageCollection }: DataPluginSetupDependencies): { + setup(core: CoreSetup, { bfetch, expressions, usageCollection, fieldFormats }: DataPluginSetupDependencies): { __enhance: (enhancements: DataEnhancements) => void; search: ISearchSetup; - fieldFormats: { - register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number; - }; + fieldFormats: FieldFormatsSetup; }; // (undocumented) - start(core: CoreStart_2): { - fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; - }; + start(core: CoreStart_2, { fieldFormats }: DataPluginStartDependencies): { + fieldFormats: FieldFormatsStart; indexPatterns: { indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; }; @@ -1228,9 +1176,7 @@ export function plugin(initializerContext: PluginInitializerContext void; - // Warning: (ae-forgotten-export) The symbol "FieldFormatsSetup" needs to be exported by the entry point index.d.ts - // - // (undocumented) + // @deprecated (undocumented) fieldFormats: FieldFormatsSetup; // (undocumented) search: ISearchSetup; @@ -1240,9 +1186,7 @@ export interface PluginSetup { // // @public (undocumented) export interface PluginStart { - // Warning: (ae-forgotten-export) The symbol "FieldFormatsStart" needs to be exported by the entry point index.d.ts - // - // (undocumented) + // @deprecated (undocumented) fieldFormats: FieldFormatsStart; // (undocumented) indexPatterns: IndexPatternsServiceStart; @@ -1425,13 +1369,6 @@ export const UI_SETTINGS: { readonly HISTOGRAM_BAR_TARGET: "histogram:barTarget"; readonly HISTOGRAM_MAX_BARS: "histogram:maxBars"; readonly HISTORY_LIMIT: "history:limit"; - readonly SHORT_DOTS_ENABLE: "shortDots:enable"; - readonly FORMAT_DEFAULT_TYPE_MAP: "format:defaultTypeMap"; - readonly FORMAT_NUMBER_DEFAULT_PATTERN: "format:number:defaultPattern"; - readonly FORMAT_PERCENT_DEFAULT_PATTERN: "format:percent:defaultPattern"; - readonly FORMAT_BYTES_DEFAULT_PATTERN: "format:bytes:defaultPattern"; - readonly FORMAT_CURRENCY_DEFAULT_PATTERN: "format:currency:defaultPattern"; - readonly FORMAT_NUMBER_DEFAULT_LOCALE: "format:number:defaultLocale"; readonly TIMEPICKER_REFRESH_INTERVAL_DEFAULTS: "timepicker:refreshIntervalDefaults"; readonly TIMEPICKER_QUICK_RANGES: "timepicker:quickRanges"; readonly TIMEPICKER_TIME_DEFAULTS: "timepicker:timeDefaults"; @@ -1455,37 +1392,23 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:139:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:170:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:21:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "FieldFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:49:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:81:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:81:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:198:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:198:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:200:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:201:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:210:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:211:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:212:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:216:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:217:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:221:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:224:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:225:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/plugin.ts:81:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:38:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:38:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:155:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:155:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:157:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:158:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:167:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:168:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:169:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:173:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:174:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:178:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:181:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:182:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/plugin.ts:87:88 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/plugin.ts:110:7 - (ae-forgotten-export) The symbol "FieldFormatsSetup" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/plugin.ts:117:14 - (ae-forgotten-export) The symbol "FieldFormatsStart" needs to be exported by the entry point index.d.ts // src/plugins/data/server/search/types.ts:120:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/server/ui_settings.ts b/src/plugins/data/server/ui_settings.ts index 0723c4b2d85c22..360529ad5a735f 100644 --- a/src/plugins/data/server/ui_settings.ts +++ b/src/plugins/data/server/ui_settings.ts @@ -9,8 +9,6 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { UiSettingsParams } from 'kibana/server'; -// @ts-ignore untyped module -import numeralLanguages from '@elastic/numeral/languages'; import { DEFAULT_QUERY_LANGUAGE, UI_SETTINGS } from '../common'; const luceneQueryLanguageLabel = i18n.translate('data.advancedSettings.searchQueryLanguageLucene', { @@ -33,15 +31,6 @@ const requestPreferenceOptionLabels = { }), }; -// We add the `en` key manually here, since that's not a real numeral locale, but the -// default fallback in case the locale is not found. -const numeralLanguageIds = [ - 'en', - ...numeralLanguages.map((numeralLanguage: any) => { - return numeralLanguage.id; - }), -]; - export function getUiSettings(): Record> { return { [UI_SETTINGS.META_FIELDS]: { @@ -341,192 +330,6 @@ export function getUiSettings(): Record> { }), schema: schema.number(), }, - [UI_SETTINGS.SHORT_DOTS_ENABLE]: { - name: i18n.translate('data.advancedSettings.shortenFieldsTitle', { - defaultMessage: 'Shorten fields', - }), - value: false, - description: i18n.translate('data.advancedSettings.shortenFieldsText', { - defaultMessage: 'Shorten long fields, for example, instead of foo.bar.baz, show f.b.baz', - }), - schema: schema.boolean(), - }, - [UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP]: { - name: i18n.translate('data.advancedSettings.format.defaultTypeMapTitle', { - defaultMessage: 'Field type format name', - }), - value: `{ - "ip": { "id": "ip", "params": {} }, - "date": { "id": "date", "params": {} }, - "date_nanos": { "id": "date_nanos", "params": {}, "es": true }, - "number": { "id": "number", "params": {} }, - "boolean": { "id": "boolean", "params": {} }, - "histogram": { "id": "histogram", "params": {} }, - "_source": { "id": "_source", "params": {} }, - "_default_": { "id": "string", "params": {} } -}`, - type: 'json', - description: i18n.translate('data.advancedSettings.format.defaultTypeMapText', { - defaultMessage: - 'Map of the format name to use by default for each field type. ' + - '{defaultFormat} is used if the field type is not mentioned explicitly', - values: { - defaultFormat: '"_default_"', - }, - }), - schema: schema.object({ - ip: schema.object({ - id: schema.string(), - params: schema.object({}), - }), - date: schema.object({ - id: schema.string(), - params: schema.object({}), - }), - date_nanos: schema.object({ - id: schema.string(), - params: schema.object({}), - es: schema.boolean(), - }), - number: schema.object({ - id: schema.string(), - params: schema.object({}), - }), - boolean: schema.object({ - id: schema.string(), - params: schema.object({}), - }), - histogram: schema.object({ - id: schema.string(), - params: schema.object({}), - }), - _source: schema.object({ - id: schema.string(), - params: schema.object({}), - }), - _default_: schema.object({ - id: schema.string(), - params: schema.object({}), - }), - }), - }, - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: { - name: i18n.translate('data.advancedSettings.format.numberFormatTitle', { - defaultMessage: 'Number format', - }), - value: '0,0.[000]', - type: 'string', - description: i18n.translate('data.advancedSettings.format.numberFormatText', { - defaultMessage: 'Default {numeralFormatLink} for the "number" format', - description: - 'Part of composite text: data.advancedSettings.format.numberFormatText + ' + - 'data.advancedSettings.format.numberFormat.numeralFormatLinkText', - values: { - numeralFormatLink: - '' + - i18n.translate('data.advancedSettings.format.numberFormat.numeralFormatLinkText', { - defaultMessage: 'numeral format', - }) + - '', - }, - }), - schema: schema.string(), - }, - [UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: { - name: i18n.translate('data.advancedSettings.format.percentFormatTitle', { - defaultMessage: 'Percent format', - }), - value: '0,0.[000]%', - type: 'string', - description: i18n.translate('data.advancedSettings.format.percentFormatText', { - defaultMessage: 'Default {numeralFormatLink} for the "percent" format', - description: - 'Part of composite text: data.advancedSettings.format.percentFormatText + ' + - 'data.advancedSettings.format.percentFormat.numeralFormatLinkText', - values: { - numeralFormatLink: - '' + - i18n.translate('data.advancedSettings.format.percentFormat.numeralFormatLinkText', { - defaultMessage: 'numeral format', - }) + - '', - }, - }), - schema: schema.string(), - }, - [UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: { - name: i18n.translate('data.advancedSettings.format.bytesFormatTitle', { - defaultMessage: 'Bytes format', - }), - value: '0,0.[0]b', - type: 'string', - description: i18n.translate('data.advancedSettings.format.bytesFormatText', { - defaultMessage: 'Default {numeralFormatLink} for the "bytes" format', - description: - 'Part of composite text: data.advancedSettings.format.bytesFormatText + ' + - 'data.advancedSettings.format.bytesFormat.numeralFormatLinkText', - values: { - numeralFormatLink: - '' + - i18n.translate('data.advancedSettings.format.bytesFormat.numeralFormatLinkText', { - defaultMessage: 'numeral format', - }) + - '', - }, - }), - schema: schema.string(), - }, - [UI_SETTINGS.FORMAT_CURRENCY_DEFAULT_PATTERN]: { - name: i18n.translate('data.advancedSettings.format.currencyFormatTitle', { - defaultMessage: 'Currency format', - }), - value: '($0,0.[00])', - type: 'string', - description: i18n.translate('data.advancedSettings.format.currencyFormatText', { - defaultMessage: 'Default {numeralFormatLink} for the "currency" format', - description: - 'Part of composite text: data.advancedSettings.format.currencyFormatText + ' + - 'data.advancedSettings.format.currencyFormat.numeralFormatLinkText', - values: { - numeralFormatLink: - '' + - i18n.translate('data.advancedSettings.format.currencyFormat.numeralFormatLinkText', { - defaultMessage: 'numeral format', - }) + - '', - }, - }), - schema: schema.string(), - }, - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE]: { - name: i18n.translate('data.advancedSettings.format.formattingLocaleTitle', { - defaultMessage: 'Formatting locale', - }), - value: 'en', - type: 'select', - options: numeralLanguageIds, - optionLabels: Object.fromEntries( - numeralLanguages.map((language: Record) => [language.id, language.name]) - ), - description: i18n.translate('data.advancedSettings.format.formattingLocaleText', { - defaultMessage: `{numeralLanguageLink} locale`, - description: - 'Part of composite text: data.advancedSettings.format.formattingLocale.numeralLanguageLinkText + ' + - 'data.advancedSettings.format.formattingLocaleText', - values: { - numeralLanguageLink: - '' + - i18n.translate( - 'data.advancedSettings.format.formattingLocale.numeralLanguageLinkText', - { - defaultMessage: 'Numeral language', - } - ) + - '', - }, - }), - schema: schema.string(), - }, [UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS]: { name: i18n.translate('data.advancedSettings.timepicker.refreshIntervalDefaultsTitle', { defaultMessage: 'Time filter refresh interval', diff --git a/src/plugins/data/tsconfig.json b/src/plugins/data/tsconfig.json index 9c95878af631eb..6e4e89f7538fd7 100644 --- a/src/plugins/data/tsconfig.json +++ b/src/plugins/data/tsconfig.json @@ -23,6 +23,7 @@ { "path": "../inspector/tsconfig.json" }, { "path": "../usage_collection/tsconfig.json" }, { "path": "../kibana_utils/tsconfig.json" }, - { "path": "../kibana_react/tsconfig.json" } + { "path": "../kibana_react/tsconfig.json" }, + { "path": "../field_formats/tsconfig.json" } ] } diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json index 04469e0ef4276e..f612e0ec2cc8d5 100644 --- a/src/plugins/discover/kibana.json +++ b/src/plugins/discover/kibana.json @@ -16,7 +16,7 @@ "indexPatternFieldEditor" ], "optionalPlugins": ["home", "share", "usageCollection"], - "requiredBundles": ["kibanaUtils", "home", "kibanaReact"], + "requiredBundles": ["kibanaUtils", "home", "kibanaReact", "fieldFormats"], "owner": { "name": "Kibana App", "githubTeam": "kibana-app" diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts b/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts index 5e3a025d8c7baf..0f6c86df0db646 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header.ts @@ -9,7 +9,7 @@ import { TableHeader } from './table_header/table_header'; import { getServices } from '../../../../kibana_services'; import { SORT_DEFAULT_ORDER_SETTING, DOC_HIDE_TIME_COLUMN_SETTING } from '../../../../../common'; -import { UI_SETTINGS } from '../../../../../../data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../field_formats/common'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export function createTableHeaderDirective(reactDirective: any) { @@ -30,7 +30,7 @@ export function createTableHeaderDirective(reactDirective: any) { { restrict: 'A' }, { hideTimeColumn: config.get(DOC_HIDE_TIME_COLUMN_SETTING, false), - isShortDots: config.get(UI_SETTINGS.SHORT_DOTS_ENABLE), + isShortDots: config.get(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE), defaultSortOrder: config.get(SORT_DEFAULT_ORDER_SETTING, 'desc'), } ); diff --git a/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts b/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts index 80fb8a570f78b0..6b356446850e6f 100644 --- a/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts +++ b/src/plugins/discover/public/application/angular/helpers/row_formatter.test.ts @@ -9,7 +9,7 @@ import { formatRow, formatTopLevelObject } from './row_formatter'; import { stubbedSavedObjectIndexPattern } from '../../../__mocks__/stubbed_saved_object_index_pattern'; import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; -import { fieldFormatsMock } from '../../../../../data/common/field_formats/mocks'; +import { fieldFormatsMock } from '../../../../../field_formats/common/mocks'; import { setServices } from '../../../kibana_services'; import { DiscoverServices } from '../../../build_services'; diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index c0179ad3c8d20a..8c71091e3ecf26 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -24,6 +24,7 @@ { "path": "../kibana_utils/tsconfig.json" }, { "path": "../kibana_react/tsconfig.json" }, { "path": "../kibana_legacy/tsconfig.json" }, - { "path": "../index_pattern_field_editor/tsconfig.json"} + { "path": "../index_pattern_field_editor/tsconfig.json"}, + { "path": "../field_formats/tsconfig.json" } ] } diff --git a/src/plugins/field_formats/README.md b/src/plugins/field_formats/README.md new file mode 100755 index 00000000000000..1bb519af87acf0 --- /dev/null +++ b/src/plugins/field_formats/README.md @@ -0,0 +1,3 @@ +# Field formats + +Index pattern fields formatters diff --git a/src/plugins/data/common/field_formats/constants/base_formatters.ts b/src/plugins/field_formats/common/constants/base_formatters.ts similarity index 100% rename from src/plugins/data/common/field_formats/constants/base_formatters.ts rename to src/plugins/field_formats/common/constants/base_formatters.ts diff --git a/src/plugins/data/common/field_formats/constants/color_default.ts b/src/plugins/field_formats/common/constants/color_default.ts similarity index 100% rename from src/plugins/data/common/field_formats/constants/color_default.ts rename to src/plugins/field_formats/common/constants/color_default.ts diff --git a/src/plugins/field_formats/common/constants/ui_settings.ts b/src/plugins/field_formats/common/constants/ui_settings.ts new file mode 100644 index 00000000000000..98bae6bcf39d37 --- /dev/null +++ b/src/plugins/field_formats/common/constants/ui_settings.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const FORMATS_UI_SETTINGS = { + FORMAT_DEFAULT_TYPE_MAP: 'format:defaultTypeMap', + FORMAT_NUMBER_DEFAULT_PATTERN: 'format:number:defaultPattern', + FORMAT_PERCENT_DEFAULT_PATTERN: 'format:percent:defaultPattern', + FORMAT_BYTES_DEFAULT_PATTERN: 'format:bytes:defaultPattern', + FORMAT_CURRENCY_DEFAULT_PATTERN: 'format:currency:defaultPattern', + FORMAT_NUMBER_DEFAULT_LOCALE: 'format:number:defaultLocale', + SHORT_DOTS_ENABLE: 'shortDots:enable', +} as const; diff --git a/src/plugins/data/common/field_formats/content_types/html_content_type.ts b/src/plugins/field_formats/common/content_types/html_content_type.ts similarity index 100% rename from src/plugins/data/common/field_formats/content_types/html_content_type.ts rename to src/plugins/field_formats/common/content_types/html_content_type.ts diff --git a/src/plugins/data/common/field_formats/content_types/index.ts b/src/plugins/field_formats/common/content_types/index.ts similarity index 100% rename from src/plugins/data/common/field_formats/content_types/index.ts rename to src/plugins/field_formats/common/content_types/index.ts diff --git a/src/plugins/data/common/field_formats/content_types/text_content_type.ts b/src/plugins/field_formats/common/content_types/text_content_type.ts similarity index 100% rename from src/plugins/data/common/field_formats/content_types/text_content_type.ts rename to src/plugins/field_formats/common/content_types/text_content_type.ts diff --git a/src/plugins/data/common/field_formats/converters/boolean.test.ts b/src/plugins/field_formats/common/converters/boolean.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/boolean.test.ts rename to src/plugins/field_formats/common/converters/boolean.test.ts diff --git a/src/plugins/data/common/field_formats/converters/boolean.ts b/src/plugins/field_formats/common/converters/boolean.ts similarity index 94% rename from src/plugins/data/common/field_formats/converters/boolean.ts rename to src/plugins/field_formats/common/converters/boolean.ts index d70501bf0f9d5d..674e02fac8de0b 100644 --- a/src/plugins/data/common/field_formats/converters/boolean.ts +++ b/src/plugins/field_formats/common/converters/boolean.ts @@ -14,7 +14,7 @@ import { asPrettyString } from '../utils'; export class BoolFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.BOOLEAN; - static title = i18n.translate('data.fieldFormats.boolean.title', { + static title = i18n.translate('fieldFormats.boolean.title', { defaultMessage: 'Boolean', }); static fieldType = [KBN_FIELD_TYPES.BOOLEAN, KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING]; diff --git a/src/plugins/data/common/field_formats/converters/bytes.test.ts b/src/plugins/field_formats/common/converters/bytes.test.ts similarity index 85% rename from src/plugins/data/common/field_formats/converters/bytes.test.ts rename to src/plugins/field_formats/common/converters/bytes.test.ts index be8adbb6355ac1..a820b2c1f2b329 100644 --- a/src/plugins/data/common/field_formats/converters/bytes.test.ts +++ b/src/plugins/field_formats/common/converters/bytes.test.ts @@ -7,12 +7,12 @@ */ import { BytesFormat } from './bytes'; -import { UI_SETTINGS } from '../../constants'; +import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; describe('BytesFormat', () => { const config: Record = {}; - config[UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN] = '0,0.[000]b'; + config[FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN] = '0,0.[000]b'; const getConfig = (key: string) => config[key]; diff --git a/src/plugins/data/common/field_formats/converters/bytes.ts b/src/plugins/field_formats/common/converters/bytes.ts similarity index 91% rename from src/plugins/data/common/field_formats/converters/bytes.ts rename to src/plugins/field_formats/common/converters/bytes.ts index 840a59f3d3092d..829063aa07d029 100644 --- a/src/plugins/data/common/field_formats/converters/bytes.ts +++ b/src/plugins/field_formats/common/converters/bytes.ts @@ -12,7 +12,7 @@ import { FIELD_FORMAT_IDS } from '../types'; export class BytesFormat extends NumeralFormat { static id = FIELD_FORMAT_IDS.BYTES; - static title = i18n.translate('data.fieldFormats.bytes.title', { + static title = i18n.translate('fieldFormats.bytes.title', { defaultMessage: 'Bytes', }); diff --git a/src/plugins/data/common/field_formats/converters/color.test.ts b/src/plugins/field_formats/common/converters/color.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/color.test.ts rename to src/plugins/field_formats/common/converters/color.test.ts diff --git a/src/plugins/data/common/field_formats/converters/color.tsx b/src/plugins/field_formats/common/converters/color.tsx similarity index 97% rename from src/plugins/data/common/field_formats/converters/color.tsx rename to src/plugins/field_formats/common/converters/color.tsx index f653c6a4f82a21..d3a6964619caeb 100644 --- a/src/plugins/data/common/field_formats/converters/color.tsx +++ b/src/plugins/field_formats/common/converters/color.tsx @@ -18,7 +18,7 @@ import { DEFAULT_CONVERTER_COLOR } from '../constants/color_default'; export class ColorFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.COLOR; - static title = i18n.translate('data.fieldFormats.color.title', { + static title = i18n.translate('fieldFormats.color.title', { defaultMessage: 'Color', }); static fieldType = [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING]; diff --git a/src/plugins/data/common/field_formats/converters/custom.ts b/src/plugins/field_formats/common/converters/custom.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/custom.ts rename to src/plugins/field_formats/common/converters/custom.ts diff --git a/src/plugins/data/common/field_formats/converters/date_nanos_shared.test.ts b/src/plugins/field_formats/common/converters/date_nanos_shared.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/date_nanos_shared.test.ts rename to src/plugins/field_formats/common/converters/date_nanos_shared.test.ts diff --git a/src/plugins/data/common/field_formats/converters/date_nanos_shared.ts b/src/plugins/field_formats/common/converters/date_nanos_shared.ts similarity index 96% rename from src/plugins/data/common/field_formats/converters/date_nanos_shared.ts rename to src/plugins/field_formats/common/converters/date_nanos_shared.ts index d058d0c6a6574c..1d226c936a977a 100644 --- a/src/plugins/data/common/field_formats/converters/date_nanos_shared.ts +++ b/src/plugins/field_formats/common/converters/date_nanos_shared.ts @@ -8,8 +8,9 @@ import { i18n } from '@kbn/i18n'; import { memoize, noop } from 'lodash'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; import moment, { Moment } from 'moment'; -import { FieldFormat, FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../'; +import { FieldFormat, FIELD_FORMAT_IDS } from '../'; import { TextContextTypeConvert } from '../types'; /** @@ -59,7 +60,7 @@ export function formatWithNanos( export class DateNanosFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DATE_NANOS; - static title = i18n.translate('data.fieldFormats.date_nanos.title', { + static title = i18n.translate('fieldFormats.date_nanos.title', { defaultMessage: 'Date nanos', }); static fieldType = KBN_FIELD_TYPES.DATE; diff --git a/src/plugins/data/common/field_formats/converters/duration.test.ts b/src/plugins/field_formats/common/converters/duration.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/duration.test.ts rename to src/plugins/field_formats/common/converters/duration.test.ts diff --git a/src/plugins/data/common/field_formats/converters/duration.ts b/src/plugins/field_formats/common/converters/duration.ts similarity index 74% rename from src/plugins/data/common/field_formats/converters/duration.ts rename to src/plugins/field_formats/common/converters/duration.ts index 926f907af8fe96..a92a6756b335c8 100644 --- a/src/plugins/data/common/field_formats/converters/duration.ts +++ b/src/plugins/field_formats/common/converters/duration.ts @@ -21,76 +21,76 @@ const HUMAN_FRIENDLY = 'humanize'; const HUMAN_FRIENDLY_PRECISE = 'humanizePrecise'; const DEFAULT_OUTPUT_PRECISION = 2; const DEFAULT_INPUT_FORMAT = { - text: i18n.translate('data.fieldFormats.duration.inputFormats.seconds', { + text: i18n.translate('fieldFormats.duration.inputFormats.seconds', { defaultMessage: 'Seconds', }), kind: 'seconds', }; const inputFormats = [ { - text: i18n.translate('data.fieldFormats.duration.inputFormats.picoseconds', { + text: i18n.translate('fieldFormats.duration.inputFormats.picoseconds', { defaultMessage: 'Picoseconds', }), kind: 'picoseconds', }, { - text: i18n.translate('data.fieldFormats.duration.inputFormats.nanoseconds', { + text: i18n.translate('fieldFormats.duration.inputFormats.nanoseconds', { defaultMessage: 'Nanoseconds', }), kind: 'nanoseconds', }, { - text: i18n.translate('data.fieldFormats.duration.inputFormats.microseconds', { + text: i18n.translate('fieldFormats.duration.inputFormats.microseconds', { defaultMessage: 'Microseconds', }), kind: 'microseconds', }, { - text: i18n.translate('data.fieldFormats.duration.inputFormats.milliseconds', { + text: i18n.translate('fieldFormats.duration.inputFormats.milliseconds', { defaultMessage: 'Milliseconds', }), kind: 'milliseconds', }, { ...DEFAULT_INPUT_FORMAT }, { - text: i18n.translate('data.fieldFormats.duration.inputFormats.minutes', { + text: i18n.translate('fieldFormats.duration.inputFormats.minutes', { defaultMessage: 'Minutes', }), kind: 'minutes', }, { - text: i18n.translate('data.fieldFormats.duration.inputFormats.hours', { + text: i18n.translate('fieldFormats.duration.inputFormats.hours', { defaultMessage: 'Hours', }), kind: 'hours', }, { - text: i18n.translate('data.fieldFormats.duration.inputFormats.days', { + text: i18n.translate('fieldFormats.duration.inputFormats.days', { defaultMessage: 'Days', }), kind: 'days', }, { - text: i18n.translate('data.fieldFormats.duration.inputFormats.weeks', { + text: i18n.translate('fieldFormats.duration.inputFormats.weeks', { defaultMessage: 'Weeks', }), kind: 'weeks', }, { - text: i18n.translate('data.fieldFormats.duration.inputFormats.months', { + text: i18n.translate('fieldFormats.duration.inputFormats.months', { defaultMessage: 'Months', }), kind: 'months', }, { - text: i18n.translate('data.fieldFormats.duration.inputFormats.years', { + text: i18n.translate('fieldFormats.duration.inputFormats.years', { defaultMessage: 'Years', }), kind: 'years', }, ]; const DEFAULT_OUTPUT_FORMAT = { - text: i18n.translate('data.fieldFormats.duration.outputFormats.humanize.approximate', { + text: i18n.translate('fieldFormats.duration.outputFormats.humanize.approximate', { defaultMessage: 'Human-readable (approximate)', }), method: 'humanize', @@ -98,79 +98,79 @@ const DEFAULT_OUTPUT_FORMAT = { const outputFormats = [ { ...DEFAULT_OUTPUT_FORMAT }, { - text: i18n.translate('data.fieldFormats.duration.outputFormats.humanize.precise', { + text: i18n.translate('fieldFormats.duration.outputFormats.humanize.precise', { defaultMessage: 'Human-readable (precise)', }), method: 'humanizePrecise', }, { - text: i18n.translate('data.fieldFormats.duration.outputFormats.asMilliseconds', { + text: i18n.translate('fieldFormats.duration.outputFormats.asMilliseconds', { defaultMessage: 'Milliseconds', }), - shortText: i18n.translate('data.fieldFormats.duration.outputFormats.asMilliseconds.short', { + shortText: i18n.translate('fieldFormats.duration.outputFormats.asMilliseconds.short', { defaultMessage: 'ms', }), method: 'asMilliseconds', }, { - text: i18n.translate('data.fieldFormats.duration.outputFormats.asSeconds', { + text: i18n.translate('fieldFormats.duration.outputFormats.asSeconds', { defaultMessage: 'Seconds', }), - shortText: i18n.translate('data.fieldFormats.duration.outputFormats.asSeconds.short', { + shortText: i18n.translate('fieldFormats.duration.outputFormats.asSeconds.short', { defaultMessage: 's', }), method: 'asSeconds', }, { - text: i18n.translate('data.fieldFormats.duration.outputFormats.asMinutes', { + text: i18n.translate('fieldFormats.duration.outputFormats.asMinutes', { defaultMessage: 'Minutes', }), - shortText: i18n.translate('data.fieldFormats.duration.outputFormats.asMinutes.short', { + shortText: i18n.translate('fieldFormats.duration.outputFormats.asMinutes.short', { defaultMessage: 'min', }), method: 'asMinutes', }, { - text: i18n.translate('data.fieldFormats.duration.outputFormats.asHours', { + text: i18n.translate('fieldFormats.duration.outputFormats.asHours', { defaultMessage: 'Hours', }), - shortText: i18n.translate('data.fieldFormats.duration.outputFormats.asHours.short', { + shortText: i18n.translate('fieldFormats.duration.outputFormats.asHours.short', { defaultMessage: 'h', }), method: 'asHours', }, { - text: i18n.translate('data.fieldFormats.duration.outputFormats.asDays', { + text: i18n.translate('fieldFormats.duration.outputFormats.asDays', { defaultMessage: 'Days', }), - shortText: i18n.translate('data.fieldFormats.duration.outputFormats.asDays.short', { + shortText: i18n.translate('fieldFormats.duration.outputFormats.asDays.short', { defaultMessage: 'd', }), method: 'asDays', }, { - text: i18n.translate('data.fieldFormats.duration.outputFormats.asWeeks', { + text: i18n.translate('fieldFormats.duration.outputFormats.asWeeks', { defaultMessage: 'Weeks', }), - shortText: i18n.translate('data.fieldFormats.duration.outputFormats.asWeeks.short', { + shortText: i18n.translate('fieldFormats.duration.outputFormats.asWeeks.short', { defaultMessage: 'w', }), method: 'asWeeks', }, { - text: i18n.translate('data.fieldFormats.duration.outputFormats.asMonths', { + text: i18n.translate('fieldFormats.duration.outputFormats.asMonths', { defaultMessage: 'Months', }), - shortText: i18n.translate('data.fieldFormats.duration.outputFormats.asMonths.short', { + shortText: i18n.translate('fieldFormats.duration.outputFormats.asMonths.short', { defaultMessage: 'mon', }), method: 'asMonths', }, { - text: i18n.translate('data.fieldFormats.duration.outputFormats.asYears', { + text: i18n.translate('fieldFormats.duration.outputFormats.asYears', { defaultMessage: 'Years', }), - shortText: i18n.translate('data.fieldFormats.duration.outputFormats.asYears.short', { + shortText: i18n.translate('fieldFormats.duration.outputFormats.asYears.short', { defaultMessage: 'y', }), method: 'asYears', @@ -210,7 +210,7 @@ function formatInputHumanPrecise( export class DurationFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DURATION; - static title = i18n.translate('data.fieldFormats.duration.title', { + static title = i18n.translate('fieldFormats.duration.title', { defaultMessage: 'Duration', }); static fieldType = KBN_FIELD_TYPES.NUMBER; @@ -250,7 +250,7 @@ export class DurationFormat extends FieldFormat { const prefix = val < 0 && human - ? i18n.translate('data.fieldFormats.duration.negativeLabel', { + ? i18n.translate('fieldFormats.duration.negativeLabel', { defaultMessage: 'minus', }) + ' ' : ''; diff --git a/src/plugins/data/common/field_formats/converters/histogram.ts b/src/plugins/field_formats/common/converters/histogram.ts similarity index 95% rename from src/plugins/data/common/field_formats/converters/histogram.ts rename to src/plugins/field_formats/common/converters/histogram.ts index 0812dcc795dc94..586d42ffa7a314 100644 --- a/src/plugins/data/common/field_formats/converters/histogram.ts +++ b/src/plugins/field_formats/common/converters/histogram.ts @@ -17,7 +17,7 @@ import { PercentFormat } from './percent'; export class HistogramFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.HISTOGRAM; static fieldType = KBN_FIELD_TYPES.HISTOGRAM; - static title = i18n.translate('data.fieldFormats.histogram.title', { + static title = i18n.translate('fieldFormats.histogram.title', { defaultMessage: 'Histogram', }); diff --git a/src/plugins/data/common/field_formats/converters/index.ts b/src/plugins/field_formats/common/converters/index.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/index.ts rename to src/plugins/field_formats/common/converters/index.ts diff --git a/src/plugins/data/common/field_formats/converters/ip.test.ts b/src/plugins/field_formats/common/converters/ip.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/ip.test.ts rename to src/plugins/field_formats/common/converters/ip.test.ts diff --git a/src/plugins/data/common/field_formats/converters/ip.ts b/src/plugins/field_formats/common/converters/ip.ts similarity index 94% rename from src/plugins/data/common/field_formats/converters/ip.ts rename to src/plugins/field_formats/common/converters/ip.ts index 76fe4404e2418f..b981eca5db4410 100644 --- a/src/plugins/data/common/field_formats/converters/ip.ts +++ b/src/plugins/field_formats/common/converters/ip.ts @@ -13,7 +13,7 @@ import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class IpFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.IP; - static title = i18n.translate('data.fieldFormats.ip.title', { + static title = i18n.translate('fieldFormats.ip.title', { defaultMessage: 'IP address', }); static fieldType = KBN_FIELD_TYPES.IP; diff --git a/src/plugins/data/common/field_formats/converters/number.test.ts b/src/plugins/field_formats/common/converters/number.test.ts similarity index 85% rename from src/plugins/data/common/field_formats/converters/number.test.ts rename to src/plugins/field_formats/common/converters/number.test.ts index 837031b59dc5ce..da849fae1e6aba 100644 --- a/src/plugins/data/common/field_formats/converters/number.test.ts +++ b/src/plugins/field_formats/common/converters/number.test.ts @@ -7,12 +7,12 @@ */ import { NumberFormat } from './number'; -import { UI_SETTINGS } from '../../constants'; +import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; describe('NumberFormat', () => { const config: Record = {}; - config[UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN] = '0,0.[000]'; + config[FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN] = '0,0.[000]'; const getConfig = (key: string) => config[key]; diff --git a/src/plugins/data/common/field_formats/converters/number.ts b/src/plugins/field_formats/common/converters/number.ts similarity index 91% rename from src/plugins/data/common/field_formats/converters/number.ts rename to src/plugins/field_formats/common/converters/number.ts index c8c98d010dc61c..36470cbd148749 100644 --- a/src/plugins/data/common/field_formats/converters/number.ts +++ b/src/plugins/field_formats/common/converters/number.ts @@ -12,7 +12,7 @@ import { FIELD_FORMAT_IDS } from '../types'; export class NumberFormat extends NumeralFormat { static id = FIELD_FORMAT_IDS.NUMBER; - static title = i18n.translate('data.fieldFormats.number.title', { + static title = i18n.translate('fieldFormats.number.title', { defaultMessage: 'Number', }); diff --git a/src/plugins/data/common/field_formats/converters/numeral.ts b/src/plugins/field_formats/common/converters/numeral.ts similarity index 90% rename from src/plugins/data/common/field_formats/converters/numeral.ts rename to src/plugins/field_formats/common/converters/numeral.ts index 9d8b7ba1c369d4..7f11b6377836df 100644 --- a/src/plugins/data/common/field_formats/converters/numeral.ts +++ b/src/plugins/field_formats/common/converters/numeral.ts @@ -13,7 +13,7 @@ import numeralLanguages from '@elastic/numeral/languages'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { FieldFormat } from '../field_format'; import { TextContextTypeConvert } from '../types'; -import { UI_SETTINGS } from '../../constants'; +import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; const numeralInst = numeral(); @@ -42,7 +42,7 @@ export abstract class NumeralFormat extends FieldFormat { const previousLocale = numeral.language(); const defaultLocale = - (this.getConfig && this.getConfig(UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE)) || 'en'; + (this.getConfig && this.getConfig(FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE)) || 'en'; numeral.language(defaultLocale); const formatted = numeralInst.set(val).format(this.param('pattern')); diff --git a/src/plugins/data/common/field_formats/converters/percent.test.ts b/src/plugins/field_formats/common/converters/percent.test.ts similarity index 85% rename from src/plugins/data/common/field_formats/converters/percent.test.ts rename to src/plugins/field_formats/common/converters/percent.test.ts index 77f4274759ba36..d01acf571f9d94 100644 --- a/src/plugins/data/common/field_formats/converters/percent.test.ts +++ b/src/plugins/field_formats/common/converters/percent.test.ts @@ -7,12 +7,12 @@ */ import { PercentFormat } from './percent'; -import { UI_SETTINGS } from '../../constants'; +import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; describe('PercentFormat', () => { const config: Record = {}; - config[UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN] = '0,0.[000]%'; + config[FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN] = '0,0.[000]%'; const getConfig = (key: string) => config[key]; diff --git a/src/plugins/data/common/field_formats/converters/percent.ts b/src/plugins/field_formats/common/converters/percent.ts similarity index 82% rename from src/plugins/data/common/field_formats/converters/percent.ts rename to src/plugins/field_formats/common/converters/percent.ts index a402975c672e2d..3fd70c8ce74a87 100644 --- a/src/plugins/data/common/field_formats/converters/percent.ts +++ b/src/plugins/field_formats/common/converters/percent.ts @@ -9,11 +9,11 @@ import { i18n } from '@kbn/i18n'; import { NumeralFormat } from './numeral'; import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; -import { UI_SETTINGS } from '../../constants'; +import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; export class PercentFormat extends NumeralFormat { static id = FIELD_FORMAT_IDS.PERCENT; - static title = i18n.translate('data.fieldFormats.percent.title', { + static title = i18n.translate('fieldFormats.percent.title', { defaultMessage: 'Percentage', }); @@ -22,7 +22,7 @@ export class PercentFormat extends NumeralFormat { allowsNumericalAggregations = true; getParamDefaults = () => ({ - pattern: this.getConfig!(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN), + pattern: this.getConfig!(FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN), fractional: true, }); diff --git a/src/plugins/data/common/field_formats/converters/relative_date.test.ts b/src/plugins/field_formats/common/converters/relative_date.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/relative_date.test.ts rename to src/plugins/field_formats/common/converters/relative_date.test.ts diff --git a/src/plugins/data/common/field_formats/converters/relative_date.ts b/src/plugins/field_formats/common/converters/relative_date.ts similarity index 93% rename from src/plugins/data/common/field_formats/converters/relative_date.ts rename to src/plugins/field_formats/common/converters/relative_date.ts index c54e32452f2672..6111cbf838b79f 100644 --- a/src/plugins/data/common/field_formats/converters/relative_date.ts +++ b/src/plugins/field_formats/common/converters/relative_date.ts @@ -14,7 +14,7 @@ import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class RelativeDateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.RELATIVE_DATE; - static title = i18n.translate('data.fieldFormats.relative_date.title', { + static title = i18n.translate('fieldFormats.relative_date.title', { defaultMessage: 'Relative date', }); static fieldType = KBN_FIELD_TYPES.DATE; diff --git a/src/plugins/data/common/field_formats/converters/source.test.ts b/src/plugins/field_formats/common/converters/source.test.ts similarity index 87% rename from src/plugins/data/common/field_formats/converters/source.test.ts rename to src/plugins/field_formats/common/converters/source.test.ts index 655cf315a05a40..726f2c31e78251 100644 --- a/src/plugins/data/common/field_formats/converters/source.test.ts +++ b/src/plugins/field_formats/common/converters/source.test.ts @@ -9,7 +9,21 @@ import { SourceFormat } from './source'; import { HtmlContextTypeConvert } from '../types'; import { HTML_CONTEXT_TYPE } from '../content_types'; -import { stubIndexPatternWithFields } from '../../index_patterns/index_pattern.stub'; + +export const stubIndexPatternWithFields = { + id: '1234', + title: 'logstash-*', + fields: [ + { + name: 'response', + type: 'number', + esTypes: ['integer'], + aggregatable: true, + filterable: true, + searchable: true, + }, + ], +}; describe('Source Format', () => { let convertHtml: Function; diff --git a/src/plugins/data/common/field_formats/converters/source.tsx b/src/plugins/field_formats/common/converters/source.tsx similarity index 92% rename from src/plugins/data/common/field_formats/converters/source.tsx rename to src/plugins/field_formats/common/converters/source.tsx index de77736503f869..b3ed90329d6d47 100644 --- a/src/plugins/data/common/field_formats/converters/source.tsx +++ b/src/plugins/field_formats/common/converters/source.tsx @@ -6,14 +6,14 @@ * Side Public License, v 1. */ +import { KBN_FIELD_TYPES } from '@kbn/field-types'; import React, { Fragment } from 'react'; import ReactDOM from 'react-dom/server'; import { escape, keys } from 'lodash'; -import { KBN_FIELD_TYPES } from '@kbn/field-types'; -import { shortenDottedString } from '../../utils'; +import { shortenDottedString } from '../utils'; import { FieldFormat } from '../field_format'; import { TextContextTypeConvert, HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; -import { UI_SETTINGS } from '../../constants'; +import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; interface Props { defPairs: Array<[string, string]>; @@ -55,7 +55,7 @@ export class SourceFormat extends FieldFormat { const formatted = indexPattern.formatHit(hit); const highlightPairs: any[] = []; const sourcePairs: any[] = []; - const isShortDots = this.getConfig!(UI_SETTINGS.SHORT_DOTS_ENABLE); + const isShortDots = this.getConfig!(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE); keys(formatted).forEach((key) => { const pairs = highlights[key] ? highlightPairs : sourcePairs; diff --git a/src/plugins/data/common/field_formats/converters/static_lookup.ts b/src/plugins/field_formats/common/converters/static_lookup.ts similarity index 95% rename from src/plugins/data/common/field_formats/converters/static_lookup.ts rename to src/plugins/field_formats/common/converters/static_lookup.ts index 63d3506dda0bb2..ba5b38ba8b24fa 100644 --- a/src/plugins/data/common/field_formats/converters/static_lookup.ts +++ b/src/plugins/field_formats/common/converters/static_lookup.ts @@ -23,7 +23,7 @@ function convertLookupEntriesToMap(lookupEntries: any[]) { export class StaticLookupFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.STATIC_LOOKUP; - static title = i18n.translate('data.fieldFormats.static_lookup.title', { + static title = i18n.translate('fieldFormats.static_lookup.title', { defaultMessage: 'Static lookup', }); static fieldType = [ diff --git a/src/plugins/data/common/field_formats/converters/string.test.ts b/src/plugins/field_formats/common/converters/string.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/string.test.ts rename to src/plugins/field_formats/common/converters/string.test.ts diff --git a/src/plugins/data/common/field_formats/converters/string.ts b/src/plugins/field_formats/common/converters/string.ts similarity index 80% rename from src/plugins/data/common/field_formats/converters/string.ts rename to src/plugins/field_formats/common/converters/string.ts index c6aba38cf376de..149da871761e27 100644 --- a/src/plugins/data/common/field_formats/converters/string.ts +++ b/src/plugins/field_formats/common/converters/string.ts @@ -9,55 +9,54 @@ import { escape } from 'lodash'; import { i18n } from '@kbn/i18n'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; -import { asPrettyString, getHighlightHtml } from '../utils'; +import { asPrettyString, getHighlightHtml, shortenDottedString } from '../utils'; import { FieldFormat } from '../field_format'; import { TextContextTypeConvert, FIELD_FORMAT_IDS, HtmlContextTypeConvert } from '../types'; -import { shortenDottedString } from '../../utils'; -const emptyLabel = i18n.translate('data.fieldFormats.string.emptyLabel', { +const emptyLabel = i18n.translate('fieldFormats.string.emptyLabel', { defaultMessage: '(empty)', }); const TRANSFORM_OPTIONS = [ { kind: false, - text: i18n.translate('data.fieldFormats.string.transformOptions.none', { + text: i18n.translate('fieldFormats.string.transformOptions.none', { defaultMessage: '- None -', }), }, { kind: 'lower', - text: i18n.translate('data.fieldFormats.string.transformOptions.lower', { + text: i18n.translate('fieldFormats.string.transformOptions.lower', { defaultMessage: 'Lower Case', }), }, { kind: 'upper', - text: i18n.translate('data.fieldFormats.string.transformOptions.upper', { + text: i18n.translate('fieldFormats.string.transformOptions.upper', { defaultMessage: 'Upper Case', }), }, { kind: 'title', - text: i18n.translate('data.fieldFormats.string.transformOptions.title', { + text: i18n.translate('fieldFormats.string.transformOptions.title', { defaultMessage: 'Title Case', }), }, { kind: 'short', - text: i18n.translate('data.fieldFormats.string.transformOptions.short', { + text: i18n.translate('fieldFormats.string.transformOptions.short', { defaultMessage: 'Short Dots', }), }, { kind: 'base64', - text: i18n.translate('data.fieldFormats.string.transformOptions.base64', { + text: i18n.translate('fieldFormats.string.transformOptions.base64', { defaultMessage: 'Base64 Decode', }), }, { kind: 'urlparam', - text: i18n.translate('data.fieldFormats.string.transformOptions.url', { + text: i18n.translate('fieldFormats.string.transformOptions.url', { defaultMessage: 'URL Param Decode', }), }, @@ -66,7 +65,7 @@ const DEFAULT_TRANSFORM_OPTION = false; export class StringFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.STRING; - static title = i18n.translate('data.fieldFormats.string.title', { + static title = i18n.translate('fieldFormats.string.title', { defaultMessage: 'String', }); static fieldType = [ diff --git a/src/plugins/data/common/field_formats/converters/truncate.test.ts b/src/plugins/field_formats/common/converters/truncate.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/truncate.test.ts rename to src/plugins/field_formats/common/converters/truncate.test.ts diff --git a/src/plugins/data/common/field_formats/converters/truncate.ts b/src/plugins/field_formats/common/converters/truncate.ts similarity index 93% rename from src/plugins/data/common/field_formats/converters/truncate.ts rename to src/plugins/field_formats/common/converters/truncate.ts index 4c68a4c056b821..fd8bbcbc46541c 100644 --- a/src/plugins/data/common/field_formats/converters/truncate.ts +++ b/src/plugins/field_formats/common/converters/truncate.ts @@ -16,7 +16,7 @@ const omission = '...'; export class TruncateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.TRUNCATE; - static title = i18n.translate('data.fieldFormats.truncated_string.title', { + static title = i18n.translate('fieldFormats.truncated_string.title', { defaultMessage: 'Truncated string', }); static fieldType = KBN_FIELD_TYPES.STRING; diff --git a/src/plugins/data/common/field_formats/converters/url.test.ts b/src/plugins/field_formats/common/converters/url.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/converters/url.test.ts rename to src/plugins/field_formats/common/converters/url.test.ts diff --git a/src/plugins/data/common/field_formats/converters/url.ts b/src/plugins/field_formats/common/converters/url.ts similarity index 95% rename from src/plugins/data/common/field_formats/converters/url.ts rename to src/plugins/field_formats/common/converters/url.ts index 3e808d90f04090..07d9ed4cd535e3 100644 --- a/src/plugins/data/common/field_formats/converters/url.ts +++ b/src/plugins/field_formats/common/converters/url.ts @@ -24,19 +24,19 @@ const allowedUrlSchemes = ['http://', 'https://']; const URL_TYPES = [ { kind: 'a', - text: i18n.translate('data.fieldFormats.url.types.link', { + text: i18n.translate('fieldFormats.url.types.link', { defaultMessage: 'Link', }), }, { kind: 'img', - text: i18n.translate('data.fieldFormats.url.types.img', { + text: i18n.translate('fieldFormats.url.types.img', { defaultMessage: 'Image', }), }, { kind: 'audio', - text: i18n.translate('data.fieldFormats.url.types.audio', { + text: i18n.translate('fieldFormats.url.types.audio', { defaultMessage: 'Audio', }), }, @@ -45,7 +45,7 @@ const DEFAULT_URL_TYPE = 'a'; export class UrlFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.URL; - static title = i18n.translate('data.fieldFormats.url.title', { + static title = i18n.translate('fieldFormats.url.title', { defaultMessage: 'Url', }); static fieldType = [ diff --git a/src/plugins/data/common/field_formats/errors.ts b/src/plugins/field_formats/common/errors.ts similarity index 100% rename from src/plugins/data/common/field_formats/errors.ts rename to src/plugins/field_formats/common/errors.ts diff --git a/src/plugins/data/common/field_formats/field_format.test.ts b/src/plugins/field_formats/common/field_format.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/field_format.test.ts rename to src/plugins/field_formats/common/field_format.test.ts diff --git a/src/plugins/data/common/field_formats/field_format.ts b/src/plugins/field_formats/common/field_format.ts similarity index 100% rename from src/plugins/data/common/field_formats/field_format.ts rename to src/plugins/field_formats/common/field_format.ts diff --git a/src/plugins/data/common/field_formats/field_formats_registry.test.ts b/src/plugins/field_formats/common/field_formats_registry.test.ts similarity index 99% rename from src/plugins/data/common/field_formats/field_formats_registry.test.ts rename to src/plugins/field_formats/common/field_formats_registry.test.ts index 86e44b69c05bfa..e94efc88be20f6 100644 --- a/src/plugins/data/common/field_formats/field_formats_registry.test.ts +++ b/src/plugins/field_formats/common/field_formats_registry.test.ts @@ -9,7 +9,7 @@ import { FieldFormatsRegistry } from './field_formats_registry'; import { BoolFormat, PercentFormat, StringFormat } from './converters'; import { FieldFormatsGetConfigFn, FieldFormatInstanceType } from './types'; -import { KBN_FIELD_TYPES } from '../../common'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; const getValueOfPrivateField = (instance: any, field: string) => instance[field]; diff --git a/src/plugins/data/common/field_formats/field_formats_registry.ts b/src/plugins/field_formats/common/field_formats_registry.ts similarity index 96% rename from src/plugins/data/common/field_formats/field_formats_registry.ts rename to src/plugins/field_formats/common/field_formats_registry.ts index 9e96f5cc1a6bdf..675ec897c2b70e 100644 --- a/src/plugins/data/common/field_formats/field_formats_registry.ts +++ b/src/plugins/field_formats/common/field_formats_registry.ts @@ -17,13 +17,13 @@ import { FieldFormatInstanceType, FieldFormatId, IFieldFormatMetaParams, + SerializedFieldFormat, + FormatFactory, } from './types'; import { baseFormatters } from './constants/base_formatters'; import { FieldFormat } from './field_format'; -import { FormatFactory } from './utils'; -import { UI_SETTINGS } from '../constants'; -import { FieldFormatNotFoundError } from '../field_formats'; -import { SerializedFieldFormat } from '../../../expressions/common/types'; +import { FORMATS_UI_SETTINGS } from '../common/constants/ui_settings'; +import { FieldFormatNotFoundError } from './errors'; export class FieldFormatsRegistry { protected fieldFormats: Map = new Map(); @@ -53,7 +53,7 @@ export class FieldFormatsRegistry { metaParamsOptions: Record = {}, defaultFieldConverters: FieldFormatInstanceType[] = baseFormatters ) { - const defaultTypeMap = getConfig(UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP); + const defaultTypeMap = getConfig(FORMATS_UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP); this.register(defaultFieldConverters); this.parseDefaultTypeMap(defaultTypeMap); this.getConfig = getConfig; diff --git a/src/plugins/data/common/field_formats/index.ts b/src/plugins/field_formats/common/index.ts similarity index 90% rename from src/plugins/data/common/field_formats/index.ts rename to src/plugins/field_formats/common/index.ts index a3d763196e11f1..f2395eef29c549 100644 --- a/src/plugins/data/common/field_formats/index.ts +++ b/src/plugins/field_formats/common/index.ts @@ -33,6 +33,7 @@ export { export { getHighlightRequest } from './utils'; export { DEFAULT_CONVERTER_COLOR } from './constants/color_default'; +export { FORMATS_UI_SETTINGS } from './constants/ui_settings'; export { FIELD_FORMAT_IDS } from './types'; export { HTML_CONTEXT_TYPE, TEXT_CONTEXT_TYPE } from './content_types'; @@ -41,7 +42,9 @@ export { FieldFormatsContentType, FieldFormatConfig, FieldFormatId, - // Used in data plugin only + SerializedFieldFormat, + FormatFactory, + // Used in field format plugin only FieldFormatInstanceType, IFieldFormat, FieldFormatsStartCommon, diff --git a/src/plugins/data/common/field_formats/mocks.ts b/src/plugins/field_formats/common/mocks.ts similarity index 100% rename from src/plugins/data/common/field_formats/mocks.ts rename to src/plugins/field_formats/common/mocks.ts diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/field_formats/common/types.ts similarity index 73% rename from src/plugins/data/common/field_formats/types.ts rename to src/plugins/field_formats/common/types.ts index c09106751815f3..9c9112f45373bb 100644 --- a/src/plugins/data/common/field_formats/types.ts +++ b/src/plugins/field_formats/common/types.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { GetConfigFn } from '../types'; import { FieldFormat } from './field_format'; import { FieldFormatsRegistry } from './field_formats_registry'; @@ -65,7 +64,17 @@ export interface FieldFormatConfig { es?: boolean; } -export type FieldFormatsGetConfigFn = GetConfigFn; +/** + * If a service is being shared on both the client and the server, and + * the client code requires synchronous access to uiSettings, both client + * and server should wrap the core uiSettings services in a function + * matching this signature. + * + * This matches the signature of the public `core.uiSettings.get`, and + * should only be used in scenarios where async access to uiSettings is + * not possible. + */ +export type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T; export type IFieldFormat = FieldFormat; @@ -96,3 +105,17 @@ export interface IFieldFormatMetaParams { } export type FieldFormatsStartCommon = Omit; + +/** + * JSON representation of a field formatter configuration. + * Is used to carry information about how to format data in + * a data table as part of the column definition. + * + * @public + */ +export interface SerializedFieldFormat> { + id?: string; + params?: TParams; +} + +export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; diff --git a/src/plugins/data/common/field_formats/utils/as_pretty_string.test.ts b/src/plugins/field_formats/common/utils/as_pretty_string.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/utils/as_pretty_string.test.ts rename to src/plugins/field_formats/common/utils/as_pretty_string.test.ts diff --git a/src/plugins/data/common/field_formats/utils/as_pretty_string.ts b/src/plugins/field_formats/common/utils/as_pretty_string.ts similarity index 100% rename from src/plugins/data/common/field_formats/utils/as_pretty_string.ts rename to src/plugins/field_formats/common/utils/as_pretty_string.ts diff --git a/src/plugins/data/common/field_formats/utils/highlight/highlight_html.test.ts b/src/plugins/field_formats/common/utils/highlight/highlight_html.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/utils/highlight/highlight_html.test.ts rename to src/plugins/field_formats/common/utils/highlight/highlight_html.test.ts diff --git a/src/plugins/data/common/field_formats/utils/highlight/highlight_html.ts b/src/plugins/field_formats/common/utils/highlight/highlight_html.ts similarity index 100% rename from src/plugins/data/common/field_formats/utils/highlight/highlight_html.ts rename to src/plugins/field_formats/common/utils/highlight/highlight_html.ts diff --git a/src/plugins/data/common/field_formats/utils/highlight/highlight_request.test.ts b/src/plugins/field_formats/common/utils/highlight/highlight_request.test.ts similarity index 100% rename from src/plugins/data/common/field_formats/utils/highlight/highlight_request.test.ts rename to src/plugins/field_formats/common/utils/highlight/highlight_request.test.ts diff --git a/src/plugins/data/common/field_formats/utils/highlight/highlight_request.ts b/src/plugins/field_formats/common/utils/highlight/highlight_request.ts similarity index 100% rename from src/plugins/data/common/field_formats/utils/highlight/highlight_request.ts rename to src/plugins/field_formats/common/utils/highlight/highlight_request.ts diff --git a/src/plugins/data/common/field_formats/utils/highlight/highlight_tags.ts b/src/plugins/field_formats/common/utils/highlight/highlight_tags.ts similarity index 100% rename from src/plugins/data/common/field_formats/utils/highlight/highlight_tags.ts rename to src/plugins/field_formats/common/utils/highlight/highlight_tags.ts diff --git a/src/plugins/data/common/field_formats/utils/highlight/html_tags.ts b/src/plugins/field_formats/common/utils/highlight/html_tags.ts similarity index 100% rename from src/plugins/data/common/field_formats/utils/highlight/html_tags.ts rename to src/plugins/field_formats/common/utils/highlight/html_tags.ts diff --git a/src/plugins/data/common/field_formats/utils/highlight/index.ts b/src/plugins/field_formats/common/utils/highlight/index.ts similarity index 100% rename from src/plugins/data/common/field_formats/utils/highlight/index.ts rename to src/plugins/field_formats/common/utils/highlight/index.ts diff --git a/src/plugins/data/common/field_formats/utils/index.ts b/src/plugins/field_formats/common/utils/index.ts similarity index 70% rename from src/plugins/data/common/field_formats/utils/index.ts rename to src/plugins/field_formats/common/utils/index.ts index 5771c0166075db..3cfa0241c6f414 100644 --- a/src/plugins/data/common/field_formats/utils/index.ts +++ b/src/plugins/field_formats/common/utils/index.ts @@ -6,10 +6,6 @@ * Side Public License, v 1. */ -import { SerializedFieldFormat } from '../../../../expressions/common/types'; -import { IFieldFormat } from '../index'; - export { asPrettyString } from './as_pretty_string'; export { getHighlightHtml, getHighlightRequest } from './highlight'; - -export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; +export { shortenDottedString } from './shorten_dotted_string'; diff --git a/src/plugins/field_formats/common/utils/shorten_dotted_string.test.ts b/src/plugins/field_formats/common/utils/shorten_dotted_string.test.ts new file mode 100644 index 00000000000000..33a44925982ecf --- /dev/null +++ b/src/plugins/field_formats/common/utils/shorten_dotted_string.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { shortenDottedString } from './shorten_dotted_string'; + +describe('shortenDottedString', () => { + test('should convert a dot.notated.string into a short string', () => { + expect(shortenDottedString('dot.notated.string')).toBe('d.n.string'); + }); + + test('should ignore non-string values', () => { + const obj = { key: 'val' }; + + expect(shortenDottedString(true)).toBe(true); + expect(shortenDottedString(123)).toBe(123); + expect(shortenDottedString(obj)).toBe(obj); + }); +}); diff --git a/src/plugins/field_formats/common/utils/shorten_dotted_string.ts b/src/plugins/field_formats/common/utils/shorten_dotted_string.ts new file mode 100644 index 00000000000000..53f7471913dc3e --- /dev/null +++ b/src/plugins/field_formats/common/utils/shorten_dotted_string.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const DOT_PREFIX_RE = /(.).+?\./g; + +/** + * Convert a dot.notated.string into a short + * version (d.n.string) + * + * @return {any} + */ +export function shortenDottedString(input: any) { + return typeof input !== 'string' ? input : input.replace(DOT_PREFIX_RE, '$1.'); +} diff --git a/src/plugins/vis_type_timelion/public/helpers/active_cursor.ts b/src/plugins/field_formats/jest.config.js similarity index 71% rename from src/plugins/vis_type_timelion/public/helpers/active_cursor.ts rename to src/plugins/field_formats/jest.config.js index 7f7f62fd6a9dab..ea20fcfec6d099 100644 --- a/src/plugins/vis_type_timelion/public/helpers/active_cursor.ts +++ b/src/plugins/field_formats/jest.config.js @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import { Subject } from 'rxjs'; -import { PointerEvent } from '@elastic/charts'; - -export const activeCursor$ = new Subject(); +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/field_formats'], +}; diff --git a/src/plugins/field_formats/kibana.json b/src/plugins/field_formats/kibana.json new file mode 100755 index 00000000000000..ee5529697851d1 --- /dev/null +++ b/src/plugins/field_formats/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "fieldFormats", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": true, + "extraPublicDirs": ["common"], + "requiredPlugins": [], + "optionalPlugins": [], + "owner": { + "name": "App Services", + "githubTeam": "kibana-app-services" + }, + "description": "Index pattern fields and ambiguous values formatters" +} diff --git a/src/plugins/field_formats/public/index.scss b/src/plugins/field_formats/public/index.scss new file mode 100644 index 00000000000000..7928d81b1f565f --- /dev/null +++ b/src/plugins/field_formats/public/index.scss @@ -0,0 +1 @@ +@import './lib/converters/index'; diff --git a/src/plugins/field_formats/public/index.ts b/src/plugins/field_formats/public/index.ts new file mode 100755 index 00000000000000..f765513fb4c4c4 --- /dev/null +++ b/src/plugins/field_formats/public/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FieldFormatsPlugin } from './plugin'; +export { DateFormat, DateNanosFormat } from './lib/converters'; + +export function plugin() { + return new FieldFormatsPlugin(); +} +export { FieldFormatsSetup, FieldFormatsStart } from './plugin'; diff --git a/src/plugins/data/public/field_formats/constants.ts b/src/plugins/field_formats/public/lib/constants.ts similarity index 100% rename from src/plugins/data/public/field_formats/constants.ts rename to src/plugins/field_formats/public/lib/constants.ts diff --git a/src/plugins/data/public/field_formats/converters/_index.scss b/src/plugins/field_formats/public/lib/converters/_index.scss similarity index 100% rename from src/plugins/data/public/field_formats/converters/_index.scss rename to src/plugins/field_formats/public/lib/converters/_index.scss diff --git a/src/plugins/data/public/field_formats/converters/_string.scss b/src/plugins/field_formats/public/lib/converters/_string.scss similarity index 100% rename from src/plugins/data/public/field_formats/converters/_string.scss rename to src/plugins/field_formats/public/lib/converters/_string.scss diff --git a/src/plugins/data/public/field_formats/converters/date.test.ts b/src/plugins/field_formats/public/lib/converters/date.test.ts similarity index 100% rename from src/plugins/data/public/field_formats/converters/date.test.ts rename to src/plugins/field_formats/public/lib/converters/date.test.ts diff --git a/src/plugins/data/public/field_formats/converters/date.ts b/src/plugins/field_formats/public/lib/converters/date.ts similarity index 87% rename from src/plugins/data/public/field_formats/converters/date.ts rename to src/plugins/field_formats/public/lib/converters/date.ts index 1d74e6095427d8..acc051afd6b1da 100644 --- a/src/plugins/data/public/field_formats/converters/date.ts +++ b/src/plugins/field_formats/public/lib/converters/date.ts @@ -9,12 +9,13 @@ import { i18n } from '@kbn/i18n'; import { memoize, noop } from 'lodash'; import moment from 'moment'; -import { FieldFormat, KBN_FIELD_TYPES, FIELD_FORMAT_IDS } from '../../../common'; -import { TextContextTypeConvert } from '../../../common/field_formats/types'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { FieldFormat, FIELD_FORMAT_IDS } from '../../../common'; +import { TextContextTypeConvert } from '../../../common/types'; export class DateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DATE; - static title = i18n.translate('data.fieldFormats.date.title', { + static title = i18n.translate('fieldFormats.date.title', { defaultMessage: 'Date', }); static fieldType = KBN_FIELD_TYPES.DATE; diff --git a/src/plugins/data/public/field_formats/converters/date_nanos.ts b/src/plugins/field_formats/public/lib/converters/date_nanos.ts similarity index 79% rename from src/plugins/data/public/field_formats/converters/date_nanos.ts rename to src/plugins/field_formats/public/lib/converters/date_nanos.ts index f94d0e47a8ed4d..c2e0b3f929067f 100644 --- a/src/plugins/data/public/field_formats/converters/date_nanos.ts +++ b/src/plugins/field_formats/public/lib/converters/date_nanos.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { DateNanosFormat } from '../../../common/field_formats/converters/date_nanos_shared'; +export { DateNanosFormat } from '../../../common/converters/date_nanos_shared'; diff --git a/src/plugins/data/public/field_formats/converters/index.ts b/src/plugins/field_formats/public/lib/converters/index.ts similarity index 100% rename from src/plugins/data/public/field_formats/converters/index.ts rename to src/plugins/field_formats/public/lib/converters/index.ts diff --git a/src/plugins/data/public/field_formats/index.ts b/src/plugins/field_formats/public/lib/index.ts similarity index 82% rename from src/plugins/data/public/field_formats/index.ts rename to src/plugins/field_formats/public/lib/index.ts index 8b8b00b2b8746c..83a2dc60d8d952 100644 --- a/src/plugins/data/public/field_formats/index.ts +++ b/src/plugins/field_formats/public/lib/index.ts @@ -6,6 +6,5 @@ * Side Public License, v 1. */ -export { FieldFormatsService, FieldFormatsSetup, FieldFormatsStart } from './field_formats_service'; export { DateFormat, DateNanosFormat } from './converters'; export { baseFormattersPublic } from './constants'; diff --git a/src/plugins/data/public/field_formats/mocks.ts b/src/plugins/field_formats/public/mocks.ts similarity index 56% rename from src/plugins/data/public/field_formats/mocks.ts rename to src/plugins/field_formats/public/mocks.ts index 883af759392206..53f8cf3a174945 100644 --- a/src/plugins/data/public/field_formats/mocks.ts +++ b/src/plugins/field_formats/public/mocks.ts @@ -6,17 +6,26 @@ * Side Public License, v 1. */ -import type { PublicMethodsOf } from '@kbn/utility-types'; -import { FieldFormatsStart, FieldFormatsSetup, FieldFormatsService } from '.'; -import { fieldFormatsMock } from '../../common/field_formats/mocks'; +import { CoreSetup } from 'src/core/public'; +import { baseFormattersPublic } from './lib/constants'; +import { FieldFormatsRegistry } from '../common'; +import type { FieldFormatsStart, FieldFormatsSetup } from '.'; +import { fieldFormatsMock } from '../common/mocks'; -type FieldFormatsServiceClientContract = PublicMethodsOf; +export const getFieldFormatsRegistry = (core: CoreSetup) => { + const fieldFormatsRegistry = new FieldFormatsRegistry(); + const getConfig = core.uiSettings.get.bind(core.uiSettings); + + fieldFormatsRegistry.init(getConfig, {}, baseFormattersPublic); + + return fieldFormatsRegistry; +}; const createSetupContractMock = () => fieldFormatsMock as FieldFormatsSetup; const createStartContractMock = () => fieldFormatsMock as FieldFormatsStart; const createMock = () => { - const mocked: jest.Mocked = { + const mocked: jest.Mocked<{ start: () => FieldFormatsStart; setup: () => FieldFormatsSetup }> = { setup: jest.fn().mockReturnValue(createSetupContractMock()), start: jest.fn().mockReturnValue(createStartContractMock()), }; diff --git a/src/plugins/data/public/field_formats/field_formats_service.test.ts b/src/plugins/field_formats/public/plugin.test.ts similarity index 51% rename from src/plugins/data/public/field_formats/field_formats_service.test.ts rename to src/plugins/field_formats/public/plugin.test.ts index 06d007b2aff009..808413667918d2 100644 --- a/src/plugins/data/public/field_formats/field_formats_service.test.ts +++ b/src/plugins/field_formats/public/plugin.test.ts @@ -6,18 +6,18 @@ * Side Public License, v 1. */ -import { FieldFormatsService } from './field_formats_service'; -import { coreMock } from '../../../../../src/core/public/mocks'; -import { DateFormat } from './converters/date'; +import { coreMock } from '../../../../src/core/public/mocks'; +import { DateFormat } from './lib/converters/date'; +import { FieldFormatsPlugin } from './plugin'; -describe('FieldFormatService', () => { +describe('FieldFormatsPublic', () => { test('DateFormat is public version', () => { const mockCore = coreMock.createSetup(); - const service = new FieldFormatsService(); - service.setup(mockCore); - const fieldFormatsRegistry = service.start(); - const DateFormatFromRegsitry = fieldFormatsRegistry.getTypeWithoutMetaParams('date'); + const plugin = new FieldFormatsPlugin(); + plugin.setup(mockCore); + const fieldFormatsRegistry = plugin.start(); + const DateFormatFromRegistry = fieldFormatsRegistry.getTypeWithoutMetaParams('date'); - expect(DateFormatFromRegsitry).toEqual(DateFormat); + expect(DateFormatFromRegistry).toEqual(DateFormat); }); }); diff --git a/src/plugins/data/public/field_formats/field_formats_service.ts b/src/plugins/field_formats/public/plugin.ts old mode 100644 new mode 100755 similarity index 73% rename from src/plugins/data/public/field_formats/field_formats_service.ts rename to src/plugins/field_formats/public/plugin.ts index 041d0d7dd0e1c8..0e90aa9b48c8ed --- a/src/plugins/data/public/field_formats/field_formats_service.ts +++ b/src/plugins/field_formats/public/plugin.ts @@ -6,17 +6,18 @@ * Side Public License, v 1. */ -import { CoreSetup } from 'src/core/public'; -import { FieldFormatsRegistry, UI_SETTINGS } from '../../common'; -import { FormatFactory } from '../../common/field_formats/utils'; -import { baseFormattersPublic } from './constants'; +import { CoreSetup, Plugin } from '../../../core/public'; +import { FieldFormatsRegistry, FORMATS_UI_SETTINGS } from '../common'; +import { baseFormattersPublic } from './lib'; +import { FormatFactory } from '../common/types'; +import './index.scss'; -export class FieldFormatsService { +export class FieldFormatsPlugin implements Plugin { private readonly fieldFormatsRegistry: FieldFormatsRegistry = new FieldFormatsRegistry(); - public setup(core: CoreSetup) { + public setup(core: CoreSetup): FieldFormatsSetup { core.uiSettings.getUpdate$().subscribe(({ key, newValue }) => { - if (key === UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP) { + if (key === FORMATS_UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP) { this.fieldFormatsRegistry.parseDefaultTypeMap(newValue); } }); @@ -41,6 +42,8 @@ export class FieldFormatsService { public start() { return this.fieldFormatsRegistry as FieldFormatsStart; } + + public stop() {} } /** @public */ diff --git a/src/plugins/field_formats/server/index.ts b/src/plugins/field_formats/server/index.ts new file mode 100755 index 00000000000000..44de8fde558ec4 --- /dev/null +++ b/src/plugins/field_formats/server/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginInitializerContext } from '../../../core/server'; +import { FieldFormatsPlugin } from './plugin'; +export { DateFormat, DateNanosFormat } from './lib/converters'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new FieldFormatsPlugin(initializerContext); +} + +export { FieldFormatsSetup, FieldFormatsStart } from './types'; diff --git a/src/plugins/data/server/field_formats/converters/date_nanos_server.test.ts b/src/plugins/field_formats/server/lib/converters/date_nanos_server.test.ts similarity index 97% rename from src/plugins/data/server/field_formats/converters/date_nanos_server.test.ts rename to src/plugins/field_formats/server/lib/converters/date_nanos_server.test.ts index 3353f4a959b7eb..86f0b2a7dc875c 100644 --- a/src/plugins/data/server/field_formats/converters/date_nanos_server.test.ts +++ b/src/plugins/field_formats/server/lib/converters/date_nanos_server.test.ts @@ -7,7 +7,7 @@ */ import { DateNanosFormat } from './date_nanos_server'; -import { FieldFormatsGetConfigFn } from 'src/plugins/data/common'; +import { FieldFormatsGetConfigFn } from '../../../common'; describe('Date Nanos Format: Server side edition', () => { let convert: Function; diff --git a/src/plugins/data/server/field_formats/converters/date_nanos_server.ts b/src/plugins/field_formats/server/lib/converters/date_nanos_server.ts similarity index 94% rename from src/plugins/data/server/field_formats/converters/date_nanos_server.ts rename to src/plugins/field_formats/server/lib/converters/date_nanos_server.ts index 655c76d328f47a..d47475f6274fac 100644 --- a/src/plugins/data/server/field_formats/converters/date_nanos_server.ts +++ b/src/plugins/field_formats/server/lib/converters/date_nanos_server.ts @@ -12,8 +12,8 @@ import { analysePatternForFract, DateNanosFormat, formatWithNanos, -} from '../../../common/field_formats/converters/date_nanos_shared'; -import { TextContextTypeConvert } from '../../../common/field_formats/types'; +} from '../../../common/converters/date_nanos_shared'; +import { TextContextTypeConvert } from '../../../common/types'; class DateNanosFormatServer extends DateNanosFormat { textConvert: TextContextTypeConvert = (val) => { diff --git a/src/plugins/data/server/field_formats/converters/date_server.ts b/src/plugins/field_formats/server/lib/converters/date_server.ts similarity index 90% rename from src/plugins/data/server/field_formats/converters/date_server.ts rename to src/plugins/field_formats/server/lib/converters/date_server.ts index a86f3c894c5437..bf2151db0072c8 100644 --- a/src/plugins/data/server/field_formats/converters/date_server.ts +++ b/src/plugins/field_formats/server/lib/converters/date_server.ts @@ -9,20 +9,13 @@ import { i18n } from '@kbn/i18n'; import { memoize, noop } from 'lodash'; import moment from 'moment-timezone'; -import { - FieldFormat, - KBN_FIELD_TYPES, - FIELD_FORMAT_IDS, - FieldFormatsGetConfigFn, -} from '../../../common'; -import { - IFieldFormatMetaParams, - TextContextTypeConvert, -} from '../../../common/field_formats/types'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { FieldFormat, FIELD_FORMAT_IDS, FieldFormatsGetConfigFn } from '../../../common'; +import { IFieldFormatMetaParams, TextContextTypeConvert } from '../../../common/types'; export class DateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DATE; - static title = i18n.translate('data.fieldFormats.date.title', { + static title = i18n.translate('fieldFormats.date.title', { defaultMessage: 'Date', }); static fieldType = KBN_FIELD_TYPES.DATE; diff --git a/src/plugins/data/server/field_formats/converters/index.ts b/src/plugins/field_formats/server/lib/converters/index.ts similarity index 100% rename from src/plugins/data/server/field_formats/converters/index.ts rename to src/plugins/field_formats/server/lib/converters/index.ts diff --git a/src/plugins/data/server/field_formats/mocks.ts b/src/plugins/field_formats/server/mocks.ts similarity index 100% rename from src/plugins/data/server/field_formats/mocks.ts rename to src/plugins/field_formats/server/mocks.ts diff --git a/src/plugins/data/server/field_formats/field_formats_service.test.ts b/src/plugins/field_formats/server/plugin.test.ts similarity index 58% rename from src/plugins/data/server/field_formats/field_formats_service.test.ts rename to src/plugins/field_formats/server/plugin.test.ts index 0bfa9f89117e87..266d724ce5e220 100644 --- a/src/plugins/data/server/field_formats/field_formats_service.test.ts +++ b/src/plugins/field_formats/server/plugin.test.ts @@ -6,16 +6,16 @@ * Side Public License, v 1. */ -import { FieldFormatsService } from './field_formats_service'; -import { DateFormat } from './converters/date_server'; -import { coreMock } from '../../../../core/server/mocks'; +import { DateFormat } from './lib/converters/date_server'; +import { coreMock } from '../../../core/server/mocks'; +import { FieldFormatsPlugin } from './plugin'; -describe('FieldFormatService', () => { +describe('FieldFormats registry server plugin', () => { test('DateFormat is server version', async () => { - const service = new FieldFormatsService(); - const fieldFormatsService = await service.start(); + const plugin = new FieldFormatsPlugin(coreMock.createPluginInitializerContext()); + const pluginStart = await plugin.start(coreMock.createStart()); const uiSettings = coreMock.createStart().uiSettings.asScopedToClient({} as any); - const fieldFormatsRegistry = await fieldFormatsService.fieldFormatServiceFactory(uiSettings); + const fieldFormatsRegistry = await pluginStart.fieldFormatServiceFactory(uiSettings); const DateFormatFromRegsitry = fieldFormatsRegistry.getTypeWithoutMetaParams('date'); expect(DateFormatFromRegsitry).toEqual(DateFormat); diff --git a/src/plugins/data/server/field_formats/field_formats_service.ts b/src/plugins/field_formats/server/plugin.ts old mode 100644 new mode 100755 similarity index 60% rename from src/plugins/data/server/field_formats/field_formats_service.ts rename to src/plugins/field_formats/server/plugin.ts index fa94e5dcef9cf8..e2c64807433a99 --- a/src/plugins/data/server/field_formats/field_formats_service.ts +++ b/src/plugins/field_formats/server/plugin.ts @@ -8,28 +8,36 @@ import { has } from 'lodash'; import { - FieldFormatsRegistry, - FieldFormatInstanceType, - baseFormatters, -} from '../../common/field_formats'; -import { IUiSettingsClient } from '../../../../core/server'; -import { DateFormat, DateNanosFormat } from './converters'; - -export class FieldFormatsService { - private readonly fieldFormatClasses: FieldFormatInstanceType[] = [ + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, + IUiSettingsClient, +} from '../../../core/server'; +import { FieldFormatsStart, FieldFormatsSetup } from './types'; +import { DateFormat, DateNanosFormat } from './lib/converters'; +import { baseFormatters, FieldFormatInstanceType, FieldFormatsRegistry } from '../common'; +import { getUiSettings } from './ui_settings'; + +export class FieldFormatsPlugin implements Plugin { + private readonly fieldFormats: FieldFormatInstanceType[] = [ DateFormat, DateNanosFormat, ...baseFormatters, ]; - public setup() { + constructor(initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup) { + core.uiSettings.register(getUiSettings()); + return { register: (customFieldFormat: FieldFormatInstanceType) => - this.fieldFormatClasses.push(customFieldFormat), + this.fieldFormats.push(customFieldFormat), }; } - public start() { + public start(core: CoreStart) { return { fieldFormatServiceFactory: async (uiSettings: IUiSettingsClient) => { const fieldFormatsRegistry = new FieldFormatsRegistry(); @@ -43,16 +51,12 @@ export class FieldFormatsService { } }); - fieldFormatsRegistry.init((key: string) => uiConfigs[key], {}, this.fieldFormatClasses); + fieldFormatsRegistry.init((key: string) => uiConfigs[key], {}, this.fieldFormats); return fieldFormatsRegistry; }, }; } -} -/** @public */ -export type FieldFormatsSetup = ReturnType; - -/** @public */ -export type FieldFormatsStart = ReturnType; + public stop() {} +} diff --git a/src/plugins/field_formats/server/types.ts b/src/plugins/field_formats/server/types.ts new file mode 100755 index 00000000000000..eaf9c9ffdc3909 --- /dev/null +++ b/src/plugins/field_formats/server/types.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FieldFormatInstanceType, FieldFormatsRegistry } from '../common'; +import { IUiSettingsClient } from '../../../core/server'; + +export interface FieldFormatsSetup { + /** + * Register a server side field formatter + * @param fieldFormat {@link FieldFormatInstanceType} + */ + register: (fieldFormat: FieldFormatInstanceType) => void; +} + +export interface FieldFormatsStart { + /** + * Create a field format registry + * @param uiSettings - {@link IUiSettingsClient} + */ + fieldFormatServiceFactory: (uiSettings: IUiSettingsClient) => Promise; +} diff --git a/src/plugins/field_formats/server/ui_settings.ts b/src/plugins/field_formats/server/ui_settings.ts new file mode 100644 index 00000000000000..f24ddc60e1f974 --- /dev/null +++ b/src/plugins/field_formats/server/ui_settings.ts @@ -0,0 +1,226 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; +import { UiSettingsParams } from 'kibana/server'; +// @ts-ignore untyped module +import numeralLanguages from '@elastic/numeral/languages'; +import { FORMATS_UI_SETTINGS } from '../common'; + +// We add the `en` key manually here, since that's not a real numeral locale, but the +// default fallback in case the locale is not found. +const numeralLanguageIds = [ + 'en', + ...numeralLanguages.map((numeralLanguage: any) => { + return numeralLanguage.id; + }), +]; + +export function getUiSettings(): Record> { + return { + [FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE]: { + name: i18n.translate('fieldFormats.advancedSettings.shortenFieldsTitle', { + defaultMessage: 'Shorten fields', + }), + value: false, + description: i18n.translate('fieldFormats.advancedSettings.shortenFieldsText', { + defaultMessage: 'Shorten long fields, for example, instead of foo.bar.baz, show f.b.baz', + }), + schema: schema.boolean(), + }, + [FORMATS_UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP]: { + name: i18n.translate('fieldFormats.advancedSettings.format.defaultTypeMapTitle', { + defaultMessage: 'Field type format name', + }), + value: `{ + "ip": { "id": "ip", "params": {} }, + "date": { "id": "date", "params": {} }, + "date_nanos": { "id": "date_nanos", "params": {}, "es": true }, + "number": { "id": "number", "params": {} }, + "boolean": { "id": "boolean", "params": {} }, + "histogram": { "id": "histogram", "params": {} }, + "_source": { "id": "_source", "params": {} }, + "_default_": { "id": "string", "params": {} } +}`, + type: 'json', + description: i18n.translate('fieldFormats.advancedSettings.format.defaultTypeMapText', { + defaultMessage: + 'Map of the format name to use by default for each field type. ' + + '{defaultFormat} is used if the field type is not mentioned explicitly', + values: { + defaultFormat: '"_default_"', + }, + }), + schema: schema.object({ + ip: schema.object({ + id: schema.string(), + params: schema.object({}), + }), + date: schema.object({ + id: schema.string(), + params: schema.object({}), + }), + date_nanos: schema.object({ + id: schema.string(), + params: schema.object({}), + es: schema.boolean(), + }), + number: schema.object({ + id: schema.string(), + params: schema.object({}), + }), + boolean: schema.object({ + id: schema.string(), + params: schema.object({}), + }), + histogram: schema.object({ + id: schema.string(), + params: schema.object({}), + }), + _source: schema.object({ + id: schema.string(), + params: schema.object({}), + }), + _default_: schema.object({ + id: schema.string(), + params: schema.object({}), + }), + }), + }, + [FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: { + name: i18n.translate('fieldFormats.advancedSettings.format.numberFormatTitle', { + defaultMessage: 'Number format', + }), + value: '0,0.[000]', + type: 'string', + description: i18n.translate('fieldFormats.advancedSettings.format.numberFormatText', { + defaultMessage: 'Default {numeralFormatLink} for the "number" format', + description: + 'Part of composite text: fieldFormats.advancedSettings.format.numberFormatText + ' + + 'fieldFormats.advancedSettings.format.numberFormat.numeralFormatLinkText', + values: { + numeralFormatLink: + '' + + i18n.translate( + 'fieldFormats.advancedSettings.format.numberFormat.numeralFormatLinkText', + { + defaultMessage: 'numeral format', + } + ) + + '', + }, + }), + schema: schema.string(), + }, + [FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: { + name: i18n.translate('fieldFormats.advancedSettings.format.percentFormatTitle', { + defaultMessage: 'Percent format', + }), + value: '0,0.[000]%', + type: 'string', + description: i18n.translate('fieldFormats.advancedSettings.format.percentFormatText', { + defaultMessage: 'Default {numeralFormatLink} for the "percent" format', + description: + 'Part of composite text: fieldFormats.advancedSettings.format.percentFormatText + ' + + 'fieldFormats.advancedSettings.format.percentFormat.numeralFormatLinkText', + values: { + numeralFormatLink: + '' + + i18n.translate( + 'fieldFormats.advancedSettings.format.percentFormat.numeralFormatLinkText', + { + defaultMessage: 'numeral format', + } + ) + + '', + }, + }), + schema: schema.string(), + }, + [FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: { + name: i18n.translate('fieldFormats.advancedSettings.format.bytesFormatTitle', { + defaultMessage: 'Bytes format', + }), + value: '0,0.[0]b', + type: 'string', + description: i18n.translate('fieldFormats.advancedSettings.format.bytesFormatText', { + defaultMessage: 'Default {numeralFormatLink} for the "bytes" format', + description: + 'Part of composite text: fieldFormats.advancedSettings.format.bytesFormatText + ' + + 'fieldFormats.advancedSettings.format.bytesFormat.numeralFormatLinkText', + values: { + numeralFormatLink: + '' + + i18n.translate( + 'fieldFormats.advancedSettings.format.bytesFormat.numeralFormatLinkText', + { + defaultMessage: 'numeral format', + } + ) + + '', + }, + }), + schema: schema.string(), + }, + [FORMATS_UI_SETTINGS.FORMAT_CURRENCY_DEFAULT_PATTERN]: { + name: i18n.translate('fieldFormats.advancedSettings.format.currencyFormatTitle', { + defaultMessage: 'Currency format', + }), + value: '($0,0.[00])', + type: 'string', + description: i18n.translate('fieldFormats.advancedSettings.format.currencyFormatText', { + defaultMessage: 'Default {numeralFormatLink} for the "currency" format', + description: + 'Part of composite text: fieldFormats.advancedSettings.format.currencyFormatText + ' + + 'fieldFormats.advancedSettings.format.currencyFormat.numeralFormatLinkText', + values: { + numeralFormatLink: + '' + + i18n.translate( + 'fieldFormats.advancedSettings.format.currencyFormat.numeralFormatLinkText', + { + defaultMessage: 'numeral format', + } + ) + + '', + }, + }), + schema: schema.string(), + }, + [FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE]: { + name: i18n.translate('fieldFormats.advancedSettings.format.formattingLocaleTitle', { + defaultMessage: 'Formatting locale', + }), + value: 'en', + type: 'select', + options: numeralLanguageIds, + optionLabels: Object.fromEntries( + numeralLanguages.map((language: Record) => [language.id, language.name]) + ), + description: i18n.translate('fieldFormats.advancedSettings.format.formattingLocaleText', { + defaultMessage: `{numeralLanguageLink} locale`, + description: + 'Part of composite text: fieldFormats.advancedSettings.format.formattingLocale.numeralLanguageLinkText + ' + + 'fieldFormats.advancedSettings.format.formattingLocaleText', + values: { + numeralLanguageLink: + '' + + i18n.translate( + 'fieldFormats.advancedSettings.format.formattingLocale.numeralLanguageLinkText', + { + defaultMessage: 'Numeral language', + } + ) + + '', + }, + }), + schema: schema.string(), + }, + }; +} diff --git a/src/plugins/field_formats/tsconfig.json b/src/plugins/field_formats/tsconfig.json new file mode 100644 index 00000000000000..4382ab1051c1d6 --- /dev/null +++ b/src/plugins/field_formats/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + "config.ts", + "common/**/*.json", + "public/**/*.json" + ], + "references": [{ "path": "../../core/tsconfig.json" }] +} diff --git a/src/plugins/index_pattern_field_editor/kibana.json b/src/plugins/index_pattern_field_editor/kibana.json index a3a7e61e9806e5..02308b349d4ca1 100644 --- a/src/plugins/index_pattern_field_editor/kibana.json +++ b/src/plugins/index_pattern_field_editor/kibana.json @@ -5,7 +5,7 @@ "ui": true, "requiredPlugins": ["data"], "optionalPlugins": ["usageCollection"], - "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection"], + "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection", "fieldFormats"], "owner": { "name": "App Services", "githubTeam": "kibana-app-services" diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/bytes.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/bytes.test.tsx index 04be866a33ca73..219a51e421b44e 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/bytes.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/bytes.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { coreMock } from 'src/core/public/mocks'; import { createKibanaReactContext } from '../../../../../../kibana_react/public'; -import { FieldFormat } from 'src/plugins/data/public'; +import { FieldFormat } from 'src/plugins/field_formats/common'; import { BytesFormatEditor } from './bytes'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.test.tsx index 1026012f3b8878..4b0df63261496d 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.test.tsx @@ -8,17 +8,16 @@ import React from 'react'; import { shallowWithI18nProvider } from '@kbn/test/jest'; -import { FieldFormat } from 'src/plugins/data/public'; import { ColorFormatEditor } from './color'; -import { fieldFormats } from '../../../../../../data/public'; +import { FieldFormat, DEFAULT_CONVERTER_COLOR } from '../../../../../../field_formats/common'; const fieldType = 'string'; const format = { getConverterFor: jest.fn(), }; const formatParams = { - colors: [{ ...fieldFormats.DEFAULT_CONVERTER_COLOR }], + colors: [{ ...DEFAULT_CONVERTER_COLOR }], }; const onChange = jest.fn(); const onError = jest.fn(); diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx index 3570ef4c9d33a6..67290212c04ddc 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx @@ -15,7 +15,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { DefaultFormatEditor } from '../default/default'; import { formatId } from './constants'; -import { fieldFormats } from '../../../../../../data/public'; +import { DEFAULT_CONVERTER_COLOR } from '../../../../../../field_formats/common'; import { FormatEditorProps } from '../types'; interface Color { @@ -56,7 +56,7 @@ export class ColorFormatEditor extends DefaultFormatEditor { const colors = [...(this.props.formatParams.colors || [])]; this.onChange({ - colors: [...colors, { ...fieldFormats.DEFAULT_CONVERTER_COLOR }], + colors: [...colors, { ...DEFAULT_CONVERTER_COLOR }], }); }; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/date.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/date.test.tsx index f3f46cbbdda31c..348ac86e209eae 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/date.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/date.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; import { DateFormatEditor } from './date'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx index 4e8d56f91c6eb1..8fc4f4dbdfdb61 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import type { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; import { DateNanosFormatEditor } from './date_nanos'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.test.tsx index 90f1d43fa357a3..c56d1c0261ed1c 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { FieldFormat } from 'src/plugins/data/public'; +import { FieldFormat } from 'src/plugins/field_formats/common'; import { SampleInput } from '../../types'; import { DefaultFormatEditor, convertSampleInput } from './default'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx index 7228a02afa2a5c..129049e1b05657 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx @@ -9,7 +9,7 @@ import React, { PureComponent, ReactText } from 'react'; import { i18n } from '@kbn/i18n'; -import { FieldFormatsContentType } from 'src/plugins/data/public'; +import type { FieldFormatsContentType } from 'src/plugins/field_formats/common'; import { Sample, SampleInput } from '../../types'; import { FormatEditorProps } from '../types'; import { formatId } from './constants'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.test.tsx index 80a57b94197638..dbc369126d64e6 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { DurationFormatEditor } from './duration'; -import { FieldFormat } from 'src/plugins/data/public'; +import { FieldFormat } from 'src/plugins/field_formats/common'; import { EuiSwitch } from '@elastic/eui'; const fieldType = 'number'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx index 2bfb0182cbb880..89bac89a95b768 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx @@ -7,7 +7,7 @@ */ import React, { Fragment } from 'react'; -import { DurationFormat } from 'src/plugins/data/common'; +import { DurationFormat } from 'src/plugins/field_formats/common'; import { EuiFieldNumber, EuiFormRow, EuiSelect, EuiSwitch } from '@elastic/eui'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/histogram/histogram.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/histogram/histogram.test.tsx index 053dff12139086..4c34af8748f599 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/histogram/histogram.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/histogram/histogram.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; import { HistogramFormatEditor } from './histogram'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.test.tsx index ad71263978d9c9..1554da93dac532 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { coreMock } from 'src/core/public/mocks'; import { createKibanaReactContext } from '../../../../../../kibana_react/public'; -import { FieldFormat } from 'src/plugins/data/public'; +import { FieldFormat } from 'src/plugins/field_formats/common'; import { NumberFormatEditor } from './number'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/percent.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/percent.test.tsx index 6f6a88cfb7c955..ef71055deca7ba 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/percent.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/percent.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { coreMock } from 'src/core/public/mocks'; import { createKibanaReactContext } from '../../../../../../kibana_react/public'; -import { FieldFormat } from 'src/plugins/data/public'; +import { FieldFormat } from 'src/plugins/field_formats/common'; import { PercentFormatEditor } from './percent'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx index 8d9cb17b33a403..68a65e5a0fa6cb 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { shallowWithI18nProvider } from '@kbn/test/jest'; import { StaticLookupFormatEditorFormatParams } from './static_lookup'; -import { FieldFormat } from 'src/plugins/data/public'; +import { FieldFormat } from 'src/plugins/field_formats/common'; import { StaticLookupFormatEditor } from './static_lookup'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.test.tsx index 5b573d938ebb28..61076353dbf5e0 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { FieldFormat } from 'src/plugins/data/public'; +import { FieldFormat } from 'src/plugins/field_formats/common'; import { StringFormatEditor } from './string'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.test.tsx index 6a4ae0fde0c598..acba1fbedb2061 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.test.tsx @@ -9,7 +9,7 @@ import React, { ChangeEvent } from 'react'; import { shallow } from 'enzyme'; import { EuiFieldNumber } from '@elastic/eui'; -import { FieldFormat } from 'src/plugins/data/public'; +import { FieldFormat } from 'src/plugins/field_formats/common'; import { TruncateFormatEditor } from './truncate'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/types.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/types.ts index 556078286e5243..cbdd8efb2a4c87 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/types.ts +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/types.ts @@ -7,7 +7,7 @@ */ import type { ComponentType } from 'react'; -import type { FieldFormat } from '../../../../../data/common'; +import type { FieldFormat } from '../../../../../field_formats/common'; import type { FormatSelectEditorProps } from '../field_format_editor'; /** diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.test.tsx index 1000d9d2b86505..79dc63230dc33d 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.test.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; import { __IntlProvider as IntlProvider } from '@kbn/i18n/react'; import { UrlFormatEditor } from './url'; import { coreMock } from 'src/core/public/mocks'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx index ccb6cf7794f871..1c0c7ecba3b2bc 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx @@ -12,12 +12,12 @@ import { EuiCode, EuiFormRow, EuiSelect } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { - FieldFormatInstanceType, IndexPattern, KBN_FIELD_TYPES, ES_FIELD_TYPES, DataPublicPluginStart, } from 'src/plugins/data/public'; +import type { FieldFormatInstanceType } from 'src/plugins/field_formats/common'; import { CoreStart } from 'src/core/public'; import { castEsToKbnFieldTypeName } from '../../../../data/public'; import { FormatEditor } from './format_editor'; diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx index dbbaf51f114b11..c6f5fc9899ac7d 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx @@ -9,7 +9,7 @@ import { EuiDelayRender, EuiLoadingContent } from '@elastic/eui'; import { memoize } from 'lodash'; import React, { PureComponent, LazyExoticComponent } from 'react'; -import type { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; import { FieldFormatEditorFactory, FieldFormatEditor } from './editors'; export interface FormatEditorProps { diff --git a/src/plugins/index_pattern_field_editor/public/shared_imports.ts b/src/plugins/index_pattern_field_editor/public/shared_imports.ts index 9caa5e093a96f1..cfc36543780c18 100644 --- a/src/plugins/index_pattern_field_editor/public/shared_imports.ts +++ b/src/plugins/index_pattern_field_editor/public/shared_imports.ts @@ -6,12 +6,7 @@ * Side Public License, v 1. */ -export { - IndexPattern, - IndexPatternField, - DataPublicPluginStart, - FieldFormat, -} from '../../data/public'; +export { IndexPattern, IndexPatternField, DataPublicPluginStart } from '../../data/public'; export { UsageCollectionStart } from '../../usage_collection/public'; @@ -19,6 +14,8 @@ export { RuntimeType, RuntimeField, KBN_FIELD_TYPES, ES_FIELD_TYPES } from '../. export { createKibanaReactContext, toMountPoint, CodeEditor } from '../../kibana_react/public'; +export { FieldFormat } from '../../field_formats/common'; + export { useForm, useFormData, diff --git a/src/plugins/index_pattern_field_editor/tsconfig.json b/src/plugins/index_pattern_field_editor/tsconfig.json index 559b1aaf0fc26c..5450ae74a91a05 100644 --- a/src/plugins/index_pattern_field_editor/tsconfig.json +++ b/src/plugins/index_pattern_field_editor/tsconfig.json @@ -16,5 +16,6 @@ { "path": "../kibana_react/tsconfig.json" }, { "path": "../kibana_utils/tsconfig.json" }, { "path": "../es_ui_shared/tsconfig.json" }, + { "path": "../field_formats/tsconfig.json" } ] } diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx index c2f0c7cbfd64c6..1a06fdf4255006 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx @@ -10,7 +10,7 @@ import React, { PureComponent } from 'react'; import { shallow } from 'enzyme'; import { FieldFormatEditor } from './field_format_editor'; -import type { FieldFormat } from '../../../../../../data/public'; +import type { FieldFormat } from '../../../../../../field_formats/common'; class TestEditor extends PureComponent { render() { diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.tsx index 545a464da0eb31..3689ae70639c88 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.tsx @@ -13,7 +13,7 @@ import type { FieldFormatEditorFactory, FieldFormatEditor as InnerFieldFormatEditor, } from 'src/plugins/index_pattern_field_editor/public'; -import type { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; export interface FieldFormatEditorProps { fieldType: string; diff --git a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.test.tsx b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.test.tsx index ae3783ecfec092..18c749d448da8e 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.test.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.test.tsx @@ -6,12 +6,8 @@ * Side Public License, v 1. */ -import { - IndexPattern, - IndexPatternField, - FieldFormatInstanceType, - IndexPatternsService, -} from 'src/plugins/data/public'; +import { IndexPattern, IndexPatternField, IndexPatternsService } from 'src/plugins/data/public'; +import { FieldFormatInstanceType } from 'src/plugins/field_formats/common'; import { findTestSubject } from '@elastic/eui/lib/test'; jest.mock('brace/mode/groovy', () => ({})); diff --git a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx index b5c0186cd998ce..bdc18938f9d6cd 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx @@ -33,13 +33,13 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import type { FieldFormatInstanceType } from 'src/plugins/field_formats/common'; import { getEnabledScriptingLanguages, getDeprecatedScriptingLanguages, getSupportedScriptingLanguages, } from '../../scripting_languages'; import { - FieldFormatInstanceType, IndexPattern, IndexPatternField, KBN_FIELD_TYPES, diff --git a/src/plugins/kibana_react/public/code_editor/languages/index.ts b/src/plugins/kibana_react/public/code_editor/languages/index.ts index ff7da1725fa7f9..b797ea44d1f919 100644 --- a/src/plugins/kibana_react/public/code_editor/languages/index.ts +++ b/src/plugins/kibana_react/public/code_editor/languages/index.ts @@ -9,5 +9,6 @@ import { Lang as CssLang } from './css'; import { Lang as HandlebarsLang } from './handlebars'; import { Lang as MarkdownLang } from './markdown'; +import { Lang as YamlLang } from './yaml'; -export { CssLang, HandlebarsLang, MarkdownLang }; +export { CssLang, HandlebarsLang, MarkdownLang, YamlLang }; diff --git a/src/plugins/kibana_react/public/code_editor/languages/yaml/constants.ts b/src/plugins/kibana_react/public/code_editor/languages/yaml/constants.ts new file mode 100644 index 00000000000000..71a34088a216bb --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/yaml/constants.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const LANG = 'yaml'; diff --git a/src/plugins/kibana_react/public/code_editor/languages/yaml/index.ts b/src/plugins/kibana_react/public/code_editor/languages/yaml/index.ts new file mode 100644 index 00000000000000..f501de74debec9 --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/yaml/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { LangModuleType } from '@kbn/monaco'; +import { languageConfiguration, lexerRules } from './language'; +import { LANG } from './constants'; + +export const Lang: LangModuleType = { ID: LANG, languageConfiguration, lexerRules }; diff --git a/src/plugins/kibana_react/public/code_editor/languages/yaml/language.ts b/src/plugins/kibana_react/public/code_editor/languages/yaml/language.ts new file mode 100644 index 00000000000000..d17f284b284083 --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/yaml/language.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @kbn/eslint/module_migration */ +import { conf, language } from 'monaco-editor/esm/vs/basic-languages/yaml/yaml'; + +export { conf as languageConfiguration, language as lexerRules }; diff --git a/src/plugins/kibana_react/public/code_editor/register_languages.ts b/src/plugins/kibana_react/public/code_editor/register_languages.ts index b4a0f4d53cdf47..a32318a7e4b20f 100644 --- a/src/plugins/kibana_react/public/code_editor/register_languages.ts +++ b/src/plugins/kibana_react/public/code_editor/register_languages.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ import { registerLanguage } from '@kbn/monaco'; -import { CssLang, HandlebarsLang, MarkdownLang } from './languages'; +import { CssLang, HandlebarsLang, MarkdownLang, YamlLang } from './languages'; registerLanguage(CssLang); registerLanguage(HandlebarsLang); registerLanguage(MarkdownLang); +registerLanguage(YamlLang); diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index ace26dc9e62950..57c9893f0cfea0 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -428,10 +428,6 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, - 'apm:enableServiceOverview': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, 'observability:enableInspectEsQueries': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 95faaee88fa613..52cf38d32c2e21 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -35,7 +35,6 @@ export interface UsageStats { 'discover:maxDocFieldsDisplayed': number; 'securitySolution:rulesTableRefresh': string; 'apm:enableSignificantTerms': boolean; - 'apm:enableServiceOverview': boolean; 'observability:enableInspectEsQueries': boolean; 'visualize:enableLabs': boolean; 'visualization:heatmap:maxBuckets': number; diff --git a/src/plugins/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts index 8aeb26e8253b66..f6b99badca492c 100644 --- a/src/plugins/telemetry/common/constants.ts +++ b/src/plugins/telemetry/common/constants.ts @@ -49,3 +49,17 @@ export const PRIVACY_STATEMENT_URL = `https://www.elastic.co/legal/privacy-state * The endpoint version when hitting the remote telemetry service */ export const ENDPOINT_VERSION = 'v2'; + +/** + * The telemetry endpoints for the remote telemetry service. + */ +export const TELEMETRY_ENDPOINT = { + MAIN_CHANNEL: { + PROD: `https://telemetry.elastic.co/xpack/${ENDPOINT_VERSION}/send`, + STAGING: `https://telemetry-staging.elastic.co/xpack/${ENDPOINT_VERSION}/send`, + }, + OPT_IN_STATUS_CHANNEL: { + PROD: `https://telemetry.elastic.co/opt_in_status/${ENDPOINT_VERSION}/send`, + STAGING: `https://telemetry-staging.elastic.co/opt_in_status/${ENDPOINT_VERSION}/send`, + }, +}; diff --git a/src/plugins/telemetry/common/telemetry_config/get_telemetry_channel_endpoint.test.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_channel_endpoint.test.ts new file mode 100644 index 00000000000000..74d45f6a9f7d40 --- /dev/null +++ b/src/plugins/telemetry/common/telemetry_config/get_telemetry_channel_endpoint.test.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getTelemetryChannelEndpoint } from './get_telemetry_channel_endpoint'; +import { TELEMETRY_ENDPOINT } from '../constants'; + +describe('getTelemetryChannelEndpoint', () => { + it('throws on unknown env', () => { + expect(() => + // @ts-expect-error + getTelemetryChannelEndpoint({ env: 'ANY', channelName: 'main' }) + ).toThrowErrorMatchingInlineSnapshot(`"Unknown telemetry endpoint env ANY."`); + }); + + it('throws on unknown channelName', () => { + expect(() => + // @ts-expect-error + getTelemetryChannelEndpoint({ env: 'prod', channelName: 'ANY' }) + ).toThrowErrorMatchingInlineSnapshot(`"Unknown telemetry channel ANY."`); + }); + + describe('main channel', () => { + it('returns correct prod endpoint', () => { + const endpoint = getTelemetryChannelEndpoint({ env: 'prod', channelName: 'main' }); + expect(endpoint).toBe(TELEMETRY_ENDPOINT.MAIN_CHANNEL.PROD); + }); + it('returns correct staging endpoint', () => { + const endpoint = getTelemetryChannelEndpoint({ env: 'staging', channelName: 'main' }); + expect(endpoint).toBe(TELEMETRY_ENDPOINT.MAIN_CHANNEL.STAGING); + }); + }); + + describe('optInStatus channel', () => { + it('returns correct prod endpoint', () => { + const endpoint = getTelemetryChannelEndpoint({ env: 'prod', channelName: 'optInStatus' }); + expect(endpoint).toBe(TELEMETRY_ENDPOINT.OPT_IN_STATUS_CHANNEL.PROD); + }); + it('returns correct staging endpoint', () => { + const endpoint = getTelemetryChannelEndpoint({ env: 'staging', channelName: 'optInStatus' }); + expect(endpoint).toBe(TELEMETRY_ENDPOINT.OPT_IN_STATUS_CHANNEL.STAGING); + }); + }); +}); diff --git a/src/plugins/telemetry/common/telemetry_config/get_telemetry_channel_endpoint.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_channel_endpoint.ts new file mode 100644 index 00000000000000..a0af7878afef64 --- /dev/null +++ b/src/plugins/telemetry/common/telemetry_config/get_telemetry_channel_endpoint.ts @@ -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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { TELEMETRY_ENDPOINT } from '../constants'; + +export interface GetTelemetryChannelEndpointConfig { + channelName: 'main' | 'optInStatus'; + env: 'staging' | 'prod'; +} + +export function getTelemetryChannelEndpoint({ + channelName, + env, +}: GetTelemetryChannelEndpointConfig): string { + if (env !== 'staging' && env !== 'prod') { + throw new Error(`Unknown telemetry endpoint env ${env}.`); + } + + const endpointEnv = env === 'staging' ? 'STAGING' : 'PROD'; + + switch (channelName) { + case 'main': + return TELEMETRY_ENDPOINT.MAIN_CHANNEL[endpointEnv]; + case 'optInStatus': + return TELEMETRY_ENDPOINT.OPT_IN_STATUS_CHANNEL[endpointEnv]; + default: + throw new Error(`Unknown telemetry channel ${channelName}.`); + } +} diff --git a/src/plugins/telemetry/common/telemetry_config/index.ts b/src/plugins/telemetry/common/telemetry_config/index.ts index cc4ff102742d7f..eb268639cad919 100644 --- a/src/plugins/telemetry/common/telemetry_config/index.ts +++ b/src/plugins/telemetry/common/telemetry_config/index.ts @@ -11,3 +11,5 @@ export { getTelemetrySendUsageFrom } from './get_telemetry_send_usage_from'; export { getTelemetryAllowChangingOptInStatus } from './get_telemetry_allow_changing_opt_in_status'; export { getTelemetryFailureDetails } from './get_telemetry_failure_details'; export type { TelemetryFailureDetails } from './get_telemetry_failure_details'; +export { getTelemetryChannelEndpoint } from './get_telemetry_channel_endpoint'; +export type { GetTelemetryChannelEndpointConfig } from './get_telemetry_channel_endpoint'; diff --git a/src/plugins/telemetry/public/mocks.ts b/src/plugins/telemetry/public/mocks.ts index 418aeace77c704..405c6620e802aa 100644 --- a/src/plugins/telemetry/public/mocks.ts +++ b/src/plugins/telemetry/public/mocks.ts @@ -34,8 +34,7 @@ export function mockTelemetryService({ }: TelemetryServiceMockOptions = {}) { const config = { enabled: true, - url: 'http://localhost', - optInStatusUrl: 'http://localhost', + sendUsageTo: 'staging' as const, sendUsageFrom: 'browser' as const, optIn: true, banner: true, diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts index 04fabe3d62b17c..73dc07d7a4fb93 100644 --- a/src/plugins/telemetry/public/plugin.ts +++ b/src/plugins/telemetry/public/plugin.ts @@ -90,16 +90,14 @@ interface TelemetryPluginSetupDependencies { export interface TelemetryPluginConfig { /** Is the plugin enabled? **/ enabled: boolean; - /** Remote telemetry service's URL **/ - url: string; /** The banner is expected to be shown when needed **/ banner: boolean; /** Does the cluster allow changing the opt-in/out status via the UI? **/ allowChangingOptInStatus: boolean; /** Is the cluster opted-in? **/ optIn: boolean | null; - /** Opt-in/out notification URL **/ - optInStatusUrl: string; + /** Specify if telemetry should send usage to the prod or staging remote telemetry service **/ + sendUsageTo: 'prod' | 'staging'; /** Should the telemetry payloads be sent from the server or the browser? **/ sendUsageFrom: 'browser' | 'server'; /** Should notify the user about the opt-in status? **/ diff --git a/src/plugins/telemetry/public/services/telemetry_service.test.ts b/src/plugins/telemetry/public/services/telemetry_service.test.ts index 94630c0cb8d80e..b23ba127c1522c 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.test.ts @@ -10,7 +10,7 @@ /* eslint-disable dot-notation */ import { mockTelemetryService } from '../mocks'; - +import { TELEMETRY_ENDPOINT } from '../../common/constants'; describe('TelemetryService', () => { describe('fetchTelemetry', () => { it('calls expected URL with 20 minutes - now', async () => { @@ -137,13 +137,42 @@ describe('TelemetryService', () => { }); describe('getTelemetryUrl', () => { - it('should return the config.url parameter', async () => { - const url = 'http://test.com'; + it('should return staging endpoint when sendUsageTo is set to staging', async () => { + const telemetryService = mockTelemetryService({ + config: { sendUsageTo: 'staging' }, + }); + + expect(telemetryService.getTelemetryUrl()).toBe(TELEMETRY_ENDPOINT.MAIN_CHANNEL.STAGING); + }); + + it('should return prod endpoint when sendUsageTo is set to prod', async () => { + const telemetryService = mockTelemetryService({ + config: { sendUsageTo: 'prod' }, + }); + + expect(telemetryService.getTelemetryUrl()).toBe(TELEMETRY_ENDPOINT.MAIN_CHANNEL.PROD); + }); + }); + + describe('getOptInStatusUrl', () => { + it('should return staging endpoint when sendUsageTo is set to staging', async () => { + const telemetryService = mockTelemetryService({ + config: { sendUsageTo: 'staging' }, + }); + + expect(telemetryService.getOptInStatusUrl()).toBe( + TELEMETRY_ENDPOINT.OPT_IN_STATUS_CHANNEL.STAGING + ); + }); + + it('should return prod endpoint when sendUsageTo is set to prod', async () => { const telemetryService = mockTelemetryService({ - config: { url }, + config: { sendUsageTo: 'prod' }, }); - expect(telemetryService.getTelemetryUrl()).toBe(url); + expect(telemetryService.getOptInStatusUrl()).toBe( + TELEMETRY_ENDPOINT.OPT_IN_STATUS_CHANNEL.PROD + ); }); }); diff --git a/src/plugins/telemetry/public/services/telemetry_service.ts b/src/plugins/telemetry/public/services/telemetry_service.ts index 515d2039a4a114..4e52ec3a7e6edf 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { CoreStart } from 'kibana/public'; import { TelemetryPluginConfig } from '../plugin'; +import { getTelemetryChannelEndpoint } from '../../common/telemetry_config'; interface TelemetryServiceConstructor { config: TelemetryPluginConfig; @@ -93,14 +94,14 @@ export class TelemetryService { /** Retrieve the opt-in/out notification URL **/ public getOptInStatusUrl = () => { - const telemetryOptInStatusUrl = this.config.optInStatusUrl; - return telemetryOptInStatusUrl; + const { sendUsageTo } = this.config; + return getTelemetryChannelEndpoint({ channelName: 'optInStatus', env: sendUsageTo }); }; /** Retrieve the URL to report telemetry **/ public getTelemetryUrl = () => { - const telemetryUrl = this.config.url; - return telemetryUrl; + const { sendUsageTo } = this.config; + return getTelemetryChannelEndpoint({ channelName: 'main', env: sendUsageTo }); }; /** diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 6947b6cc3ce384..3fceb7913eae64 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7783,12 +7783,6 @@ "description": "Non-default value of setting." } }, - "apm:enableServiceOverview": { - "type": "boolean", - "_meta": { - "description": "Non-default value of setting." - } - }, "observability:enableInspectEsQueries": { "type": "boolean", "_meta": { @@ -9336,4 +9330,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/plugins/telemetry/server/config.ts b/src/plugins/telemetry/server/config/config.ts similarity index 50% rename from src/plugins/telemetry/server/config.ts rename to src/plugins/telemetry/server/config/config.ts index 8123ae4c66e3c5..8d75f0aba1726c 100644 --- a/src/plugins/telemetry/server/config.ts +++ b/src/plugins/telemetry/server/config/config.ts @@ -6,11 +6,18 @@ * Side Public License, v 1. */ -import { schema, TypeOf } from '@kbn/config-schema'; +import { schema, TypeOf, Type } from '@kbn/config-schema'; import { getConfigPath } from '@kbn/utils'; -import { ENDPOINT_VERSION } from '../common/constants'; +import { PluginConfigDescriptor } from 'kibana/server'; +import { TELEMETRY_ENDPOINT } from '../../common/constants'; +import { deprecateEndpointConfigs } from './deprecations'; -export const configSchema = schema.object({ +const clusterEnvSchema: [Type<'prod'>, Type<'staging'>] = [ + schema.literal('prod'), + schema.literal('staging'), +]; + +const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), allowChangingOptInStatus: schema.boolean({ defaultValue: true }), optIn: schema.conditional( @@ -23,24 +30,38 @@ export const configSchema = schema.object({ // `config` is used internally and not intended to be set config: schema.string({ defaultValue: getConfigPath() }), banner: schema.boolean({ defaultValue: true }), + sendUsageTo: schema.conditional( + schema.contextRef('dist'), + schema.literal(false), // Point to staging if it's not a distributable release + schema.oneOf(clusterEnvSchema, { defaultValue: 'staging' }), + schema.oneOf(clusterEnvSchema, { defaultValue: 'prod' }) + ), + /** + * REMOVE IN 8.0 - INTERNAL CONFIG DEPRECATED IN 7.15 + * REPLACED WITH `telemetry.sendUsageTo: staging | prod` + */ url: schema.conditional( schema.contextRef('dist'), schema.literal(false), // Point to staging if it's not a distributable release schema.string({ - defaultValue: `https://telemetry-staging.elastic.co/xpack/${ENDPOINT_VERSION}/send`, + defaultValue: TELEMETRY_ENDPOINT.MAIN_CHANNEL.STAGING, }), schema.string({ - defaultValue: `https://telemetry.elastic.co/xpack/${ENDPOINT_VERSION}/send`, + defaultValue: TELEMETRY_ENDPOINT.MAIN_CHANNEL.PROD, }) ), + /** + * REMOVE IN 8.0 - INTERNAL CONFIG DEPRECATED IN 7.15 + * REPLACED WITH `telemetry.sendUsageTo: staging | prod` + */ optInStatusUrl: schema.conditional( schema.contextRef('dist'), schema.literal(false), // Point to staging if it's not a distributable release schema.string({ - defaultValue: `https://telemetry-staging.elastic.co/opt_in_status/${ENDPOINT_VERSION}/send`, + defaultValue: TELEMETRY_ENDPOINT.OPT_IN_STATUS_CHANNEL.STAGING, }), schema.string({ - defaultValue: `https://telemetry.elastic.co/opt_in_status/${ENDPOINT_VERSION}/send`, + defaultValue: TELEMETRY_ENDPOINT.OPT_IN_STATUS_CHANNEL.PROD, }) ), sendUsageFrom: schema.oneOf([schema.literal('server'), schema.literal('browser')], { @@ -49,3 +70,16 @@ export const configSchema = schema.object({ }); export type TelemetryConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, + exposeToBrowser: { + enabled: true, + banner: true, + allowChangingOptInStatus: true, + optIn: true, + sendUsageFrom: true, + sendUsageTo: true, + }, + deprecations: () => [deprecateEndpointConfigs], +}; diff --git a/src/plugins/telemetry/server/config/deprecations.test.ts b/src/plugins/telemetry/server/config/deprecations.test.ts new file mode 100644 index 00000000000000..0823b521b23a7f --- /dev/null +++ b/src/plugins/telemetry/server/config/deprecations.test.ts @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { deprecateEndpointConfigs } from './deprecations'; +import type { TelemetryConfigType } from './config'; +import { TELEMETRY_ENDPOINT } from '../../common/constants'; +describe('deprecateEndpointConfigs', () => { + const fromPath = 'telemetry'; + const mockAddDeprecation = jest.fn(); + beforeEach(() => { + jest.clearAllMocks(); + }); + + function createMockRawConfig(telemetryConfig?: Partial) { + return { + elasticsearch: { username: 'kibana_system', password: 'changeme' }, + plugins: { paths: [] }, + server: { port: 5603, basePath: '/hln', rewriteBasePath: true }, + logging: { json: false }, + ...(telemetryConfig ? { telemetry: telemetryConfig } : {}), + }; + } + + it('returns void if telemetry.* config is not set', () => { + const rawConfig = createMockRawConfig(); + const result = deprecateEndpointConfigs(rawConfig, fromPath, mockAddDeprecation); + expect(result).toBe(undefined); + }); + + it('sets "telemetryConfig.sendUsageTo: staging" if "telemetry.url" uses the staging endpoint', () => { + const rawConfig = createMockRawConfig({ + url: TELEMETRY_ENDPOINT.MAIN_CHANNEL.STAGING, + }); + const result = deprecateEndpointConfigs(rawConfig, fromPath, mockAddDeprecation); + expect(result).toMatchInlineSnapshot(` + Object { + "set": Array [ + Object { + "path": "telemetry.sendUsageTo", + "value": "staging", + }, + ], + "unset": Array [ + Object { + "path": "telemetry.url", + }, + ], + } + `); + }); + + it('sets "telemetryConfig.sendUsageTo: prod" if "telemetry.url" uses the non-staging endpoint', () => { + const rawConfig = createMockRawConfig({ + url: 'random-endpoint', + }); + const result = deprecateEndpointConfigs(rawConfig, fromPath, mockAddDeprecation); + expect(result).toMatchInlineSnapshot(` + Object { + "set": Array [ + Object { + "path": "telemetry.sendUsageTo", + "value": "prod", + }, + ], + "unset": Array [ + Object { + "path": "telemetry.url", + }, + ], + } + `); + }); + + it('sets "telemetryConfig.sendUsageTo: staging" if "telemetry.optInStatusUrl" uses the staging endpoint', () => { + const rawConfig = createMockRawConfig({ + optInStatusUrl: TELEMETRY_ENDPOINT.MAIN_CHANNEL.STAGING, + }); + const result = deprecateEndpointConfigs(rawConfig, fromPath, mockAddDeprecation); + expect(result).toMatchInlineSnapshot(` + Object { + "set": Array [ + Object { + "path": "telemetry.sendUsageTo", + "value": "staging", + }, + ], + "unset": Array [ + Object { + "path": "telemetry.optInStatusUrl", + }, + ], + } + `); + }); + + it('sets "telemetryConfig.sendUsageTo: prod" if "telemetry.optInStatusUrl" uses the non-staging endpoint', () => { + const rawConfig = createMockRawConfig({ + optInStatusUrl: 'random-endpoint', + }); + const result = deprecateEndpointConfigs(rawConfig, fromPath, mockAddDeprecation); + expect(result).toMatchInlineSnapshot(` + Object { + "set": Array [ + Object { + "path": "telemetry.sendUsageTo", + "value": "prod", + }, + ], + "unset": Array [ + Object { + "path": "telemetry.optInStatusUrl", + }, + ], + } + `); + }); + + it('registers deprecation when "telemetry.url" is set', () => { + const rawConfig = createMockRawConfig({ + url: TELEMETRY_ENDPOINT.MAIN_CHANNEL.PROD, + }); + deprecateEndpointConfigs(rawConfig, fromPath, mockAddDeprecation); + expect(mockAddDeprecation).toBeCalledTimes(1); + expect(mockAddDeprecation.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "manualSteps": Array [ + "Remove \\"telemetry.url\\" from the Kibana configuration.", + "To send usage to the staging endpoint add \\"telemetry.sendUsageTo: staging\\" to the Kibana configuration.", + ], + }, + "message": "\\"telemetry.url\\" has been deprecated. Set \\"telemetry.sendUsageTo: staging\\" to the Kibana configurations to send usage to the staging endpoint.", + }, + ] + `); + }); + + it('registers deprecation when "telemetry.optInStatusUrl" is set', () => { + const rawConfig = createMockRawConfig({ + optInStatusUrl: 'random-endpoint', + }); + deprecateEndpointConfigs(rawConfig, fromPath, mockAddDeprecation); + expect(mockAddDeprecation).toBeCalledTimes(1); + expect(mockAddDeprecation.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "manualSteps": Array [ + "Remove \\"telemetry.optInStatusUrl\\" from the Kibana configuration.", + "To send usage to the staging endpoint add \\"telemetry.sendUsageTo: staging\\" to the Kibana configuration.", + ], + }, + "message": "\\"telemetry.optInStatusUrl\\" has been deprecated. Set \\"telemetry.sendUsageTo: staging\\" to the Kibana configurations to send usage to the staging endpoint.", + }, + ] + `); + }); +}); diff --git a/src/plugins/telemetry/server/config/deprecations.ts b/src/plugins/telemetry/server/config/deprecations.ts new file mode 100644 index 00000000000000..5507c3e1f6675b --- /dev/null +++ b/src/plugins/telemetry/server/config/deprecations.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import type { ConfigDeprecation } from 'kibana/server'; +import type { TelemetryConfigType } from './config'; + +export const deprecateEndpointConfigs: ConfigDeprecation = ( + rawConfig, + fromPath, + addDeprecation +) => { + const telemetryConfig: TelemetryConfigType = rawConfig[fromPath]; + if (!telemetryConfig) { + return; + } + + const unset: Array<{ path: string }> = []; + const endpointConfigPaths = ['url', 'optInStatusUrl'] as const; + let useStaging = telemetryConfig.sendUsageTo === 'staging' ? true : false; + + for (const configPath of endpointConfigPaths) { + const configValue = telemetryConfig[configPath]; + const fullConfigPath = `telemetry.${configPath}`; + if (typeof configValue !== 'undefined') { + unset.push({ path: fullConfigPath }); + + if (/telemetry-staging\.elastic\.co/i.test(configValue)) { + useStaging = true; + } + + addDeprecation({ + message: i18n.translate('telemetry.endpointConfigs.deprecationMessage', { + defaultMessage: + '"{configPath}" has been deprecated. Set "telemetry.sendUsageTo: staging" to the Kibana configurations to send usage to the staging endpoint.', + values: { configPath: fullConfigPath }, + }), + correctiveActions: { + manualSteps: [ + i18n.translate('telemetry.endpointConfigs.deprecationManualStep1', { + defaultMessage: 'Remove "{configPath}" from the Kibana configuration.', + values: { configPath: fullConfigPath }, + }), + i18n.translate('telemetry.endpointConfigs.deprecationManualStep2', { + defaultMessage: + 'To send usage to the staging endpoint add "telemetry.sendUsageTo: staging" to the Kibana configuration.', + }), + ], + }, + }); + } + } + + return { + set: [{ path: 'telemetry.sendUsageTo', value: useStaging ? 'staging' : 'prod' }], + unset, + }; +}; diff --git a/src/plugins/data/server/field_formats/index.ts b/src/plugins/telemetry/server/config/index.ts similarity index 77% rename from src/plugins/data/server/field_formats/index.ts rename to src/plugins/telemetry/server/config/index.ts index a8e6d28f2c6427..19ccd73e17fcde 100644 --- a/src/plugins/data/server/field_formats/index.ts +++ b/src/plugins/telemetry/server/config/index.ts @@ -6,4 +6,5 @@ * Side Public License, v 1. */ -export { FieldFormatsService, FieldFormatsSetup, FieldFormatsStart } from './field_formats_service'; +export { config } from './config'; +export type { TelemetryConfigType } from './config'; diff --git a/src/plugins/telemetry/server/fetcher.ts b/src/plugins/telemetry/server/fetcher.ts index fb188a2414b989..e15b5be2604ec4 100644 --- a/src/plugins/telemetry/server/fetcher.ts +++ b/src/plugins/telemetry/server/fetcher.ts @@ -19,6 +19,7 @@ import { ICustomClusterClient, } from '../../../core/server'; import { + getTelemetryChannelEndpoint, getTelemetryOptIn, getTelemetrySendUsageFrom, getTelemetryFailureDetails, @@ -139,7 +140,10 @@ export class FetcherTask { const configTelemetrySendUsageFrom = config.sendUsageFrom; const allowChangingOptInStatus = config.allowChangingOptInStatus; const configTelemetryOptIn = typeof config.optIn === 'undefined' ? null : config.optIn; - const telemetryUrl = config.url; + const telemetryUrl = getTelemetryChannelEndpoint({ + channelName: 'main', + env: config.sendUsageTo, + }); const { failureCount, failureVersion } = getTelemetryFailureDetails({ telemetrySavedObject, }); @@ -208,17 +212,17 @@ export class FetcherTask { }); } - private async sendTelemetry(url: string, cluster: string): Promise { + private async sendTelemetry(telemetryUrl: string, cluster: string): Promise { this.logger.debug(`Sending usage stats.`); /** * send OPTIONS before sending usage data. * OPTIONS is less intrusive as it does not contain any payload and is used here to check if the endpoint is reachable. */ - await fetch(url, { + await fetch(telemetryUrl, { method: 'options', }); - await fetch(url, { + await fetch(telemetryUrl, { method: 'post', body: cluster, headers: { 'X-Elastic-Stack-Version': this.currentKibanaVersion }, diff --git a/src/plugins/telemetry/server/index.ts b/src/plugins/telemetry/server/index.ts index 530f7c499c3f20..6b56996c756aa7 100644 --- a/src/plugins/telemetry/server/index.ts +++ b/src/plugins/telemetry/server/index.ts @@ -6,25 +6,13 @@ * Side Public License, v 1. */ -import { PluginInitializerContext, PluginConfigDescriptor } from 'kibana/server'; +import type { PluginInitializerContext } from 'kibana/server'; +import type { TelemetryConfigType } from './config'; import { TelemetryPlugin } from './plugin'; -import { configSchema, TelemetryConfigType } from './config'; +export { config } from './config'; export type { TelemetryPluginSetup, TelemetryPluginStart } from './plugin'; -export const config: PluginConfigDescriptor = { - schema: configSchema, - exposeToBrowser: { - enabled: true, - url: true, - banner: true, - allowChangingOptInStatus: true, - optIn: true, - optInStatusUrl: true, - sendUsageFrom: true, - }, -}; - export const plugin = (initializerContext: PluginInitializerContext) => new TelemetryPlugin(initializerContext); export { getClusterUuids, getLocalStats } from './telemetry_collection'; diff --git a/src/plugins/telemetry/server/plugin.ts b/src/plugins/telemetry/server/plugin.ts index 40714bf4cf2be2..d38f054a4402e0 100644 --- a/src/plugins/telemetry/server/plugin.ts +++ b/src/plugins/telemetry/server/plugin.ts @@ -30,11 +30,11 @@ import { registerTelemetryUsageCollector, registerTelemetryPluginUsageCollector, } from './collectors'; -import { TelemetryConfigType } from './config'; +import type { TelemetryConfigType } from './config'; import { FetcherTask } from './fetcher'; import { handleOldSettings } from './handle_old_settings'; import { getTelemetrySavedObject } from './telemetry_repository'; -import { getTelemetryOptIn } from '../common/telemetry_config'; +import { getTelemetryOptIn, getTelemetryChannelEndpoint } from '../common/telemetry_config'; interface TelemetryPluginsDepsSetup { usageCollection: UsageCollectionSetup; @@ -117,8 +117,10 @@ export class TelemetryPlugin implements Plugin { - const config = await config$.pipe(take(1)).toPromise(); - return new URL(config.url); + const { sendUsageTo } = await config$.pipe(take(1)).toPromise(); + const telemetryUrl = getTelemetryChannelEndpoint({ env: sendUsageTo, channelName: 'main' }); + + return new URL(telemetryUrl); }, }; } diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in.ts index 196631f33384dd..e3fad0642303c2 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in.ts @@ -83,15 +83,15 @@ export function registerTelemetryOptInRoutes({ ); if (config.sendUsageFrom === 'server') { - const optInStatusUrl = config.optInStatusUrl; + const { sendUsageTo } = config; sendTelemetryOptInStatus( telemetryCollectionManager, - { optInStatusUrl, newOptInStatus, currentKibanaVersion }, + { sendUsageTo, newOptInStatus, currentKibanaVersion }, statsGetterConfig ).catch((err) => { // The server is likely behind a firewall and can't reach the remote service logger.warn( - `Failed to notify "${optInStatusUrl}" from the server about the opt-in selection. Possibly blocked by a firewall? - Error: ${err.message}` + `Failed to notify the telemetry endpoint about the opt-in selection. Possibly blocked by a firewall? - Error: ${err.message}` ); }); } diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.test.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.test.ts index 6660a36e76199c..acc9a863af61b4 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.test.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.test.ts @@ -10,30 +10,53 @@ jest.mock('node-fetch'); import fetch from 'node-fetch'; import { sendTelemetryOptInStatus } from './telemetry_opt_in_stats'; import { StatsGetterConfig } from 'src/plugins/telemetry_collection_manager/server'; - +import { TELEMETRY_ENDPOINT } from '../../common/constants'; describe('sendTelemetryOptInStatus', () => { + const mockStatsGetterConfig = { unencrypted: false } as StatsGetterConfig; + const mockTelemetryCollectionManager = { + getOptInStats: jest.fn().mockResolvedValue(['mock_opt_in_hashed_value']), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + it('calls fetch with the opt in status returned from the telemetryCollectionManager', async () => { - const mockOptInStatus = ['mock_opt_in_hashed_value']; - const mockTelemetryCollectionManager = { - getOptInStats: jest.fn().mockResolvedValue(mockOptInStatus), - }; const mockConfig = { - optInStatusUrl: 'some_url', + sendUsageTo: 'prod' as const, newOptInStatus: true, currentKibanaVersion: 'mock_kibana_version', }; - const mockStatsGetterConfig = { - unencrypted: false, - }; const result = await sendTelemetryOptInStatus( mockTelemetryCollectionManager, mockConfig, - mockStatsGetterConfig as StatsGetterConfig + mockStatsGetterConfig ); expect(result).toBeUndefined(); expect(fetch).toBeCalledTimes(1); - expect(fetch).toBeCalledWith(mockConfig.optInStatusUrl, { + expect(fetch).toBeCalledWith(TELEMETRY_ENDPOINT.OPT_IN_STATUS_CHANNEL.PROD, { + method: 'post', + body: '["mock_opt_in_hashed_value"]', + headers: { 'X-Elastic-Stack-Version': mockConfig.currentKibanaVersion }, + }); + }); + + it('sends to staging endpoint on "sendUsageTo: staging"', async () => { + const mockConfig = { + sendUsageTo: 'staging' as const, + newOptInStatus: true, + currentKibanaVersion: 'mock_kibana_version', + }; + + await sendTelemetryOptInStatus( + mockTelemetryCollectionManager, + mockConfig, + mockStatsGetterConfig + ); + + expect(fetch).toBeCalledTimes(1); + expect(fetch).toBeCalledWith(TELEMETRY_ENDPOINT.OPT_IN_STATUS_CHANNEL.STAGING, { method: 'post', body: '["mock_opt_in_hashed_value"]', headers: { 'X-Elastic-Stack-Version': mockConfig.currentKibanaVersion }, diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts index 77ec981d4c35bf..f6b7eddcbe765c 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts @@ -14,9 +14,10 @@ import { TelemetryCollectionManagerPluginSetup, StatsGetterConfig, } from 'src/plugins/telemetry_collection_manager/server'; +import { getTelemetryChannelEndpoint } from '../../common/telemetry_config'; interface SendTelemetryOptInStatusConfig { - optInStatusUrl: string; + sendUsageTo: 'staging' | 'prod'; newOptInStatus: boolean; currentKibanaVersion: string; } @@ -26,7 +27,12 @@ export async function sendTelemetryOptInStatus( config: SendTelemetryOptInStatusConfig, statsGetterConfig: StatsGetterConfig ) { - const { optInStatusUrl, newOptInStatus, currentKibanaVersion } = config; + const { sendUsageTo, newOptInStatus, currentKibanaVersion } = config; + const optInStatusUrl = getTelemetryChannelEndpoint({ + env: sendUsageTo, + channelName: 'optInStatus', + }); + const optInStatus = await telemetryCollectionManager.getOptInStats( newOptInStatus, statsGetterConfig diff --git a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap index 014142a2a3d068..7c9154cba4f885 100644 --- a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap +++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap @@ -311,9 +311,8 @@ exports[`TelemetryManagementSectionComponent renders null because allowChangingO "banner": true, "enabled": true, "optIn": true, - "optInStatusUrl": "", "sendUsageFrom": "browser", - "url": "", + "sendUsageTo": "staging", }, "fetchExample": [Function], "fetchTelemetry": [Function], diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx index fe6f8e254142b5..b8332317e6b682 100644 --- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx +++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx @@ -24,12 +24,11 @@ describe('TelemetryManagementSectionComponent', () => { const isSecurityExampleEnabled = jest.fn().mockReturnValue(true); const telemetryService = new TelemetryService({ config: { + sendUsageTo: 'staging', enabled: true, - url: '', banner: true, allowChangingOptInStatus: true, optIn: true, - optInStatusUrl: '', sendUsageFrom: 'browser', }, isScreenshotMode: false, @@ -60,12 +59,11 @@ describe('TelemetryManagementSectionComponent', () => { const telemetryService = new TelemetryService({ config: { enabled: true, - url: '', banner: true, allowChangingOptInStatus: true, optIn: false, - optInStatusUrl: '', sendUsageFrom: 'browser', + sendUsageTo: 'staging', }, isScreenshotMode: false, reportOptInStatusChange: false, @@ -116,11 +114,10 @@ describe('TelemetryManagementSectionComponent', () => { const telemetryService = new TelemetryService({ config: { enabled: true, - url: '', banner: true, allowChangingOptInStatus: true, optIn: false, - optInStatusUrl: '', + sendUsageTo: 'staging', sendUsageFrom: 'browser', }, isScreenshotMode: false, @@ -165,11 +162,10 @@ describe('TelemetryManagementSectionComponent', () => { const telemetryService = new TelemetryService({ config: { enabled: true, - url: '', banner: true, allowChangingOptInStatus: false, optIn: true, - optInStatusUrl: '', + sendUsageTo: 'staging', sendUsageFrom: 'browser', }, isScreenshotMode: false, @@ -205,11 +201,10 @@ describe('TelemetryManagementSectionComponent', () => { const telemetryService = new TelemetryService({ config: { enabled: true, - url: '', banner: true, allowChangingOptInStatus: true, optIn: false, - optInStatusUrl: '', + sendUsageTo: 'staging', sendUsageFrom: 'browser', }, isScreenshotMode: false, @@ -246,11 +241,10 @@ describe('TelemetryManagementSectionComponent', () => { const telemetryService = new TelemetryService({ config: { enabled: true, - url: '', banner: true, allowChangingOptInStatus: true, optIn: false, - optInStatusUrl: '', + sendUsageTo: 'staging', sendUsageFrom: 'browser', }, isScreenshotMode: false, @@ -287,11 +281,10 @@ describe('TelemetryManagementSectionComponent', () => { const telemetryService = new TelemetryService({ config: { enabled: true, - url: '', banner: true, allowChangingOptInStatus: true, optIn: false, - optInStatusUrl: '', + sendUsageTo: 'staging', sendUsageFrom: 'browser', }, isScreenshotMode: false, @@ -328,11 +321,10 @@ describe('TelemetryManagementSectionComponent', () => { const telemetryService = new TelemetryService({ config: { enabled: true, - url: '', banner: true, allowChangingOptInStatus: true, optIn: false, - optInStatusUrl: '', + sendUsageTo: 'staging', sendUsageFrom: 'browser', }, isScreenshotMode: false, @@ -379,11 +371,10 @@ describe('TelemetryManagementSectionComponent', () => { const telemetryService = new TelemetryService({ config: { enabled: true, - url: '', banner: true, allowChangingOptInStatus: false, optIn: false, - optInStatusUrl: '', + sendUsageTo: 'staging', sendUsageFrom: 'browser', }, isScreenshotMode: false, diff --git a/src/plugins/telemetry_management_section/public/plugin.tsx b/src/plugins/telemetry_management_section/public/plugin.tsx index 24583260329a69..2e3b1beb64d3c5 100644 --- a/src/plugins/telemetry_management_section/public/plugin.tsx +++ b/src/plugins/telemetry_management_section/public/plugin.tsx @@ -17,17 +17,6 @@ import { TelemetryManagementSectionWrapperProps, } from './components/telemetry_management_section_wrapper'; -export interface TelemetryPluginConfig { - enabled: boolean; - url: string; - banner: boolean; - allowChangingOptInStatus: boolean; - optIn: boolean | null; - optInStatusUrl: string; - sendUsageFrom: 'browser' | 'server'; - telemetryNotifyUserAboutOptInDefault?: boolean; -} - export interface TelemetryManagementSectionPluginDepsSetup { telemetry: TelemetryPluginSetup; advancedSettings: AdvancedSettingsSetup; diff --git a/src/plugins/vis_default_editor/kibana.json b/src/plugins/vis_default_editor/kibana.json index c89e5ec1abaffc..93a5afed6a92e8 100644 --- a/src/plugins/vis_default_editor/kibana.json +++ b/src/plugins/vis_default_editor/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "ui": true, "optionalPlugins": ["visualize"], - "requiredBundles": ["kibanaUtils", "kibanaReact", "data"], + "requiredBundles": ["kibanaUtils", "kibanaReact", "data", "fieldFormats"], "owner": { "name": "Kibana App", "githubTeam": "kibana-app" diff --git a/src/plugins/vis_default_editor/public/components/options/percentage_mode.tsx b/src/plugins/vis_default_editor/public/components/options/percentage_mode.tsx index 542055d185ec5d..74f254c6176bc0 100644 --- a/src/plugins/vis_default_editor/public/components/options/percentage_mode.tsx +++ b/src/plugins/vis_default_editor/public/components/options/percentage_mode.tsx @@ -12,7 +12,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui'; import { SwitchOption } from './switch'; import { useKibana } from '../../../../kibana_react/public'; -import { UI_SETTINGS } from '../../../../data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../field_formats/common'; export interface PercentageModeOptionProps { setValue: ( @@ -31,7 +31,9 @@ function PercentageModeOption({ formatPattern, }: PercentageModeOptionProps) { const { services } = useKibana(); - const defaultPattern = services.uiSettings?.get(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN); + const defaultPattern = services.uiSettings?.get( + FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN + ); return ( <> diff --git a/src/plugins/vis_default_editor/tsconfig.json b/src/plugins/vis_default_editor/tsconfig.json index 27bb775c2d0e8a..34003bced5ad03 100644 --- a/src/plugins/vis_default_editor/tsconfig.json +++ b/src/plugins/vis_default_editor/tsconfig.json @@ -16,5 +16,6 @@ { "path": "../visualize/tsconfig.json" }, { "path": "../kibana_utils/tsconfig.json" }, { "path": "../kibana_react/tsconfig.json" }, + { "path": "../field_formats/tsconfig.json" } ] } diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx index 66be8febc496f3..87ca902f6c0906 100644 --- a/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx +++ b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx @@ -11,7 +11,7 @@ import React, { Component } from 'react'; import { isColorDark } from '@elastic/eui'; import { MetricVisValue } from './metric_vis_value'; import { Input } from '../metric_vis_fn'; -import { FieldFormatsContentType, IFieldFormat } from '../../../data/public'; +import type { FieldFormatsContentType, IFieldFormat } from '../../../field_formats/common'; import { Datatable } from '../../../expressions/public'; import { getHeatmapColors } from '../../../charts/public'; import { VisParams, MetricVisMetric } from '../types'; diff --git a/src/plugins/vis_type_metric/tsconfig.json b/src/plugins/vis_type_metric/tsconfig.json index 7441848d5a4308..bee666a5906cd4 100644 --- a/src/plugins/vis_type_metric/tsconfig.json +++ b/src/plugins/vis_type_metric/tsconfig.json @@ -15,6 +15,7 @@ { "path": "../charts/tsconfig.json" }, { "path": "../expressions/tsconfig.json" }, { "path": "../kibana_utils/tsconfig.json" }, - { "path": "../vis_default_editor/tsconfig.json" } + { "path": "../vis_default_editor/tsconfig.json" }, + { "path": "../field_formats/tsconfig.json" } ] } diff --git a/src/plugins/vis_type_pie/public/pie_component.tsx b/src/plugins/vis_type_pie/public/pie_component.tsx index b79eed2087a168..c0f4a8a6112f8d 100644 --- a/src/plugins/vis_type_pie/public/pie_component.tsx +++ b/src/plugins/vis_type_pie/public/pie_component.tsx @@ -26,7 +26,8 @@ import { ChartsPluginSetup, PaletteRegistry, } from '../../charts/public'; -import { DataPublicPluginStart, FieldFormat } from '../../data/public'; +import { DataPublicPluginStart } from '../../data/public'; +import type { FieldFormat } from '../../field_formats/common'; import type { PersistedState } from '../../visualizations/public'; import { Datatable, DatatableColumn, IInterpreterRenderHandlers } from '../../expressions/public'; import { DEFAULT_PERCENT_DECIMALS } from '../common'; diff --git a/src/plugins/vis_type_pie/public/utils/filter_helpers.ts b/src/plugins/vis_type_pie/public/utils/filter_helpers.ts index 251ff8acc698e9..f1a4791821c126 100644 --- a/src/plugins/vis_type_pie/public/utils/filter_helpers.ts +++ b/src/plugins/vis_type_pie/public/utils/filter_helpers.ts @@ -8,7 +8,8 @@ import { LayerValue, SeriesIdentifier } from '@elastic/charts'; import { Datatable, DatatableColumn } from '../../../expressions/public'; -import { DataPublicPluginStart, FieldFormat } from '../../../data/public'; +import { DataPublicPluginStart } from '../../../data/public'; +import type { FieldFormat } from '../../../field_formats/common'; import { ClickTriggerEvent } from '../../../charts/public'; import { ValueClickContext } from '../../../embeddable/public'; import { BucketColumns } from '../types'; diff --git a/src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts b/src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts index e1029b11a7b758..5addae51dd0116 100644 --- a/src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts +++ b/src/plugins/vis_type_pie/public/utils/get_split_dimension_accessor.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import { AccessorFn } from '@elastic/charts'; -import { FieldFormatsStart } from '../../../data/public'; +import type { FieldFormatsStart } from '../../../field_formats/public'; import { DatatableColumn } from '../../../expressions/public'; import { Dimension } from '../types'; diff --git a/src/plugins/vis_type_pie/tsconfig.json b/src/plugins/vis_type_pie/tsconfig.json index f12db316f19723..69bd2855b9843f 100644 --- a/src/plugins/vis_type_pie/tsconfig.json +++ b/src/plugins/vis_type_pie/tsconfig.json @@ -20,5 +20,6 @@ { "path": "../visualizations/tsconfig.json" }, { "path": "../usage_collection/tsconfig.json" }, { "path": "../vis_default_editor/tsconfig.json" }, + { "path": "../field_formats/tsconfig.json" } ] } \ No newline at end of file diff --git a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js index cbc3db6585a7da..2f423538568bd5 100644 --- a/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js +++ b/src/plugins/vis_type_table/public/legacy/agg_table/agg_table.test.js @@ -17,7 +17,7 @@ import { getFieldFormatsRegistry } from '../../../../data/public/test_utils'; import { coreMock } from '../../../../../core/public/mocks'; import { initAngularBootstrap } from '../../../../kibana_legacy/public/angular_bootstrap'; import { setUiSettings } from '../../../../data/public/services'; -import { UI_SETTINGS } from '../../../../data/public/'; +import { FORMATS_UI_SETTINGS } from '../../../../field_formats/common/'; import { CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING } from '../../../../share/public'; import { setFormatService } from '../../services'; @@ -38,12 +38,12 @@ describe('Table Vis - AggTable Directive', function () { const defaultValues = { dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS', 'dateFormat:tz': 'UTC', - [UI_SETTINGS.SHORT_DOTS_ENABLE]: true, - [UI_SETTINGS.FORMAT_CURRENCY_DEFAULT_PATTERN]: '($0,0.[00])', - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: '0,0.[000]', - [UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: '0,0.[000]%', - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE]: 'en', - [UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP]: {}, + [FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE]: true, + [FORMATS_UI_SETTINGS.FORMAT_CURRENCY_DEFAULT_PATTERN]: '($0,0.[00])', + [FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: '0,0.[000]', + [FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: '0,0.[000]%', + [FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE]: 'en', + [FORMATS_UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP]: {}, [CSV_SEPARATOR_SETTING]: ',', [CSV_QUOTE_VALUES_SETTING]: true, }; diff --git a/src/plugins/vis_type_table/public/types.ts b/src/plugins/vis_type_table/public/types.ts index 8f35909d3bfba9..4df6f2b94e17b4 100644 --- a/src/plugins/vis_type_table/public/types.ts +++ b/src/plugins/vis_type_table/public/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IFieldFormat } from 'src/plugins/data/public'; +import type { IFieldFormat } from 'src/plugins/field_formats/common'; import { DatatableColumn, DatatableRow } from 'src/plugins/expressions'; import { ExpressionValueVisDimension } from 'src/plugins/visualizations/public'; import { TableVisParams } from '../common'; diff --git a/src/plugins/vis_type_table/public/utils/add_percentage_column.test.ts b/src/plugins/vis_type_table/public/utils/add_percentage_column.test.ts index 0280637acc0999..51eef1446bac63 100644 --- a/src/plugins/vis_type_table/public/utils/add_percentage_column.test.ts +++ b/src/plugins/vis_type_table/public/utils/add_percentage_column.test.ts @@ -12,7 +12,7 @@ jest.mock('../services', () => ({ })), })); -import { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; import { TableContext } from '../types'; import { addPercentageColumn } from './add_percentage_column'; diff --git a/src/plugins/vis_type_table/tsconfig.json b/src/plugins/vis_type_table/tsconfig.json index ccff3c349cf21c..50277d51e8748d 100644 --- a/src/plugins/vis_type_table/tsconfig.json +++ b/src/plugins/vis_type_table/tsconfig.json @@ -24,5 +24,6 @@ { "path": "../kibana_legacy/tsconfig.json" }, { "path": "../kibana_react/tsconfig.json" }, { "path": "../vis_default_editor/tsconfig.json" }, + { "path": "../field_formats/tsconfig.json" } ] } diff --git a/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx b/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx index 40702c39deaf49..047de1bdb07086 100644 --- a/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx +++ b/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx @@ -126,6 +126,7 @@ function TimelionInterval({ value, setValue, setValidity }: TimelionIntervalProp placeholder={i18n.translate('timelion.vis.selectIntervalPlaceholder', { defaultMessage: 'Select an interval', })} + data-test-subj="timelionIntervalComboBox" /> ); diff --git a/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx b/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx index 4690f4fe11e458..858ba0ad64add5 100644 --- a/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx +++ b/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useEffect, useCallback, useMemo, useRef } from 'react'; +import React, { useCallback, useMemo, useRef } from 'react'; import { compact, last, map } from 'lodash'; import { Chart, @@ -14,13 +14,13 @@ import { Position, Axis, TooltipType, - PointerEvent, LegendPositionConfig, LayoutDirection, } from '@elastic/charts'; import { EuiTitle } from '@elastic/eui'; import { useKibana } from '../../../kibana_react/public'; +import { useActiveCursor } from '../../../charts/public'; import { AreaSeriesComponent, BarSeriesComponent } from './series'; @@ -33,7 +33,7 @@ import { } from '../helpers/panel_utils'; import { colors } from '../helpers/chart_constants'; -import { activeCursor$ } from '../helpers/active_cursor'; +import { getCharts } from '../helpers/plugin_services'; import type { Sheet } from '../helpers/timelion_request_handler'; import type { IInterpreterRenderHandlers } from '../../../expressions'; @@ -43,6 +43,15 @@ import type { Series } from '../helpers/timelion_request_handler'; import './timelion_vis.scss'; +declare global { + interface Window { + /** + * Flag used to enable debugState on elastic charts + */ + _echDebugStateFlag?: boolean; + } +} + interface TimelionVisComponentProps { interval: string; seriesList: Sheet; @@ -91,20 +100,14 @@ const TimelionVisComponent = ({ const kibana = useKibana(); const chartRef = useRef(null); const chart = seriesList.list; + const chartsService = getCharts(); - useEffect(() => { - const subscription = activeCursor$.subscribe((cursor: PointerEvent) => { - chartRef.current?.dispatchExternalPointerEvent(cursor); - }); - - return () => { - subscription.unsubscribe(); - }; - }, []); + const chartTheme = chartsService.theme.useChartsTheme(); + const chartBaseTheme = chartsService.theme.useChartsBaseTheme(); - const handleCursorUpdate = useCallback((cursor: PointerEvent) => { - activeCursor$.next(cursor); - }, []); + const handleCursorUpdate = useActiveCursor(chartsService.activeCursor, chartRef, { + isDateHistogram: true, + }); const brushEndListener = useCallback( ({ x }) => { @@ -174,7 +177,7 @@ const TimelionVisComponent = ({ }, [chart]); return ( -
+
{title && (

{title}

@@ -182,14 +185,15 @@ const TimelionVisComponent = ({ )} tickFormat(value), diff --git a/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts b/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts index 31ec504c3151bd..58fcf510ff7923 100644 --- a/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts +++ b/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts @@ -7,6 +7,7 @@ */ import type { IndexPatternsContract, ISearchStart } from 'src/plugins/data/public'; +import type { ChartsPluginStart } from 'src/plugins/charts/public'; import { createGetterSetter } from '../../../kibana_utils/public'; export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( @@ -14,3 +15,5 @@ export const [getIndexPatterns, setIndexPatterns] = createGetterSetter('Search'); + +export const [getCharts, setCharts] = createGetterSetter('Charts'); diff --git a/src/plugins/vis_type_timelion/public/plugin.ts b/src/plugins/vis_type_timelion/public/plugin.ts index 93712ae4507feb..1784af4e772220 100644 --- a/src/plugins/vis_type_timelion/public/plugin.ts +++ b/src/plugins/vis_type_timelion/public/plugin.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { +import type { CoreSetup, CoreStart, Plugin, @@ -14,30 +14,29 @@ import { IUiSettingsClient, HttpSetup, } from 'kibana/public'; -import { Plugin as ExpressionsPlugin } from 'src/plugins/expressions/public'; -import { +import type { Plugin as ExpressionsPlugin } from 'src/plugins/expressions/public'; +import type { DataPublicPluginSetup, DataPublicPluginStart, TimefilterContract, } from 'src/plugins/data/public'; - -import { VisualizationsSetup } from '../../visualizations/public'; -import { ChartsPluginSetup } from '../../charts/public'; +import type { VisualizationsSetup } from 'src/plugins/visualizations/public'; +import type { ChartsPluginSetup, ChartsPluginStart } from 'src/plugins/charts/public'; import { getTimelionVisualizationConfig } from './timelion_vis_fn'; import { getTimelionVisDefinition } from './timelion_vis_type'; -import { setIndexPatterns, setDataSearch } from './helpers/plugin_services'; -import { ConfigSchema } from '../config'; +import { setIndexPatterns, setDataSearch, setCharts } from './helpers/plugin_services'; import { getArgValueSuggestions } from './helpers/arg_value_suggestions'; import { getTimelionVisRenderer } from './timelion_vis_renderer'; +import type { ConfigSchema } from '../config'; + /** @internal */ export interface TimelionVisDependencies extends Partial { uiSettings: IUiSettingsClient; http: HttpSetup; timefilter: TimefilterContract; - chartTheme: ChartsPluginSetup['theme']; } /** @internal */ @@ -51,6 +50,7 @@ export interface TimelionVisSetupDependencies { /** @internal */ export interface TimelionVisStartDependencies { data: DataPublicPluginStart; + charts: ChartsPluginStart; } /** @public */ @@ -82,7 +82,6 @@ export class TimelionVisPlugin http, uiSettings, timefilter: data.query.timefilter.timefilter, - chartTheme: charts.theme, }; expressions.registerFunction(() => getTimelionVisualizationConfig(dependencies)); @@ -94,9 +93,10 @@ export class TimelionVisPlugin }; } - public start(core: CoreStart, plugins: TimelionVisStartDependencies) { - setIndexPatterns(plugins.data.indexPatterns); - setDataSearch(plugins.data.search); + public start(core: CoreStart, { data, charts }: TimelionVisStartDependencies) { + setIndexPatterns(data.indexPatterns); + setDataSearch(data.search); + setCharts(charts); return { getArgValueSuggestions, diff --git a/src/plugins/vis_type_timeseries/kibana.json b/src/plugins/vis_type_timeseries/kibana.json index 3a2ad6bdd77cf8..c172640a4c14ee 100644 --- a/src/plugins/vis_type_timeseries/kibana.json +++ b/src/plugins/vis_type_timeseries/kibana.json @@ -6,7 +6,7 @@ "ui": true, "requiredPlugins": ["charts", "data", "expressions", "visualizations", "visualize"], "optionalPlugins": ["usageCollection"], - "requiredBundles": ["kibanaUtils", "kibanaReact"], + "requiredBundles": ["kibanaUtils", "kibanaReact", "fieldFormats"], "owner": { "name": "Kibana App", "githubTeam": "kibana-app" diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js b/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js index 04af5786d2e8ea..9b9beae67e44f4 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/tick_formatter.test.js @@ -10,6 +10,7 @@ import { createTickFormatter } from './tick_formatter'; import { getFieldFormatsRegistry } from '../../../../../data/public/test_utils'; import { setFieldFormats } from '../../../services'; import { UI_SETTINGS } from '../../../../../data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../field_formats/common'; const mockUiSettings = { get: (item) => { @@ -22,7 +23,7 @@ const mockUiSettings = { [UI_SETTINGS.QUERY_STRING_OPTIONS]: {}, [UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX]: true, 'dateFormat:tz': 'Browser', - [UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP]: {}, + [FORMATS_UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP]: {}, }; const mockCore = { @@ -45,7 +46,7 @@ describe('createTickFormatter(format, template)', () => { test('returns a percent with percent formatter', () => { const config = { - [UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: '0.[00]%', + [FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: '0.[00]%', }; const fn = createTickFormatter('percent', null, (key) => config[key]); expect(fn(0.5556)).toEqual('55.56%'); @@ -53,7 +54,7 @@ describe('createTickFormatter(format, template)', () => { test('returns a byte formatted string with byte formatter', () => { const config = { - [UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0.0b', + [FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0.0b', }; const fn = createTickFormatter('bytes', null, (key) => config[key]); expect(fn(1500 ^ 10)).toEqual('1.5KB'); @@ -66,7 +67,7 @@ describe('createTickFormatter(format, template)', () => { test('returns a located string with custom locale setting', () => { const config = { - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE]: 'fr', + [FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE]: 'fr', }; const fn = createTickFormatter('0,0.0', null, (key) => config[key]); expect(fn(1500)).toEqual('1 500,0'); @@ -89,7 +90,7 @@ describe('createTickFormatter(format, template)', () => { test('returns formatted value if passed a bad template', () => { const config = { - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: '0,0.[00]', + [FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: '0,0.[00]', }; const fn = createTickFormatter('number', '{{value', (key) => config[key]); expect(fn(1.5556)).toEqual('1.56'); diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js index 4db038de912f5c..ba235a20b97ce2 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/table/vis.js @@ -15,7 +15,7 @@ import { createTickFormatter } from '../../lib/tick_formatter'; import { isSortable } from './is_sortable'; import { EuiToolTip, EuiIcon } from '@elastic/eui'; import { replaceVars } from '../../lib/replace_vars'; -import { fieldFormats } from '../../../../../../../plugins/data/public'; +import { FIELD_FORMAT_IDS } from '../../../../../../../plugins/field_formats/common'; import { FormattedMessage } from '@kbn/i18n/react'; import { getFieldFormats, getCoreStart } from '../../../../services'; import { getValueOrEmpty } from '../../../../../common/empty_label'; @@ -47,7 +47,7 @@ class TableVis extends Component { super(props); const fieldFormatsService = getFieldFormats(); - const DateFormat = fieldFormatsService.getType(fieldFormats.FIELD_FORMAT_IDS.DATE); + const DateFormat = fieldFormatsService.getType(FIELD_FORMAT_IDS.DATE); this.dateFormatter = new DateFormat({}, this.props.getConfig); } diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js index 81b8626261f436..01ba8b6e28114a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js @@ -28,7 +28,7 @@ import { import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; import { SeriesConfigQueryBarWithIgnoreGlobalFilter } from '../../series_config_query_bar_with_ignore_global_filter'; import { PalettePicker } from '../../palette_picker'; -import { getChartsSetup } from '../../../../services'; +import { getCharts } from '../../../../services'; import { isPercentDisabled } from '../../lib/stacked'; import { STACKED_OPTIONS } from '../../../visualizations/constants/chart'; @@ -120,7 +120,7 @@ export const TimeseriesConfig = injectI18n(function (props) { const selectedChartTypeOption = chartTypeOptions.find((option) => { return model.chart_type === option.value; }); - const { palettes } = getChartsSetup(); + const { palettes } = getCharts(); const [palettesRegistry, setPalettesRegistry] = useState(null); useEffect(() => { diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js index b2aa4a6d0c9feb..fd155623d5da7d 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js @@ -11,14 +11,14 @@ import { shallow } from 'enzyme'; import { TimeSeries } from '../../../visualizations/views/timeseries'; import TimeseriesVisualization from './vis'; import { setFieldFormats } from '../../../../services'; -import { UI_SETTINGS } from '../../../../../../data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../field_formats/common'; import { getFieldFormatsRegistry } from '../../../../../../data/public/test_utils'; describe('TimeseriesVisualization', () => { describe('TimeSeries Y-Axis formatted value', () => { const config = { - [UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: '0.[00]%', - [UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0.0b', + [FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: '0.[00]%', + [FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0.0b', }; const id = 'default'; const value = 500; diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js index 76bf3ca9d1b6ee..0f0879e8478464 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useEffect, useRef, useCallback } from 'react'; +import React, { useRef, useCallback } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { labelDateFormatter } from '../../../components/lib/label_date_formatter'; @@ -23,8 +23,7 @@ import { } from '@elastic/charts'; import { EuiIcon } from '@elastic/eui'; import { getTimezone } from '../../../lib/get_timezone'; -import { activeCursor$ } from '../../lib/active_cursor'; -import { getUISettings, getChartsSetup } from '../../../../services'; +import { getUISettings, getCharts } from '../../../../services'; import { GRID_LINE_CONFIG, ICON_TYPES_MAP, STACKED_OPTIONS } from '../../constants'; import { AreaSeriesDecorator } from './decorators/area_decorator'; import { BarSeriesDecorator } from './decorators/bar_decorator'; @@ -33,7 +32,7 @@ import { getBaseTheme, getChartClasses } from './utils/theme'; import { TOOLTIP_MODES } from '../../../../../common/enums'; import { getValueOrEmpty } from '../../../../../common/empty_label'; import { getSplitByTermsColor } from '../../../lib/get_split_by_terms_color'; -import { renderEndzoneTooltip } from '../../../../../../charts/public'; +import { renderEndzoneTooltip, useActiveCursor } from '../../../../../../charts/public'; import { getAxisLabelString } from '../../../components/lib/get_axis_label_string'; import { calculateDomainForSeries } from './utils/series_domain_calculation'; @@ -48,10 +47,6 @@ const generateAnnotationData = (values, formatter) => const decorateFormatter = (formatter) => ({ value }) => formatter(value); -const handleCursorUpdate = (cursor) => { - activeCursor$.next(cursor); -}; - export const TimeSeries = ({ backgroundColor, showGrid, @@ -69,22 +64,17 @@ export const TimeSeries = ({ interval, isLastBucketDropped, }) => { - const chartRef = useRef(); - // const [palettesRegistry, setPalettesRegistry] = useState(null); - - useEffect(() => { - const updateCursor = (cursor) => { - if (chartRef.current) { - chartRef.current.dispatchExternalPointerEvent(cursor); - } - }; + // If the color isn't configured by the user, use the color mapping service + // to assign a color from the Kibana palette. Colors will be shared across the + // session, including dashboards. + const { theme: themeService, activeCursor: activeCursorService } = getCharts(); - const subscription = activeCursor$.subscribe(updateCursor); + const chartRef = useRef(); + const chartTheme = themeService.useChartsTheme(); - return () => { - subscription.unsubscribe(); - }; - }, []); + const handleCursorUpdate = useActiveCursor(activeCursorService, chartRef, { + isDateHistogram: true, + }); let tooltipFormatter = decorateFormatter(xAxisFormatter); if (!isLastBucketDropped) { @@ -104,11 +94,6 @@ export const TimeSeries = ({ // apply legend style change if bgColor is configured const classes = classNames(getChartClasses(backgroundColor)); - // If the color isn't configured by the user, use the color mapping service - // to assign a color from the Kibana palette. Colors will be shared across the - // session, including dashboards. - const { theme: themeService } = getChartsSetup(); - const baseTheme = getBaseTheme(themeService.useChartsBaseTheme(), backgroundColor); const onBrushEndListener = ({ x }) => { @@ -152,6 +137,7 @@ export const TimeSeries = ({ animateData={false} onPointerUpdate={handleCursorUpdate} theme={[ + chartTheme, hasBarChart ? {} : { diff --git a/src/plugins/vis_type_timeseries/public/plugin.ts b/src/plugins/vis_type_timeseries/public/plugin.ts index 479c3f47435cc8..3cd090c7da8293 100644 --- a/src/plugins/vis_type_timeseries/public/plugin.ts +++ b/src/plugins/vis_type_timeseries/public/plugin.ts @@ -20,23 +20,23 @@ import { setFieldFormats, setCoreStart, setDataStart, - setChartsSetup, + setCharts, } from './services'; import { DataPublicPluginStart } from '../../data/public'; -import { ChartsPluginSetup } from '../../charts/public'; +import { ChartsPluginStart } from '../../charts/public'; import { getTimeseriesVisRenderer } from './timeseries_vis_renderer'; /** @internal */ export interface MetricsPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; - charts: ChartsPluginSetup; visualize: VisualizePluginSetup; } /** @internal */ export interface MetricsPluginStartDependencies { data: DataPublicPluginStart; + charts: ChartsPluginStart; } /** @internal */ @@ -49,7 +49,7 @@ export class MetricsPlugin implements Plugin { public setup( core: CoreSetup, - { expressions, visualizations, charts, visualize }: MetricsPluginSetupDependencies + { expressions, visualizations, visualize }: MetricsPluginSetupDependencies ) { visualize.visEditorsRegistry.register(TSVB_EDITOR_NAME, EditorController); expressions.registerFunction(createMetricsFn); @@ -59,11 +59,11 @@ export class MetricsPlugin implements Plugin { }) ); setUISettings(core.uiSettings); - setChartsSetup(charts); visualizations.createBaseVisualization(metricsVisDefinition); } - public start(core: CoreStart, { data }: MetricsPluginStartDependencies) { + public start(core: CoreStart, { data, charts }: MetricsPluginStartDependencies) { + setCharts(charts); setI18n(core.i18n); setFieldFormats(data.fieldFormats); setDataStart(data); diff --git a/src/plugins/vis_type_timeseries/public/services.ts b/src/plugins/vis_type_timeseries/public/services.ts index 22f99f95691ded..da1fd0fbdc2d35 100644 --- a/src/plugins/vis_type_timeseries/public/services.ts +++ b/src/plugins/vis_type_timeseries/public/services.ts @@ -8,7 +8,7 @@ import { I18nStart, IUiSettingsClient, CoreStart } from 'src/core/public'; import { createGetterSetter } from '../../kibana_utils/public'; -import { ChartsPluginSetup } from '../../charts/public'; +import { ChartsPluginStart } from '../../charts/public'; import { DataPublicPluginStart } from '../../data/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -23,6 +23,4 @@ export const [getDataStart, setDataStart] = createGetterSetter('I18n'); -export const [getChartsSetup, setChartsSetup] = createGetterSetter( - 'ChartsPluginSetup' -); +export const [getCharts, setCharts] = createGetterSetter('ChartsPluginStart'); diff --git a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx b/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx index 7a358718c1495b..3f324fcfc2f206 100644 --- a/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx +++ b/src/plugins/vis_type_timeseries/public/timeseries_vis_renderer.tsx @@ -17,7 +17,7 @@ import { VisualizationContainer, PersistedState } from '../../visualizations/pub import type { TimeseriesVisData } from '../common/types'; import { isVisTableData } from '../common/vis_data_utils'; -import { getChartsSetup } from './services'; +import { getCharts } from './services'; import type { TimeseriesVisParams } from './types'; import type { ExpressionRenderDefinition } from '../../expressions/common'; @@ -49,7 +49,7 @@ export const getTimeseriesVisRenderer: (deps: { handlers.onDestroy(() => { unmountComponentAtNode(domNode); }); - const { palettes } = getChartsSetup(); + const { palettes } = getCharts(); const showNoResult = !checkIfDataExists(config.visData, config.visParams); const palettesService = await palettes.getPalettes(); diff --git a/src/plugins/vis_type_vislib/kibana.json b/src/plugins/vis_type_vislib/kibana.json index 26d20b1ff839f7..412264a3e7270d 100644 --- a/src/plugins/vis_type_vislib/kibana.json +++ b/src/plugins/vis_type_vislib/kibana.json @@ -4,7 +4,7 @@ "server": true, "ui": true, "requiredPlugins": ["charts", "data", "expressions", "visualizations", "kibanaLegacy"], - "requiredBundles": ["kibanaUtils", "visDefaultEditor", "visTypeXy", "visTypePie"], + "requiredBundles": ["kibanaUtils", "visDefaultEditor", "visTypeXy", "visTypePie", "fieldFormats"], "owner": { "name": "Kibana App", "githubTeam": "kibana-app" diff --git a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js index 5e1f0bfbb44644..04ab8db1cda8fa 100644 --- a/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js +++ b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js @@ -9,7 +9,7 @@ import { last } from 'lodash'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; -import { UI_SETTINGS } from '../../../../../../plugins/data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../plugins/field_formats/common'; import { getValueForPercentageMode } from '../../percentage_mode_transform'; function getMax(handler, config, isGauge) { @@ -55,7 +55,7 @@ export function pointSeriesTooltipFormatter() { if (isPercentageMode && !isSetColorRange) { const percentageFormatPattern = config.get( isGauge ? 'gauge.percentageFormatPattern' : 'percentageFormatPattern', - uiSettings.get(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN) + uiSettings.get(FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN) ); value = getValueForPercentageMode( value / getMax(handler, config, isGauge), diff --git a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts index 04afc86548f221..815d0e10aafb2c 100644 --- a/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts +++ b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IFieldFormatsRegistry } from '../../../../../data/common'; +import { IFieldFormatsRegistry } from '../../../../../field_formats/common'; import { getPoint } from './_get_point'; import { setFormatService } from '../../../services'; diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js index b7f5b966a08c67..65f7df6459bfe2 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js @@ -10,7 +10,7 @@ import d3 from 'd3'; import _ from 'lodash'; import { getHeatmapColors } from '../../../../../charts/public'; -import { UI_SETTINGS } from '../../../../../data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../field_formats/common'; import { getValueForPercentageMode } from '../../percentage_mode_transform'; const arcAngles = { @@ -73,7 +73,7 @@ export class MeterGauge { const isPercentageMode = this.gaugeConfig.percentageMode; const percentageFormatPattern = this.gaugeConfig.percentageFormatPattern || - this.uiSettings.get(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN); + this.uiSettings.get(FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN); const colorsRange = this.gaugeConfig.colorsRange; const max = _.last(colorsRange).to; const labels = []; diff --git a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js index 0dc1e18270f78e..a25d4087692733 100644 --- a/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js @@ -13,7 +13,7 @@ import { isColorDark } from '@elastic/eui'; import { PointSeries } from './_point_series'; import { getHeatmapColors } from '../../../../../../plugins/charts/public'; -import { UI_SETTINGS } from '../../../../../../plugins/data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../plugins/field_formats/common'; import { getValueForPercentageMode } from '../../percentage_mode_transform'; const defaults = { @@ -54,7 +54,7 @@ export class HeatmapChart extends PointSeries { const percentageMode = cfg.get('percentageMode'); const percentageFormatPattern = cfg.get( 'percentageFormatPattern', - this.uiSettings.get(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN) + this.uiSettings.get(FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN) ); const colorsNumber = cfg.get('colorsNumber'); const colorsRange = cfg.get('colorsRange'); diff --git a/src/plugins/vis_type_xy/public/components/xy_settings.tsx b/src/plugins/vis_type_xy/public/components/xy_settings.tsx index 6d1425f488a47a..03455bae695061 100644 --- a/src/plugins/vis_type_xy/public/components/xy_settings.tsx +++ b/src/plugins/vis_type_xy/public/components/xy_settings.tsx @@ -11,6 +11,7 @@ import React, { FC } from 'react'; import { Direction, Settings, + SettingsSpecProps, DomainRange, Position, PartialTheme, @@ -49,6 +50,7 @@ type XYSettingsProps = Pick< | 'xAxis' | 'orderBucketsBySum' > & { + onPointerUpdate: SettingsSpecProps['onPointerUpdate']; xDomain?: DomainRange; adjustedXDomain?: DomainRange; showLegend: boolean; @@ -85,6 +87,7 @@ export const XYSettings: FC = ({ adjustedXDomain, showLegend, onElementClick, + onPointerUpdate, onBrushEnd, onRenderChange, legendAction, @@ -107,6 +110,9 @@ export const XYSettings: FC = ({ barSeriesStyle: { ...valueLabelsStyling, }, + crosshair: { + ...theme.crosshair, + }, axes: { axisTitle: { padding: { @@ -152,6 +158,7 @@ export const XYSettings: FC = ({ return ( ; visualizations: VisualizationsStart; data: DataPublicPluginStart; + charts: ChartsPluginStart; } type VisTypeXyCoreSetup = CoreSetup; @@ -86,11 +88,11 @@ export class VisTypeXyPlugin return {}; } - public start(core: CoreStart, { data }: VisTypeXyPluginStartDependencies) { + public start(core: CoreStart, { data, charts }: VisTypeXyPluginStartDependencies) { setFormatService(data.fieldFormats); setDataActions(data.actions); setDocLinks(core.docLinks); - + setActiveCursor(charts.activeCursor); return {}; } } diff --git a/src/plugins/vis_type_xy/public/services.ts b/src/plugins/vis_type_xy/public/services.ts index b5dd6152605dcd..63bc55b288ae3a 100644 --- a/src/plugins/vis_type_xy/public/services.ts +++ b/src/plugins/vis_type_xy/public/services.ts @@ -10,7 +10,7 @@ import { UiCounterMetricType } from '@kbn/analytics'; import { CoreSetup, DocLinksStart } from '../../../core/public'; import { createGetterSetter } from '../../kibana_utils/public'; import { DataPublicPluginStart } from '../../data/public'; -import { ChartsPluginSetup } from '../../charts/public'; +import { ChartsPluginSetup, ChartsPluginStart } from '../../charts/public'; export const [getUISettings, setUISettings] = createGetterSetter( 'xy core.uiSettings' @@ -28,6 +28,10 @@ export const [getThemeService, setThemeService] = createGetterSetter('xy charts.activeCursor'); + export const [getPalettesService, setPalettesService] = createGetterSetter< ChartsPluginSetup['palettes'] >('xy charts.palette'); diff --git a/src/plugins/vis_type_xy/public/vis_component.tsx b/src/plugins/vis_type_xy/public/vis_component.tsx index dd88822f7f0f36..2dffabb2ba0b9c 100644 --- a/src/plugins/vis_type_xy/public/vis_component.tsx +++ b/src/plugins/vis_type_xy/public/vis_component.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Chart, @@ -32,7 +32,7 @@ import { } from '../../charts/public'; import { Datatable, IInterpreterRenderHandlers } from '../../expressions/public'; import type { PersistedState } from '../../visualizations/public'; - +import { useActiveCursor } from '../../charts/public'; import { VisParams } from './types'; import { getAdjustedDomain, @@ -47,7 +47,7 @@ import { } from './utils'; import { XYAxis, XYEndzones, XYCurrentTime, XYSettings, XYThresholdLine } from './components'; import { getConfig } from './config'; -import { getThemeService, getDataActions, getPalettesService } from './services'; +import { getThemeService, getDataActions, getPalettesService, getActiveCursor } from './services'; import { ChartType } from '../common'; import './_chart.scss'; @@ -77,6 +77,11 @@ const VisComponent = (props: VisComponentProps) => { return props.uiState?.get('vis.legendOpen', bwcLegendStateDefault) as boolean; }); const [palettesRegistry, setPalettesRegistry] = useState(null); + const chartRef = useRef(null); + + const handleCursorUpdate = useActiveCursor(getActiveCursor(), chartRef, { + datatables: [props.visData], + }); const onRenderChange = useCallback( (isRendered) => { @@ -333,7 +338,7 @@ const VisComponent = (props: VisComponentProps) => { showLegend={showLegend} legendPosition={legendPosition} /> - + { { + describe('Changing field formatter to Url', () => { before(async function () { await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana'); await kibanaServer.uiSettings.replace({ @@ -48,13 +47,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await settings.clickIndexPatternLogstash(); await settings.filterField(fieldName); await settings.openControlsByName(fieldName); + await settings.toggleRow('formatRow'); await settings.setFieldFormat('url'); await settings.controlChangeSave(); }); it('applied on dashboard', async () => { await common.navigateToApp('dashboard'); - await dashboard.loadSavedDashboard('dashboard with everything'); + await dashboard.loadSavedDashboard('dashboard with table'); await dashboard.waitForRenderComplete(); const fieldLink = await visChart.getFieldLinkInVisTable(`${fieldName}: Descending`, 1); await clickFieldAndCheckUrl(fieldLink); diff --git a/test/functional/apps/visualize/_point_series_options.ts b/test/functional/apps/visualize/_point_series_options.ts index ee64fdf7ddda3b..08c26b1f3ee952 100644 --- a/test/functional/apps/visualize/_point_series_options.ts +++ b/test/functional/apps/visualize/_point_series_options.ts @@ -151,7 +151,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should put secondary axis on the right', async function () { - const length = await PageObjects.visChart.getRightValueAxesCount(); + const length = await PageObjects.visChart.getAxesCountByPosition('right'); expect(length).to.be(1); }); }); diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/_timelion.ts new file mode 100644 index 00000000000000..589559c7178427 --- /dev/null +++ b/test/functional/apps/visualize/_timelion.ts @@ -0,0 +1,206 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import expect from '@kbn/expect'; + +import type { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const { timePicker, visChart, visEditor, visualize } = getPageObjects([ + 'timePicker', + 'visChart', + 'visEditor', + 'visualize', + ]); + const monacoEditor = getService('monacoEditor'); + const kibanaServer = getService('kibanaServer'); + const elasticChart = getService('elasticChart'); + const find = getService('find'); + + describe('Timelion visualization', () => { + before(async () => { + await kibanaServer.uiSettings.update({ + 'timelion:legacyChartsLibrary': false, + }); + await visualize.initTests(true); + await visualize.navigateToNewAggBasedVisualization(); + await visualize.clickTimelion(); + await timePicker.setDefaultAbsoluteRange(); + }); + + const initVisualization = async (expression: string, interval: string = '12h') => { + await visEditor.setTimelionInterval(interval); + await monacoEditor.setCodeEditorValue(expression); + await visEditor.clickGo(); + }; + + it('should display correct data for specified index pattern and timefield', async () => { + await initVisualization('.es(index=long-window-logstash-*,timefield=@timestamp)'); + + const chartData = await visChart.getAreaChartData('q:* > count'); + expect(chartData).to.eql([3, 5, 2, 6, 1, 6, 1, 7, 0, 0]); + }); + + it('should display correct chart colors for multiple expressions', async () => { + const expectedColors = ['#01A4A4', '#FFCFDF', '#BFA7DA', '#AD7DE6']; + await initVisualization( + '.es(*), .es(*).color("#FFCFDF"), .es(*).color("#BFA7DA"), .es(*).color("#AD7DE6")' + ); + + const areas = (await elasticChart.getChartDebugData())?.areas; + expect(areas?.map(({ color }) => color)).to.eql(expectedColors); + }); + + it('should display correct chart data for average, min, max and cardinality aggregations', async () => { + await initVisualization( + '.es(index=logstash-*,metric=avg:bytes), .es(index=logstash-*,metric=min:bytes),' + + '.es(index=logstash-*,metric=max:bytes,), .es(index=logstash-*,metric=cardinality:bytes)', + '36h' + ); + + const firstAreaChartData = await visChart.getAreaChartData('q:* > avg(bytes)'); + const secondAreaChartData = await visChart.getAreaChartData('q:* > min(bytes)'); + const thirdAreaChartData = await visChart.getAreaChartData('q:* > max(bytes)'); + const forthAreaChartData = await visChart.getAreaChartData('q:* > cardinality(bytes)'); + + expect(firstAreaChartData).to.eql([5732.783676366217, 5721.775973559419]); + expect(secondAreaChartData).to.eql([0, 0]); + expect(thirdAreaChartData).to.eql([19985, 19986]); + expect(forthAreaChartData).to.eql([5019, 4958, 0, 0]); + }); + + it('should display correct chart data for expressions using functions', async () => { + const firstAreaExpectedChartData = [3, 2421, 2343, 2294, 2327, 2328, 2312, 7, 0, 0]; + const thirdAreaExpectedChartData = [200, 167, 199, 200, 200, 198, 108, 200, 200]; + const forthAreaExpectedChartData = [150, 50, 50, 50, 50, 50, 50, 150, 150, 150]; + await initVisualization( + '.es(*).label("initial"),' + + '.es(*).add(term=.es(*).multiply(-1).abs()).divide(2).label("add multiply abs divide"),' + + '.es(q="bytes<100").derivative().sum(200).min(200).label("query derivative min sum"),' + + '.es(*).if(operator=gt,if=200,then=50,else=150).label("condition")' + ); + + const firstAreaChartData = await visChart.getAreaChartData('initial'); + const secondAreaChartData = await visChart.getAreaChartData('add multiply abs divide'); + const thirdAreaChartData = await visChart.getAreaChartData('query derivative min sum'); + const forthAreaChartData = await visChart.getAreaChartData('condition'); + + expect(firstAreaChartData).to.eql(firstAreaExpectedChartData); + expect(secondAreaChartData).to.eql(firstAreaExpectedChartData); + expect(thirdAreaChartData).to.eql(thirdAreaExpectedChartData); + expect(forthAreaChartData).to.eql(forthAreaExpectedChartData); + }); + + it('should display correct chart title, data and labels for expressions with custom labels, yaxis and offset', async () => { + const firstAreaExpectedChartData = [13112352443.375292, 13095637741.055172]; + const secondAreaExpectedChartData = [ + [1442642400000, 5732.783676366217], + [1442772000000, 5721.775973559419], + ]; + const thirdAreaExpectedChartData = [ + [1442772000000, 5732.783676366217], + [1442901600000, 5721.775973559419], + ]; + await initVisualization( + '.es(index=logstash*,timefield="@timestamp",metric=avg:machine.ram).label("Average Machine RAM amount").yaxis(2,units=bytes,position=right),' + + '.es(index=logstash*,timefield="@timestamp",metric=avg:bytes).label("Average Bytes for request").yaxis(1,units=bytes,position=left),' + + '.es(index=logstash*,timefield="@timestamp",metric=avg:bytes, offset=-12h).label("Average Bytes for request with offset").yaxis(3,units=custom:BYTES_,position=right)', + '36h' + ); + + const leftAxesCount = await visChart.getAxesCountByPosition('left'); + const rightAxesCount = await visChart.getAxesCountByPosition('right'); + const firstAxesLabels = await visChart.getYAxisLabels(); + const secondAxesLabels = await visChart.getYAxisLabels(1); + const thirdAxesLabels = await visChart.getYAxisLabels(2); + const firstAreaChartData = await visChart.getAreaChartData('Average Machine RAM amount'); + const secondAreaChartData = await visChart.getAreaChartData( + 'Average Bytes for request', + undefined, + true + ); + const thirdAreaChartData = await visChart.getAreaChartData( + 'Average Bytes for request with offset', + undefined, + true + ); + + expect(leftAxesCount).to.be(1); + expect(rightAxesCount).to.be(2); + expect(firstAreaChartData).to.eql(firstAreaExpectedChartData); + expect(secondAreaChartData).to.eql(secondAreaExpectedChartData); + expect(thirdAreaChartData).to.eql(thirdAreaExpectedChartData); + expect(firstAxesLabels).to.eql(['12.19GB', '12.2GB', '12.21GB']); + expect(secondAxesLabels).to.eql(['5.59KB', '5.6KB']); + expect(thirdAxesLabels.toString()).to.be( + 'BYTES_5721,BYTES_5722,BYTES_5723,BYTES_5724,BYTES_5725,BYTES_5726,BYTES_5727,BYTES_5728,BYTES_5729,BYTES_5730,BYTES_5731,BYTES_5732,BYTES_5733' + ); + }); + + it('should display correct chart data for split expression', async () => { + await initVisualization('.es(index=logstash-*, split=geo.dest:3)', '1 day'); + + const firstAreaChartData = await visChart.getAreaChartData('q:* > geo.dest:CN > count'); + const secondAreaChartData = await visChart.getAreaChartData('q:* > geo.dest:IN > count'); + const thirdAreaChartData = await visChart.getAreaChartData('q:* > geo.dest:US > count'); + + expect(firstAreaChartData).to.eql([0, 905, 910, 850, 0]); + expect(secondAreaChartData).to.eql([0, 763, 699, 825, 0]); + expect(thirdAreaChartData).to.eql([0, 423, 386, 389, 0]); + }); + + it('should display two areas and one bar chart items', async () => { + await initVisualization('.es(*), .es(*), .es(*).bars(stack=true)'); + + const areasChartsCount = await visChart.getAreaSeriesCount(); + const barsChartsCount = await visChart.getHistogramSeriesCount(); + + expect(areasChartsCount).to.be(2); + expect(barsChartsCount).to.be(1); + }); + + describe('Legend', () => { + it('should correctly display the legend items names and position', async () => { + await initVisualization('.es(*).label("first series"), .es(*).label("second series")'); + + const legendNames = await visChart.getLegendEntries(); + const legendElement = await find.byClassName('echLegend'); + const isLegendTopPositioned = await legendElement.elementHasClass('echLegend--top'); + const isLegendLeftPositioned = await legendElement.elementHasClass('echLegend--left'); + + expect(legendNames).to.eql(['first series', 'second series']); + expect(isLegendTopPositioned).to.be(true); + expect(isLegendLeftPositioned).to.be(true); + }); + + it('should correctly display the legend position', async () => { + await initVisualization('.es(*).legend(position=se)'); + + const legendElement = await find.byClassName('echLegend'); + const isLegendBottomPositioned = await legendElement.elementHasClass('echLegend--bottom'); + const isLegendRightPositioned = await legendElement.elementHasClass('echLegend--right'); + + expect(isLegendBottomPositioned).to.be(true); + expect(isLegendRightPositioned).to.be(true); + }); + + it('should not display the legend', async () => { + await initVisualization('.es(*), .es(*).label("second series").legend(position=false)'); + + const isLegendElementExists = await find.existsByCssSelector('.echLegend'); + expect(isLegendElementExists).to.be(false); + }); + }); + + after( + async () => + await kibanaServer.uiSettings.update({ + 'timelion:legacyChartsLibrary': true, + }) + ); + }); +} diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index df6a879f5a0a92..e3d4fd989c7531 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/_tsvb_time_series.ts @@ -172,7 +172,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { // click on specific coordinates await browser .getActions() - .move({ x: 100, y: 110, origin: el._webElement }) + .move({ x: 105, y: 110, origin: el._webElement }) .click() .perform(); diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index bc6160eba38468..4af871bd9347dd 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -52,6 +52,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_vertical_bar_chart')); loadTestFile(require.resolve('./_vertical_bar_chart_nontimeindex')); loadTestFile(require.resolve('./_pie_chart')); + loadTestFile(require.resolve('./_timelion')); }); describe('visualize ciGroup9', function () { diff --git a/test/functional/apps/visualize/legacy/index.ts b/test/functional/apps/visualize/legacy/index.ts index d474287860b25e..37cf8a59505927 100644 --- a/test/functional/apps/visualize/legacy/index.ts +++ b/test/functional/apps/visualize/legacy/index.ts @@ -7,7 +7,7 @@ */ import { FtrProviderContext } from '../../../ftr_provider_context'; -import { UI_SETTINGS } from '../../../../../src/plugins/data/common'; +import { FORMATS_UI_SETTINGS } from '../../../../../src/plugins/field_formats/common'; export default function ({ getPageObjects, getService, loadTestFile }: FtrProviderContext) { const browser = getService('browser'); @@ -26,7 +26,7 @@ export default function ({ getPageObjects, getService, loadTestFile }: FtrProvid await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/visualize.json'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*', - [UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0,0.[000]b', + [FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0,0.[000]b', }); }); diff --git a/test/functional/fixtures/es_archiver/dashboard/current/kibana/data.json b/test/functional/fixtures/es_archiver/dashboard/current/kibana/data.json index 139da89e58d128..599ffbadaaea77 100644 --- a/test/functional/fixtures/es_archiver/dashboard/current/kibana/data.json +++ b/test/functional/fixtures/es_archiver/dashboard/current/kibana/data.json @@ -1135,6 +1135,49 @@ } } +{ + "type": "doc", + "value": { + "id": "dashboard:d2525040-3dcd-11e8-8660-4d65aa086b3b", + "index": ".kibana", + "source": { + "coreMigrationVersion": "7.14.0", + "dashboard": { + "description": "I have one of every visualization type since the last time I was created!", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + }, + "optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}", + "panelsJSON": "[{\"version\":\"7.3.0\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":15,\"w\":24,\"h\":15,\"i\":\"3\"},\"panelIndex\":\"3\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_3\"}]", + "refreshInterval": { + "display": "Off", + "pause": false, + "value": 0 + }, + "timeFrom": "Mon Apr 09 2018 17:56:08 GMT-0400", + "timeRestore": true, + "timeTo": "Wed Apr 11 2018 17:56:08 GMT-0400", + "title": "dashboard with table", + "version": 1 + }, + "migrationVersion": { + "dashboard": "7.14.0" + }, + "references": [ + { + "id": "4b5d6ef0-3dcb-11e8-8660-4d65aa086b3c", + "name": "3:panel_3", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2018-04-16T16:05:02.915Z" + }, + "type": "_doc" + } +} + { "type": "doc", "value": { @@ -3468,4 +3511,4 @@ }, "type": "_doc" } -} \ No newline at end of file +} diff --git a/test/functional/page_objects/visualize_chart_page.ts b/test/functional/page_objects/visualize_chart_page.ts index 64b8c363fa6c2e..bcee77a21c0b07 100644 --- a/test/functional/page_objects/visualize_chart_page.ts +++ b/test/functional/page_objects/visualize_chart_page.ts @@ -115,10 +115,10 @@ export class VisualizeChartPageObject extends FtrService { .map((tick) => $(tick).text().trim()); } - public async getYAxisLabels() { + public async getYAxisLabels(nth = 0) { if (await this.isNewLibraryChart(xyChartSelector)) { - const [yAxis] = (await this.getEsChartDebugState(xyChartSelector))?.axes?.y ?? []; - return yAxis?.labels; + const yAxis = (await this.getEsChartDebugState(xyChartSelector))?.axes?.y ?? []; + return yAxis[nth]?.labels; } const yAxis = await this.find.byCssSelector('.visAxis__column--y.visAxis__column--left'); @@ -141,14 +141,19 @@ export class VisualizeChartPageObject extends FtrService { * Gets the chart data and scales it based on chart height and label. * @param dataLabel data-label value * @param axis axis value, 'ValueAxis-1' by default + * @param shouldContainXAxisData boolean value for mapping points, false by default * * Returns an array of height values */ - public async getAreaChartData(dataLabel: string, axis = 'ValueAxis-1') { + public async getAreaChartData( + dataLabel: string, + axis = 'ValueAxis-1', + shouldContainXAxisData = false + ) { if (await this.isNewLibraryChart(xyChartSelector)) { const areas = (await this.getEsChartDebugState(xyChartSelector))?.areas ?? []; const points = areas.find(({ name }) => name === dataLabel)?.lines.y1.points ?? []; - return points.map(({ y }) => y); + return shouldContainXAxisData ? points.map(({ x, y }) => [x, y]) : points.map(({ y }) => y); } const yAxisRatio = await this.getChartYAxisRatio(axis); @@ -556,12 +561,12 @@ export class VisualizeChartPageObject extends FtrService { return values.filter((item) => item.length > 0); } - public async getRightValueAxesCount() { + public async getAxesCountByPosition(axesPosition: typeof Position[keyof typeof Position]) { if (await this.isNewLibraryChart(xyChartSelector)) { const yAxes = (await this.getEsChartDebugState(xyChartSelector))?.axes?.y ?? []; - return yAxes.filter(({ position }) => position === Position.Right).length; + return yAxes.filter(({ position }) => position === axesPosition).length; } - const axes = await this.find.allByCssSelector('.visAxis__column--right g.axis'); + const axes = await this.find.allByCssSelector(`.visAxis__column--${axesPosition} g.axis`); return axes.length; } @@ -576,6 +581,16 @@ export class VisualizeChartPageObject extends FtrService { await gauge.clickMouseButton({ xOffset: 0, yOffset }); } + public async getAreaSeriesCount() { + if (await this.isNewLibraryChart(xyChartSelector)) { + const areas = (await this.getEsChartDebugState(xyChartSelector))?.areas ?? []; + return areas.filter((area) => area.lines.y1.visible).length; + } + + const series = await this.find.allByCssSelector('.points.area'); + return series.length; + } + public async getHistogramSeriesCount() { if (await this.isNewLibraryChart(xyChartSelector)) { const bars = (await this.getEsChartDebugState(xyChartSelector))?.bars ?? []; diff --git a/test/functional/page_objects/visualize_editor_page.ts b/test/functional/page_objects/visualize_editor_page.ts index ab458c2c0fdc12..90fc320da3cda4 100644 --- a/test/functional/page_objects/visualize_editor_page.ts +++ b/test/functional/page_objects/visualize_editor_page.ts @@ -532,4 +532,10 @@ export class VisualizeEditorPageObject extends FtrService { public async setSeriesType(seriesNth: number, type: string) { await this.find.selectValue(`select#seriesType${seriesNth}`, type); } + + public async setTimelionInterval(interval: string) { + const timelionIntervalComboBoxSelector = 'timelionIntervalComboBox'; + await this.comboBox.clearInputField(timelionIntervalComboBoxSelector); + await this.comboBox.setCustom(timelionIntervalComboBoxSelector, interval); + } } diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index 7e87312a709108..966a9d29b32644 100644 --- a/test/functional/page_objects/visualize_page.ts +++ b/test/functional/page_objects/visualize_page.ts @@ -8,7 +8,7 @@ import { FtrService } from '../ftr_provider_context'; import { VisualizeConstants } from '../../../src/plugins/visualize/public/application/visualize_constants'; -import { UI_SETTINGS } from '../../../src/plugins/data/common'; +import { FORMATS_UI_SETTINGS } from '../../../src/plugins/field_formats/common'; // TODO: Remove & Refactor to use the TTV page objects interface VisualizeSaveModalArgs { @@ -55,7 +55,7 @@ export class VisualizePageObject extends FtrService { await this.kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*', - [UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0,0.[000]b', + [FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN]: '0,0.[000]b', 'visualization:visualize:legacyChartsLibrary': !isNewLibrary, 'visualization:visualize:legacyPieChartsLibrary': !isNewLibrary, }); @@ -197,6 +197,10 @@ export class VisualizePageObject extends FtrService { return await this.hasVisType('tile_map'); } + public async clickTimelion() { + await this.clickVisType('timelion'); + } + public async clickTagCloud() { await this.clickVisType('tagcloud'); } diff --git a/test/functional/screenshots/baseline/tsvb_dashboard.png b/test/functional/screenshots/baseline/tsvb_dashboard.png index 8c922d9d176f77..a36cfffebf080f 100644 Binary files a/test/functional/screenshots/baseline/tsvb_dashboard.png and b/test/functional/screenshots/baseline/tsvb_dashboard.png differ diff --git a/test/functional/services/monaco_editor.ts b/test/functional/services/monaco_editor.ts index d2afa1e3b6e92b..90674e101fc4e6 100644 --- a/test/functional/services/monaco_editor.ts +++ b/test/functional/services/monaco_editor.ts @@ -27,13 +27,13 @@ export class MonacoEditorService extends FtrService { return values[nthIndex] as string; } - public async setCodeEditorValue(nthIndex: number, value: string) { + public async setCodeEditorValue(value: string, nthIndex = 0) { await this.retry.try(async () => { await this.browser.execute( (editorIndex, codeEditorValue) => { const editor = (window as any).MonacoEnvironment.monaco.editor; const instance = editor.getModels()[editorIndex]; - instance.setValue(JSON.parse(codeEditorValue)); + instance.setValue(codeEditorValue); }, nthIndex, value diff --git a/test/scripts/jenkins_xpack_package_deb.sh b/test/scripts/jenkins_xpack_package_deb.sh index 42098a6464f530..626036e8db3f55 100755 --- a/test/scripts/jenkins_xpack_package_deb.sh +++ b/test/scripts/jenkins_xpack_package_deb.sh @@ -4,6 +4,7 @@ set -e source src/dev/ci_setup/setup_env.sh +mkdir -p target gsutil -q -m cp "gs://ci-artifacts.kibana.dev/package-testing/$GIT_COMMIT/kibana-*.deb" ./target export VAGRANT_CWD=test/package diff --git a/test/scripts/jenkins_xpack_package_docker.sh b/test/scripts/jenkins_xpack_package_docker.sh index 6cae2258303805..c9f94b2c1eb4f5 100755 --- a/test/scripts/jenkins_xpack_package_docker.sh +++ b/test/scripts/jenkins_xpack_package_docker.sh @@ -4,6 +4,7 @@ set -e source src/dev/ci_setup/setup_env.sh +mkdir -p target gsutil -q -m cp "gs://ci-artifacts.kibana.dev/package-testing/$GIT_COMMIT/kibana-[0-9]*-docker-image.tar.gz" ./target export VAGRANT_CWD=test/package diff --git a/test/scripts/jenkins_xpack_package_rpm.sh b/test/scripts/jenkins_xpack_package_rpm.sh index 6aa7754ee4b213..08095ce48c1e5a 100755 --- a/test/scripts/jenkins_xpack_package_rpm.sh +++ b/test/scripts/jenkins_xpack_package_rpm.sh @@ -4,6 +4,7 @@ set -e source src/dev/ci_setup/setup_env.sh +mkdir -p target gsutil -q -m cp "gs://ci-artifacts.kibana.dev/package-testing/$GIT_COMMIT/kibana-*.rpm" ./target export VAGRANT_CWD=test/package diff --git a/typings/index.d.ts b/typings/index.d.ts index 2a5c5e3fa430f0..85c001b26031b6 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -37,3 +37,4 @@ declare module 'react-syntax-highlighter/dist/cjs/prism-light'; // Monaco languages support declare module 'monaco-editor/esm/vs/basic-languages/markdown/markdown'; declare module 'monaco-editor/esm/vs/basic-languages/css/css'; +declare module 'monaco-editor/esm/vs/basic-languages/yaml/yaml'; diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/redirect_to_default_service_route_view.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/redirect_to_default_service_route_view.tsx index 37ec76f2b299ea..66595430f618da 100644 --- a/x-pack/plugins/apm/public/components/routing/service_detail/redirect_to_default_service_route_view.tsx +++ b/x-pack/plugins/apm/public/components/routing/service_detail/redirect_to_default_service_route_view.tsx @@ -4,17 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import qs from 'query-string'; import React from 'react'; import { Redirect } from 'react-router-dom'; -import qs from 'query-string'; -import { enableServiceOverview } from '../../../../common/ui_settings_keys'; -import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { useApmParams } from '../../../hooks/use_apm_params'; export function RedirectToDefaultServiceRouteView() { - const { - core: { uiSettings }, - } = useApmPluginContext(); const { path: { serviceName }, query, @@ -22,14 +17,7 @@ export function RedirectToDefaultServiceRouteView() { const search = qs.stringify(query); - if (uiSettings.get(enableServiceOverview)) { - return ( - - ); - } return ( - + ); } diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx index c5171a012dc63f..d92d7a8d949229 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx @@ -20,7 +20,6 @@ import { isJavaAgentName, isRumAgentName, } from '../../../../../common/agent_name'; -import { enableServiceOverview } from '../../../../../common/ui_settings_keys'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { ApmServiceContextProvider } from '../../../../context/apm_service/apm_service_context'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; @@ -125,7 +124,7 @@ function TemplateWithContext({ function useTabs({ selectedTab }: { selectedTab: Tab['key'] }) { const { agentName } = useApmServiceContext(); - const { core, config } = useApmPluginContext(); + const { config } = useApmPluginContext(); const router = useApmRouter(); @@ -152,7 +151,6 @@ function useTabs({ selectedTab }: { selectedTab: Tab['key'] }) { label: i18n.translate('xpack.apm.serviceDetails.overviewTabLabel', { defaultMessage: 'Overview', }), - hidden: !core.uiSettings.get(enableServiceOverview), }, { key: 'transactions', diff --git a/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.test.tsx index 01a8293163106e..4f3ac315170208 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.test.tsx @@ -11,10 +11,12 @@ import { ALERT_EVALUATION_VALUE, ALERT_ID, ALERT_PRODUCER, + ALERT_OWNER, ALERT_SEVERITY_LEVEL, ALERT_START, ALERT_STATUS, ALERT_UUID, + SPACE_IDS, } from '@kbn/rule-data-utils'; import { ValuesType } from 'utility-types'; import { EuiTheme } from '../../../../../../../../src/plugins/kibana_react/common'; @@ -32,6 +34,7 @@ const theme = ({ eui: { euiColorDanger, euiColorWarning }, } as unknown) as EuiTheme; const alert: Alert = { + [SPACE_IDS]: ['space-id'], 'rule.id': ['apm.transaction_duration'], [ALERT_EVALUATION_VALUE]: [2057657.39], 'service.name': ['frontend-rum'], @@ -42,6 +45,7 @@ const alert: Alert = { 'transaction.type': ['page-load'], [ALERT_PRODUCER]: ['apm'], [ALERT_UUID]: ['af2ae371-df79-4fca-b0eb-a2dbd9478180'], + [ALERT_OWNER]: ['apm'], 'rule.uuid': ['82e0ee40-c2f4-11eb-9a42-a9da66a1722f'], 'event.action': ['active'], '@timestamp': ['2021-06-01T16:16:05.183Z'], diff --git a/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts b/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts index 0e82d70faf1e19..217d7e050369d8 100644 --- a/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts +++ b/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts @@ -10,12 +10,17 @@ import * as t from 'io-ts'; import type { ClientRequestParamsOf, EndpointOf, + formatRequest as formatRequestType, ReturnOf, RouteRepositoryClient, ServerRouteRepository, ServerRoute, } from '@kbn/server-route-repository'; -import { formatRequest } from '@kbn/server-route-repository/target/format_request'; +// @ts-expect-error cannot find module or correspondent type declarations +// The code and types are at separated folders on @kbn/server-route-repository +// so in order to do targeted imports they must me imported separately, and +// an error is expected here +import { formatRequest } from '@kbn/server-route-repository/target_node/format_request'; import { FetchOptions } from '../../../common/fetch_options'; import { callApi } from './callApi'; import type { @@ -81,7 +86,10 @@ export function createCallApmApi(core: CoreStart | CoreSetup) { params?: Partial>; }; - const { method, pathname } = formatRequest(endpoint, params?.path); + const { method, pathname } = formatRequest( + endpoint, + params?.path + ) as ReturnType; return callApi(core, { ...opts, diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 50dd09a6366e86..24d41e2574bbed 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -33,7 +33,6 @@ import { createApmAgentConfigurationIndex } from './lib/settings/agent_configura import { getApmIndices } from './lib/settings/apm_indices/get_apm_indices'; import { createApmCustomLinkIndex } from './lib/settings/custom_link/create_custom_link_index'; import { apmIndices, apmTelemetry, apmServerSettings } from './saved_objects'; -import { uiSettings } from './ui_settings'; import type { ApmPluginRequestHandlerContext, APMRouteHandlerResources, @@ -81,8 +80,6 @@ export class APMPlugin core.savedObjects.registerType(apmTelemetry); core.savedObjects.registerType(apmServerSettings); - core.uiSettings.register(uiSettings); - const currentConfig = mergeConfigs( plugins.apmOss.config, this.initContext.config.get() diff --git a/x-pack/plugins/apm/server/routes/register_routes/index.test.ts b/x-pack/plugins/apm/server/routes/register_routes/index.test.ts index b9dece866fae55..b748d187c5e478 100644 --- a/x-pack/plugins/apm/server/routes/register_routes/index.test.ts +++ b/x-pack/plugins/apm/server/routes/register_routes/index.test.ts @@ -7,7 +7,7 @@ import { jsonRt } from '@kbn/io-ts-utils'; import { createServerRouteRepository } from '@kbn/server-route-repository'; -import { ServerRoute } from '@kbn/server-route-repository/target/typings'; +import { ServerRoute } from '@kbn/server-route-repository'; import * as t from 'io-ts'; import { CoreSetup, Logger } from 'src/core/server'; import { APMConfig } from '../..'; diff --git a/x-pack/plugins/apm/server/ui_settings.ts b/x-pack/plugins/apm/server/ui_settings.ts deleted file mode 100644 index 0161b11cf07844..00000000000000 --- a/x-pack/plugins/apm/server/ui_settings.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { schema } from '@kbn/config-schema'; -import { i18n } from '@kbn/i18n'; -import { UiSettingsParams } from '../../../../src/core/types'; -import { observabilityFeatureId } from '../../observability/common'; -import { enableServiceOverview } from '../common/ui_settings_keys'; - -/** - * uiSettings definitions for APM. - */ -export const uiSettings: Record> = { - [enableServiceOverview]: { - category: [observabilityFeatureId], - name: i18n.translate('xpack.apm.enableServiceOverviewExperimentName', { - defaultMessage: 'APM Service overview', - }), - value: true, - description: i18n.translate( - 'xpack.apm.enableServiceOverviewExperimentDescription', - { - defaultMessage: 'Enable the Overview tab for services in APM.', - } - ), - schema: schema.boolean(), - }, -}; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/elements/metric/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/elements/metric/index.ts index 06376bf1d750b9..76176f6ba21330 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/elements/metric/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/elements/metric/index.ts @@ -8,7 +8,7 @@ import { openSans } from '../../../common/lib/fonts'; import { ElementFactory } from '../../../types'; import { SetupInitializer } from '../../plugin'; -import { UI_SETTINGS } from '../../../../../../src/plugins/data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../src/plugins/field_formats/common'; export const metricElementInitializer: SetupInitializer = (core, setup) => { return () => ({ @@ -22,10 +22,10 @@ export const metricElementInitializer: SetupInitializer = (core, expression: `filters | demodata | math "unique(country)" - | metric "Countries" - metricFont={font size=48 family="${openSans.value}" color="#000000" align="center" lHeight=48} + | metric "Countries" + metricFont={font size=48 family="${openSans.value}" color="#000000" align="center" lHeight=48} labelFont={font size=14 family="${openSans.value}" color="#000000" align="center"} - metricFormat="${core.uiSettings.get(UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN)}" + metricFormat="${core.uiSettings.get(FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN)}" | render`, }); }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts index 90f5da78f79b2c..7c7d573bcd76c6 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts @@ -11,7 +11,7 @@ import { templateFromReactComponent } from '../../../../public/lib/template_from import { ArgumentFactory } from '../../../../types/arguments'; import { ArgumentStrings } from '../../../../i18n'; import { SetupInitializer } from '../../../plugin'; -import { UI_SETTINGS } from '../../../../../../../src/plugins/data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../../src/plugins/field_formats/common'; const { NumberFormat: strings } = ArgumentStrings; @@ -20,11 +20,11 @@ export const numberFormatInitializer: SetupInitializer { const formatMap = { - NUMBER: core.uiSettings.get(UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN), - PERCENT: core.uiSettings.get(UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN), - CURRENCY: core.uiSettings.get(UI_SETTINGS.FORMAT_CURRENCY_DEFAULT_PATTERN), + NUMBER: core.uiSettings.get(FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN), + PERCENT: core.uiSettings.get(FORMATS_UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN), + CURRENCY: core.uiSettings.get(FORMATS_UI_SETTINGS.FORMAT_CURRENCY_DEFAULT_PATTERN), DURATION: '00:00:00', - BYTES: core.uiSettings.get(UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN), + BYTES: core.uiSettings.get(FORMATS_UI_SETTINGS.FORMAT_BYTES_DEFAULT_PATTERN), }; const numberFormats = [ diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/views/metric.ts b/x-pack/plugins/canvas/canvas_plugin_src/uis/views/metric.ts index 501c3fb464c905..775588226aa807 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/uis/views/metric.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/views/metric.ts @@ -8,7 +8,7 @@ import { openSans } from '../../../common/lib/fonts'; import { ViewStrings } from '../../../i18n'; import { SetupInitializer } from '../../plugin'; -import { UI_SETTINGS } from '../../../../../../src/plugins/data/public'; +import { FORMATS_UI_SETTINGS } from '../../../../../../src/plugins/field_formats/common'; const { Metric: strings } = ViewStrings; @@ -24,7 +24,7 @@ export const metricInitializer: SetupInitializer = (core, plugin) => { displayName: strings.getMetricFormatDisplayName(), help: strings.getMetricFormatHelp(), argType: 'numberFormat', - default: `"${core.uiSettings.get(UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN)}"`, + default: `"${core.uiSettings.get(FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN)}"`, }, { name: '_', diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index 263284564de806..c465176e7ed015 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -36,6 +36,7 @@ "lens", "maps", "savedObjects", - "visualizations" + "visualizations", + "fieldFormats" ] } diff --git a/x-pack/plugins/data_visualizer/kibana.json b/x-pack/plugins/data_visualizer/kibana.json index 01aca7c2bbaee2..e7f2d71313abf0 100644 --- a/x-pack/plugins/data_visualizer/kibana.json +++ b/x-pack/plugins/data_visualizer/kibana.json @@ -23,7 +23,8 @@ "home", "kibanaReact", "maps", - "esUiShared" + "esUiShared", + "fieldFormats" ], "extraPublicDirs": [ "common" diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/services/time_buckets.js b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/services/time_buckets.js index 6a68ebeef7c30c..5d54b6c936fb25 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/services/time_buckets.js +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/services/time_buckets.js @@ -5,7 +5,8 @@ * 2.0. */ -import { FIELD_FORMAT_IDS, UI_SETTINGS } from '../../../../../../../src/plugins/data/common'; +import { FIELD_FORMAT_IDS } from '../../../../../../../src/plugins/field_formats/common'; +import { UI_SETTINGS } from '../../../../../../../src/plugins/data/common'; import { ary, assign, isPlainObject, isString, sortBy } from 'lodash'; import moment from 'moment'; import dateMath from '@elastic/datemath'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_router.test.tsx index 587ba61ce27e91..3fa01538613195 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_router.test.tsx @@ -8,17 +8,22 @@ import React from 'react'; import { Switch } from 'react-router-dom'; -import { shallow } from 'enzyme'; +import { shallow, ShallowWrapper } from 'enzyme'; + +import { rerender } from '../../../test_helpers'; import { CrawlerLanding } from './crawler_landing'; import { CrawlerOverview } from './crawler_overview'; import { CrawlerRouter } from './crawler_router'; +import { CrawlerSingleDomain } from './crawler_single_domain'; describe('CrawlerRouter', () => { + let wrapper: ShallowWrapper; const OLD_ENV = process.env; beforeEach(() => { jest.clearAllMocks(); + wrapper = shallow(); }); afterEach(() => { @@ -26,16 +31,18 @@ describe('CrawlerRouter', () => { }); it('renders a landing page by default', () => { - const wrapper = shallow(); - expect(wrapper.find(Switch)).toHaveLength(1); expect(wrapper.find(CrawlerLanding)).toHaveLength(1); }); it('renders a crawler overview in dev', () => { process.env.NODE_ENV = 'development'; - const wrapper = shallow(); + rerender(wrapper); expect(wrapper.find(CrawlerOverview)).toHaveLength(1); }); + + it('renders a crawler single domain view', () => { + expect(wrapper.find(CrawlerSingleDomain)).toHaveLength(1); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_router.tsx index c5dd3907c9019e..3919740b0c6cb0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_router.tsx @@ -8,10 +8,11 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; -import { ENGINE_CRAWLER_PATH } from '../../routes'; +import { ENGINE_CRAWLER_DOMAIN_PATH, ENGINE_CRAWLER_PATH } from '../../routes'; import { CrawlerLanding } from './crawler_landing'; import { CrawlerOverview } from './crawler_overview'; +import { CrawlerSingleDomain } from './crawler_single_domain'; export const CrawlerRouter: React.FC = () => { return ( @@ -19,6 +20,9 @@ export const CrawlerRouter: React.FC = () => { {process.env.NODE_ENV === 'development' ? : } + + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain.test.tsx new file mode 100644 index 00000000000000..dd0966276dd68c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain.test.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { setMockActions, setMockValues } from '../../../__mocks__/kea_logic'; +import '../../../__mocks__/shallow_useeffect.mock'; +import '../../__mocks__/engine_logic.mock'; +import { mockUseParams } from '../../../__mocks__/react_router'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiCode } from '@elastic/eui'; + +import { CrawlerSingleDomain } from './crawler_single_domain'; + +const MOCK_VALUES = { + dataLoading: false, + domain: { + url: 'https://elastic.co', + }, +}; + +const MOCK_ACTIONS = { + fetchDomainData: jest.fn(), +}; + +describe('CrawlerSingleDomain', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockUseParams.mockReturnValue({ domainId: '507f1f77bcf86cd799439011' }); + setMockActions(MOCK_ACTIONS); + setMockValues(MOCK_VALUES); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiCode).render().text()).toContain('https://elastic.co'); + expect(wrapper.prop('pageHeader')).toEqual({ pageTitle: 'https://elastic.co' }); + }); + + it('uses a placeholder for the page title and page chrome if a domain has not been', () => { + setMockValues({ + ...MOCK_VALUES, + domain: null, + }); + + const wrapper = shallow(); + + expect(wrapper.prop('pageHeader')).toEqual({ pageTitle: 'Loading...' }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain.tsx new file mode 100644 index 00000000000000..bdcfa465c8c320 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect } from 'react'; + +import { useParams } from 'react-router-dom'; + +import { useActions, useValues } from 'kea'; + +import { EuiCode } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { getEngineBreadcrumbs } from '../engine'; +import { AppSearchPageTemplate } from '../layout'; + +import { CRAWLER_TITLE } from './constants'; +import { CrawlerSingleDomainLogic } from './crawler_single_domain_logic'; + +export const CrawlerSingleDomain: React.FC = () => { + const { domainId } = useParams() as { domainId: string }; + + const { dataLoading, domain } = useValues(CrawlerSingleDomainLogic); + + const { fetchDomainData } = useActions(CrawlerSingleDomainLogic); + + const displayDomainUrl = domain + ? domain.url + : i18n.translate('xpack.enterpriseSearch.appSearch.crawler.singleDomain.loadingTitle', { + defaultMessage: 'Loading...', + }); + + useEffect(() => { + fetchDomainData(domainId); + }, []); + + return ( + + {JSON.stringify(domain, null, 2)} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain_logic.test.ts new file mode 100644 index 00000000000000..bb478a30ee5ef2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain_logic.test.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + LogicMounter, + mockHttpValues, + mockFlashMessageHelpers, +} from '../../../__mocks__/kea_logic'; +import '../../__mocks__/engine_logic.mock'; + +import { nextTick } from '@kbn/test/jest'; + +import { CrawlerSingleDomainLogic, CrawlerSingleDomainValues } from './crawler_single_domain_logic'; +import { CrawlerDomain } from './types'; + +const DEFAULT_VALUES: CrawlerSingleDomainValues = { + dataLoading: true, + domain: null, +}; + +describe('CrawlerSingleDomainLogic', () => { + const { mount } = new LogicMounter(CrawlerSingleDomainLogic); + const { http } = mockHttpValues; + const { flashAPIErrors } = mockFlashMessageHelpers; + + beforeEach(() => { + jest.clearAllMocks(); + mount(); + }); + + it('has expected default values', () => { + expect(CrawlerSingleDomainLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('actions', () => { + describe('onReceiveDomainData', () => { + const domain = { + id: '507f1f77bcf86cd799439011', + }; + + beforeEach(() => { + CrawlerSingleDomainLogic.actions.onReceiveDomainData(domain as CrawlerDomain); + }); + + it('should set the domain', () => { + expect(CrawlerSingleDomainLogic.values.domain).toEqual(domain); + }); + }); + }); + + describe('listeners', () => { + describe('fetchDomainData', () => { + it('updates logic with data that has been converted from server to client', async () => { + jest.spyOn(CrawlerSingleDomainLogic.actions, 'onReceiveDomainData'); + http.get.mockReturnValueOnce( + Promise.resolve({ + id: '507f1f77bcf86cd799439011', + name: 'https://elastic.co', + created_on: 'Mon, 31 Aug 2020 17:00:00 +0000', + document_count: 13, + sitemaps: [], + entry_points: [], + crawl_rules: [], + }) + ); + + CrawlerSingleDomainLogic.actions.fetchDomainData('507f1f77bcf86cd799439011'); + await nextTick(); + + expect(http.get).toHaveBeenCalledWith( + '/api/app_search/engines/some-engine/crawler/domains/507f1f77bcf86cd799439011' + ); + expect(CrawlerSingleDomainLogic.actions.onReceiveDomainData).toHaveBeenCalledWith({ + id: '507f1f77bcf86cd799439011', + createdOn: 'Mon, 31 Aug 2020 17:00:00 +0000', + url: 'https://elastic.co', + documentCount: 13, + sitemaps: [], + entryPoints: [], + crawlRules: [], + }); + }); + + it('displays any errors to the user', async () => { + http.get.mockReturnValueOnce(Promise.reject('error')); + + CrawlerSingleDomainLogic.actions.fetchDomainData('507f1f77bcf86cd799439011'); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledWith('error'); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain_logic.ts new file mode 100644 index 00000000000000..bccd67a4921d15 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain_logic.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kea, MakeLogicType } from 'kea'; + +import { flashAPIErrors } from '../../../shared/flash_messages'; + +import { HttpLogic } from '../../../shared/http'; +import { EngineLogic } from '../engine'; + +import { CrawlerDomain } from './types'; +import { crawlerDomainServerToClient } from './utils'; + +export interface CrawlerSingleDomainValues { + dataLoading: boolean; + domain: CrawlerDomain | null; +} + +interface CrawlerSingleDomainActions { + fetchDomainData(domainId: string): { domainId: string }; + onReceiveDomainData(domain: CrawlerDomain): { domain: CrawlerDomain }; +} + +export const CrawlerSingleDomainLogic = kea< + MakeLogicType +>({ + path: ['enterprise_search', 'app_search', 'crawler', 'crawler_single_domain'], + actions: { + fetchDomainData: (domainId) => ({ domainId }), + onReceiveDomainData: (domain) => ({ domain }), + }, + reducers: { + dataLoading: [ + true, + { + onReceiveDomainData: () => false, + }, + ], + domain: [ + null, + { + onReceiveDomainData: (_, { domain }) => domain, + }, + ], + }, + listeners: ({ actions }) => ({ + fetchDomainData: async ({ domainId }) => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + + try { + const response = await http.get( + `/api/app_search/engines/${engineName}/crawler/domains/${domainId}` + ); + + const domainData = crawlerDomainServerToClient(response); + + actions.onReceiveDomainData(domainData); + } catch (e) { + flashAPIErrors(e); + } + }, + }), +}); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/crawler.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/crawler.test.ts index f54771e2bef9a7..ee41dce661451a 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/crawler.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/crawler.test.ts @@ -178,6 +178,44 @@ describe('crawler routes', () => { }); }); + describe('GET /api/app_search/engines/{name}/crawler/domains/{id}', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'get', + path: '/api/app_search/engines/{name}/crawler/domains/{id}', + }); + + registerCrawlerRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request to enterprise search', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/api/as/v0/engines/:name/crawler/domains/:id', + }); + }); + + it('validates correctly with name and id', () => { + const request = { params: { name: 'some-engine', id: '1234' } }; + mockRouter.shouldValidate(request); + }); + + it('fails validation without name', () => { + const request = { params: { id: '1234' } }; + mockRouter.shouldThrow(request); + }); + + it('fails validation without id', () => { + const request = { params: { name: 'test-engine' } }; + mockRouter.shouldThrow(request); + }); + }); + describe('POST /api/app_search/crawler/validate_url', () => { let mockRouter: MockRouter; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/crawler.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/crawler.ts index 5404a9a00bdac1..29c1dd74582a30 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/crawler.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/crawler.ts @@ -66,6 +66,21 @@ export function registerCrawlerRoutes({ }) ); + router.get( + { + path: '/api/app_search/engines/{name}/crawler/domains/{id}', + validate: { + params: schema.object({ + name: schema.string(), + id: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/api/as/v0/engines/:name/crawler/domains/:id', + }) + ); + router.delete( { path: '/api/app_search/engines/{name}/crawler/domains/{id}', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_var_field.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_var_field.tsx index eed94de97113d1..398421278b7232 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_var_field.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/package_policy_input_var_field.tsx @@ -7,21 +7,20 @@ import React, { useState, memo, useMemo } from 'react'; import ReactMarkdown from 'react-markdown'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFormRow, EuiSwitch, EuiFieldText, EuiText, - EuiCodeEditor, - EuiTextArea, EuiFieldPassword, + EuiCodeBlock, } from '@elastic/eui'; import type { RegistryVarsEntry } from '../../../../types'; +import { CodeEditor } from '../../../../../../../../../../src/plugins/kibana_react/public'; -import 'brace/mode/yaml'; -import 'brace/theme/textmate'; import { MultiTextInput } from './multi_text_input'; export const PackagePolicyInputVarField: React.FunctionComponent<{ @@ -52,26 +51,34 @@ export const PackagePolicyInputVarField: React.FunctionComponent<{ switch (type) { case 'yaml': return frozen ? ( - + +
{value}
+
) : ( - onChange(newVal)} - onBlur={() => setIsDirty(true)} /> ); case 'bool': diff --git a/x-pack/plugins/fleet/public/components/settings_flyout/index.tsx b/x-pack/plugins/fleet/public/components/settings_flyout/index.tsx index 9bc1bc977b7861..3d3a4dda606320 100644 --- a/x-pack/plugins/fleet/public/components/settings_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/settings_flyout/index.tsx @@ -6,6 +6,7 @@ */ import React, { useEffect, useCallback } from 'react'; +import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { EuiFlyout, @@ -21,9 +22,9 @@ import { EuiForm, EuiFormRow, EuiCode, - EuiCodeEditor, EuiLink, EuiPanel, + EuiTextColor, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiText } from '@elastic/eui'; @@ -39,13 +40,29 @@ import { sendPutOutput, } from '../../hooks'; import { isDiffPathProtocol, normalizeHostsForAgents } from '../../../common'; +import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public'; import { SettingsConfirmModal } from './confirm_modal'; import type { SettingsConfirmModalProps } from './confirm_modal'; import { HostsInput } from './hosts_input'; -import 'brace/mode/yaml'; -import 'brace/theme/textmate'; +const CodeEditorContainer = styled.div` + min-height: 0; + position: relative; + height: 250px; +`; + +const CodeEditorPlaceholder = styled(EuiTextColor).attrs((props) => ({ + color: 'subdued', + size: 'xs', +}))` + position: absolute; + top: 0; + right: 0; + // Matches monaco editor + font-family: Menlo, Monaco, 'Courier New', monospace; + pointer-events: none; +`; const URL_REGEX = /^(https?):\/\/[^\s$.?#].[^\s]*$/gm; @@ -361,21 +378,40 @@ export const SettingFlyout: React.FunctionComponent = ({ onClose }) => { })} fullWidth > - + + + {(!inputs.additionalYamlConfig.value || inputs.additionalYamlConfig.value === '') && ( + + {`# YAML settings here will be added to the Elasticsearch output section of each policy`} + + )} + diff --git a/x-pack/plugins/lens/common/expressions/datatable/sorting.test.tsx b/x-pack/plugins/lens/common/expressions/datatable/sorting.test.tsx index f5d457db652341..b5ff0a7e6915b3 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/sorting.test.tsx +++ b/x-pack/plugins/lens/common/expressions/datatable/sorting.test.tsx @@ -6,7 +6,7 @@ */ import { getSortingCriteria } from './sorting'; -import { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; import { DatatableColumnType } from 'src/plugins/expressions'; function getMockFormatter() { diff --git a/x-pack/plugins/lens/common/expressions/datatable/sorting.tsx b/x-pack/plugins/lens/common/expressions/datatable/sorting.tsx index 13ca811b0b0823..30060c10ea37e5 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/sorting.tsx +++ b/x-pack/plugins/lens/common/expressions/datatable/sorting.tsx @@ -7,7 +7,7 @@ import ipaddr from 'ipaddr.js'; import type { IPv4, IPv6 } from 'ipaddr.js'; -import type { FieldFormat } from '../../../../../../src/plugins/data/common'; +import type { FieldFormat } from '../../../../../../src/plugins/field_formats/common'; function isIPv6Address(ip: IPv4 | IPv6): ip is IPv6 { return ip.kind() === 'ipv6'; diff --git a/x-pack/plugins/lens/common/expressions/datatable/summary.test.ts b/x-pack/plugins/lens/common/expressions/datatable/summary.test.ts index 9f8f56cc927683..61e74e0ef3bc7c 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/summary.test.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/summary.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { IFieldFormat } from 'src/plugins/data/common'; +import type { IFieldFormat } from 'src/plugins/field_formats/common'; import { Datatable } from 'src/plugins/expressions'; import { computeSummaryRowForColumn, getFinalSummaryConfiguration } from './summary'; diff --git a/x-pack/plugins/lens/common/expressions/datatable/summary.ts b/x-pack/plugins/lens/common/expressions/datatable/summary.ts index aceade2a3a5139..76c607ca4c4ee9 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/summary.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/summary.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import type { FieldFormat } from '../../../../../../src/plugins/data/common'; +import type { FieldFormat } from '../../../../../../src/plugins/field_formats/common'; import type { Datatable } from '../../../../../../src/plugins/expressions/common'; import { ColumnConfigArg } from './datatable_column'; import { getOriginalId } from './transpose_helpers'; diff --git a/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts index 7ac6b3d987c842..6adb8b59474db8 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; import type { Datatable } from 'src/plugins/expressions'; import { DatatableArgs } from './datatable'; diff --git a/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts index 06798413c8f40e..e2d928fda24ed1 100644 --- a/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts +++ b/x-pack/plugins/lens/common/expressions/datatable/transpose_helpers.ts @@ -10,7 +10,7 @@ import type { DatatableColumn, DatatableRow, } from '../../../../../../src/plugins/expressions'; -import type { FieldFormat } from '../../../../../../src/plugins/data/common'; +import type { FieldFormat } from '../../../../../../src/plugins/field_formats/common'; import type { DatatableArgs } from './datatable'; import type { ColumnConfig, ColumnConfigArg } from './datatable_column'; diff --git a/x-pack/plugins/lens/common/suffix_formatter/index.ts b/x-pack/plugins/lens/common/suffix_formatter/index.ts index 97fa8c067331e8..00ae005c38b148 100644 --- a/x-pack/plugins/lens/common/suffix_formatter/index.ts +++ b/x-pack/plugins/lens/common/suffix_formatter/index.ts @@ -6,11 +6,11 @@ */ import { i18n } from '@kbn/i18n'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { FieldFormat, FieldFormatInstanceType, - KBN_FIELD_TYPES, -} from '../../../../../src/plugins/data/common'; +} from '../../../../../src/plugins/field_formats/common'; import type { FormatFactory } from '../types'; import type { TimeScaleUnit } from '../expressions/time_scale'; diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index a60061a3aa054b..06fa31b87ce640 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -5,7 +5,8 @@ * 2.0. */ -import type { FilterMeta, Filter, IFieldFormat } from '../../../../src/plugins/data/common'; +import type { FilterMeta, Filter } from '../../../../src/plugins/data/common'; +import type { IFieldFormat } from '../../../../src/plugins/field_formats/common'; import type { Datatable, SerializedFieldFormat } from '../../../../src/plugins/expressions/common'; export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index fd394aea90d668..6a3e0f40c48f4a 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -36,7 +36,8 @@ "savedObjects", "kibanaUtils", "kibanaReact", - "embeddable" + "embeddable", + "fieldFormats" ], "owner": { "name": "Kibana App", diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx index fb9cb992fcf47d..96413444d60c46 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/cell_value.test.tsx @@ -9,7 +9,7 @@ import { mountWithIntl } from '@kbn/test/jest'; import React from 'react'; import { DataContext } from './table_basic'; import { createGridCell } from './cell_value'; -import { FieldFormat } from 'src/plugins/data/public'; +import type { FieldFormat } from 'src/plugins/field_formats/common'; import { Datatable } from 'src/plugins/expressions/public'; import { IUiSettingsClient } from 'kibana/public'; import { act } from 'react-dom/test-utils'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx index bb678a361e1741..a0d137b90e84c5 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/components/table_basic.test.tsx @@ -10,7 +10,8 @@ import { ReactWrapper, shallow } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { mountWithIntl } from '@kbn/test/jest'; import { EuiDataGrid } from '@elastic/eui'; -import { IAggType, IFieldFormat } from 'src/plugins/data/public'; +import { IAggType } from 'src/plugins/data/public'; +import { IFieldFormat } from 'src/plugins/field_formats/common'; import { VisualizationContainer } from '../../visualization_container'; import { EmptyPlaceholder } from '../../shared_components'; import { LensIconChartDatatable } from '../../assets/chart_datatable'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index 4b4d2275d0dec0..163971c4ba9fb3 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -8,7 +8,7 @@ import { DatatableProps } from '../../common/expressions'; import type { LensMultiTable } from '../../common'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; -import { IFieldFormat } from '../../../../../src/plugins/data/public'; +import type { IFieldFormat } from '../../../../../src/plugins/field_formats/common'; import { getDatatable } from './expression'; function sampleArgs() { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx index 43f5527e42d4bf..84bcb48f95234d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -23,7 +23,7 @@ import { keys, } from '@elastic/eui'; import { useDebounceWithOptions } from '../../../../shared_components'; -import { IFieldFormat } from '../../../../../../../../src/plugins/data/common'; +import { IFieldFormat } from '../../../../../../../../src/plugins/field_formats/common'; import { RangeTypeLens, isValidRange } from './ranges'; import { FROM_PLACEHOLDER, TO_PLACEHOLDER, TYPING_DEBOUNCE_TIME } from './constants'; import { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx index 3389c723b4daf6..69460c649c3e1c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx @@ -18,7 +18,7 @@ import { EuiRange, EuiToolTip, } from '@elastic/eui'; -import type { IFieldFormat } from 'src/plugins/data/public'; +import type { IFieldFormat } from 'src/plugins/field_formats/common'; import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/public'; import { RangeColumnParams, UpdateParamsFnType, MODES_TYPES } from './ranges'; import { AdvancedRangeEditor } from './advanced_editor'; @@ -59,7 +59,7 @@ const GranularityHelpPopover = () => {

{UI_SETTINGS.HISTOGRAM_MAX_BARS}, diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx index 21c68a9fe1d826..10575f37dba6e0 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx @@ -10,7 +10,7 @@ import { MetricConfig } from '../../common/expressions'; import React from 'react'; import { shallow } from 'enzyme'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; -import { IFieldFormat } from '../../../../../src/plugins/data/public'; +import type { IFieldFormat } from '../../../../../src/plugins/field_formats/common'; import type { LensMultiTable } from '../../common'; function sampleArgs() { diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap index c17eeec91dc2b6..cf5ca5c9c0c002 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap @@ -17,6 +17,7 @@ exports[`xy_expression XYChart component it renders area 1`] = ` legendPosition="top" onBrushEnd={[Function]} onElementClick={[Function]} + onPointerUpdate={[Function]} rotation={0} showLegend={false} showLegendExtra={false} @@ -234,6 +235,7 @@ exports[`xy_expression XYChart component it renders bar 1`] = ` legendPosition="top" onBrushEnd={[Function]} onElementClick={[Function]} + onPointerUpdate={[Function]} rotation={0} showLegend={false} showLegendExtra={false} @@ -465,6 +467,7 @@ exports[`xy_expression XYChart component it renders horizontal bar 1`] = ` legendPosition="top" onBrushEnd={[Function]} onElementClick={[Function]} + onPointerUpdate={[Function]} rotation={90} showLegend={false} showLegendExtra={false} @@ -696,6 +699,7 @@ exports[`xy_expression XYChart component it renders line 1`] = ` legendPosition="top" onBrushEnd={[Function]} onElementClick={[Function]} + onPointerUpdate={[Function]} rotation={0} showLegend={false} showLegendExtra={false} @@ -913,6 +917,7 @@ exports[`xy_expression XYChart component it renders stacked area 1`] = ` legendPosition="top" onBrushEnd={[Function]} onElementClick={[Function]} + onPointerUpdate={[Function]} rotation={0} showLegend={false} showLegendExtra={false} @@ -1138,6 +1143,7 @@ exports[`xy_expression XYChart component it renders stacked bar 1`] = ` legendPosition="top" onBrushEnd={[Function]} onElementClick={[Function]} + onPointerUpdate={[Function]} rotation={0} showLegend={false} showLegendExtra={false} @@ -1377,6 +1383,7 @@ exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] = legendPosition="top" onBrushEnd={[Function]} onElementClick={[Function]} + onPointerUpdate={[Function]} rotation={90} showLegend={false} showLegendExtra={false} 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 83d86eb410b195..95c9140624e63e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -8,7 +8,7 @@ import { FormatFactory } from '../../common'; import { AxisExtentConfig, XYLayerConfig } from '../../common/expressions'; import { Datatable, SerializedFieldFormat } from '../../../../../src/plugins/expressions/public'; -import { IFieldFormat } from '../../../../../src/plugins/data/public'; +import type { IFieldFormat } from '../../../../../src/plugins/field_formats/common'; interface FormattedMetric { layer: string; diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx index 56a59bd7713b2e..94ed5037000422 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx @@ -49,7 +49,12 @@ import { XyEndzones } from './x_domain'; const onClickValue = jest.fn(); const onSelectRange = jest.fn(); -const chartsThemeService = chartPluginMock.createSetupContract().theme; +const chartSetupContract = chartPluginMock.createSetupContract(); +const chartStartContract = chartPluginMock.createStartContract(); + +const chartsThemeService = chartSetupContract.theme; +const chartsActiveCursorService = chartStartContract.activeCursor; + const paletteService = chartPluginMock.createPaletteRegistry(); const mockPaletteOutput: PaletteOutput = { @@ -473,6 +478,7 @@ describe('xy_expression', () => { timeZone: 'UTC', renderMode: 'display', chartsThemeService, + chartsActiveCursorService, paletteService, minInterval: 50, onClickValue, diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index b7f22ebf8968d7..23b251b76e9504 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -7,7 +7,7 @@ import './expression.scss'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; import { Chart, @@ -47,8 +47,10 @@ import { isHorizontalChart, getSeriesColor } from './state_helpers'; import { search } from '../../../../../src/plugins/data/public'; import { ChartsPluginSetup, + ChartsPluginStart, PaletteRegistry, SeriesLayer, + useActiveCursor, } from '../../../../../src/plugins/charts/public'; import { EmptyPlaceholder } from '../shared_components'; import { getFitOptions } from './fitting_functions'; @@ -85,6 +87,7 @@ export { export type XYChartRenderProps = XYChartProps & { chartsThemeService: ChartsPluginSetup['theme']; + chartsActiveCursorService: ChartsPluginStart['activeCursor']; paletteService: PaletteRegistry; formatFactory: FormatFactory; timeZone: string; @@ -121,7 +124,8 @@ export function calculateMinInterval({ args: { layers }, data }: XYChartProps) { export const getXyChartRenderer = (dependencies: { formatFactory: Promise; - chartsThemeService: ChartsPluginSetup['theme']; + chartsThemeService: ChartsPluginStart['theme']; + chartsActiveCursorService: ChartsPluginStart['activeCursor']; paletteService: PaletteRegistry; timeZone: string; }): ExpressionRenderDefinition => ({ @@ -150,6 +154,7 @@ export const getXyChartRenderer = (dependencies: { (null); const chartTheme = chartsThemeService.useChartsTheme(); const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); const filteredLayers = getFilteredLayers(layers, data); + const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, { + datatables: Object.values(data.tables), + }); + if (filteredLayers.length === 0) { const icon: IconType = layers.length > 0 ? getIconForSeriesType(layers[0].seriesType) : 'bar'; return ; @@ -486,8 +497,9 @@ export function XYChart({ } as LegendPositionConfig; return ( - + , - { expressions, formatFactory, editorFrame, charts }: XyVisualizationPluginSetupPlugins + { expressions, formatFactory, editorFrame }: XyVisualizationPluginSetupPlugins ) { editorFrame.registerVisualization(async () => { const { @@ -41,7 +41,7 @@ export class XyVisualization { getXyChartRenderer, getXyVisualization, } = await import('../async_services'); - const [, { data }] = await core.getStartServices(); + const [, { data, charts }] = await core.getStartServices(); const palettes = await charts.palettes.getPalettes(); expressions.registerFunction(() => legendConfig); expressions.registerFunction(() => yAxisConfig); @@ -57,6 +57,7 @@ export class XyVisualization { getXyChartRenderer({ formatFactory, chartsThemeService: charts.theme, + chartsActiveCursorService: charts.activeCursor, paletteService: palettes, timeZone: getTimeZone(core.uiSettings), }) diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index 134f0b4185b840..6c4d3631a12f37 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -37,5 +37,6 @@ { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/embeddable/tsconfig.json"}, { "path": "../../../src/plugins/presentation_util/tsconfig.json"}, + { "path": "../../../src/plugins/field_formats/tsconfig.json"} ] - } \ No newline at end of file + } diff --git a/x-pack/plugins/ml/common/constants/alerts.ts b/x-pack/plugins/ml/common/constants/alerts.ts index a801872c5444dc..2192b2b504b59c 100644 --- a/x-pack/plugins/ml/common/constants/alerts.ts +++ b/x-pack/plugins/ml/common/constants/alerts.ts @@ -21,26 +21,57 @@ export const TOP_N_BUCKETS_COUNT = 1; export const ALL_JOBS_SELECTION = '*'; -export const HEALTH_CHECK_NAMES: Record = { - datafeed: i18n.translate('xpack.ml.alertTypes.jobsHealthAlertingRule.datafeedCheckName', { - defaultMessage: 'Datafeed is not started', - }), - mml: i18n.translate('xpack.ml.alertTypes.jobsHealthAlertingRule.mmlCheckName', { - defaultMessage: 'Model memory limit reached', - }), - errorMessages: i18n.translate( - 'xpack.ml.alertTypes.jobsHealthAlertingRule.errorMessagesCheckName', - { +export const HEALTH_CHECK_NAMES: Record = { + datafeed: { + name: i18n.translate('xpack.ml.alertTypes.jobsHealthAlertingRule.datafeedCheckName', { + defaultMessage: 'Datafeed is not started', + }), + description: i18n.translate( + 'xpack.ml.alertTypes.jobsHealthAlertingRule.datafeedCheckDescription', + { + defaultMessage: 'Get alerted if the corresponding datafeed of the job is not started', + } + ), + }, + mml: { + name: i18n.translate('xpack.ml.alertTypes.jobsHealthAlertingRule.mmlCheckName', { + defaultMessage: 'Model memory limit reached', + }), + description: i18n.translate('xpack.ml.alertTypes.jobsHealthAlertingRule.mmlCheckDescription', { + defaultMessage: 'Get alerted when job reaches soft or hard model memory limit.', + }), + }, + delayedData: { + name: i18n.translate('xpack.ml.alertTypes.jobsHealthAlertingRule.delayedDataCheckName', { + defaultMessage: 'Data delay has occurred', + }), + description: i18n.translate( + 'xpack.ml.alertTypes.jobsHealthAlertingRule.delayedDataCheckDescription', + { + defaultMessage: 'Get alerted if a job missed data due to data delay.', + } + ), + }, + errorMessages: { + name: i18n.translate('xpack.ml.alertTypes.jobsHealthAlertingRule.errorMessagesCheckName', { defaultMessage: 'There are errors in the job messages', - } - ), - behindRealtime: i18n.translate( - 'xpack.ml.alertTypes.jobsHealthAlertingRule.behindRealtimeCheckName', - { + }), + description: i18n.translate( + 'xpack.ml.alertTypes.jobsHealthAlertingRule.errorMessagesCheckDescription', + { + defaultMessage: 'There are errors in the job messages', + } + ), + }, + behindRealtime: { + name: i18n.translate('xpack.ml.alertTypes.jobsHealthAlertingRule.behindRealtimeCheckName', { defaultMessage: 'Job is running behind real-time', - } - ), - delayedData: i18n.translate('xpack.ml.alertTypes.jobsHealthAlertingRule.delayedDataCheckName', { - defaultMessage: 'Data delay has occurred', - }), + }), + description: i18n.translate( + 'xpack.ml.alertTypes.jobsHealthAlertingRule.behindRealtimeCheckDescription', + { + defaultMessage: 'Job is running behind real-time', + } + ), + }, }; diff --git a/x-pack/plugins/ml/common/types/alerts.ts b/x-pack/plugins/ml/common/types/alerts.ts index 877bb2d2933655..408c424f327d55 100644 --- a/x-pack/plugins/ml/common/types/alerts.ts +++ b/x-pack/plugins/ml/common/types/alerts.ts @@ -109,7 +109,7 @@ export interface JobAlertingRuleStats { alerting_rules?: MlAnomalyDetectionAlertRule[]; } -interface CommonHealthCheckConfig { +export interface CommonHealthCheckConfig { enabled: boolean; } diff --git a/x-pack/plugins/ml/common/types/annotations.ts b/x-pack/plugins/ml/common/types/annotations.ts index 0417e769e9ed56..6234444322a5bd 100644 --- a/x-pack/plugins/ml/common/types/annotations.ts +++ b/x-pack/plugins/ml/common/types/annotations.ts @@ -86,7 +86,12 @@ export interface Annotation { annotation: string; job_id: string; type: ANNOTATION_TYPE.ANNOTATION | ANNOTATION_TYPE.COMMENT; - event?: string; + event?: + | 'user' + | 'delayed_data' + | 'model_snapshot_stored' + | 'model_change' + | 'categorization_status_change'; detector_index?: number; partition_field_name?: string; partition_field_value?: string; diff --git a/x-pack/plugins/ml/common/util/alerts.test.ts b/x-pack/plugins/ml/common/util/alerts.test.ts index dda9fd449467b7..84205e6806133e 100644 --- a/x-pack/plugins/ml/common/util/alerts.test.ts +++ b/x-pack/plugins/ml/common/util/alerts.test.ts @@ -90,6 +90,11 @@ describe('getResultJobsHealthRuleConfig', () => { mml: { enabled: true, }, + delayedData: { + docsCount: 1, + enabled: true, + timeInterval: null, + }, }); }); test('returns config with overridden values based on provided configuration', () => { @@ -97,6 +102,10 @@ describe('getResultJobsHealthRuleConfig', () => { getResultJobsHealthRuleConfig({ mml: { enabled: false }, errorMessages: { enabled: true }, + delayedData: { + enabled: true, + docsCount: 1, + }, }) ).toEqual({ datafeed: { @@ -105,6 +114,11 @@ describe('getResultJobsHealthRuleConfig', () => { mml: { enabled: false, }, + delayedData: { + docsCount: 1, + enabled: true, + timeInterval: null, + }, }); }); }); diff --git a/x-pack/plugins/ml/common/util/alerts.ts b/x-pack/plugins/ml/common/util/alerts.ts index 86afd70ad74740..7328c2a4dcc711 100644 --- a/x-pack/plugins/ml/common/util/alerts.ts +++ b/x-pack/plugins/ml/common/util/alerts.ts @@ -54,7 +54,7 @@ export function getTopNBuckets(job: Job): number { return Math.ceil(narrowBucketLength / bucketSpan.asSeconds()); } -const implementedTests = ['datafeed', 'mml'] as JobsHealthTests[]; +const implementedTests = ['datafeed', 'mml', 'delayedData'] as JobsHealthTests[]; /** * Returns tests configuration combined with default values. @@ -70,6 +70,8 @@ export function getResultJobsHealthRuleConfig(config: JobsHealthRuleTestsConfig) }, delayedData: { enabled: config?.delayedData?.enabled ?? true, + docsCount: config?.delayedData?.docsCount ?? 1, + timeInterval: config?.delayedData?.timeInterval ?? null, }, behindRealtime: { enabled: config?.behindRealtime?.enabled ?? true, diff --git a/x-pack/plugins/ml/kibana.json b/x-pack/plugins/ml/kibana.json index 7f3ad80968b7a8..1196247fe4629d 100644 --- a/x-pack/plugins/ml/kibana.json +++ b/x-pack/plugins/ml/kibana.json @@ -39,7 +39,8 @@ "savedObjects", "home", "maps", - "usageCollection" + "usageCollection", + "fieldFormats" ], "extraPublicDirs": [ "common" diff --git a/x-pack/plugins/ml/public/alerting/jobs_health_rule/register_jobs_health_alerting_rule.ts b/x-pack/plugins/ml/public/alerting/jobs_health_rule/register_jobs_health_alerting_rule.ts index dc4b10102e4f13..f6446b454a8778 100644 --- a/x-pack/plugins/ml/public/alerting/jobs_health_rule/register_jobs_health_alerting_rule.ts +++ b/x-pack/plugins/ml/public/alerting/jobs_health_rule/register_jobs_health_alerting_rule.ts @@ -12,6 +12,7 @@ import { PluginSetupContract as AlertingSetup } from '../../../../alerting/publi import { ML_ALERT_TYPES } from '../../../common/constants/alerts'; import { MlAnomalyDetectionJobsHealthRuleParams } from '../../../common/types/alerts'; import { getResultJobsHealthRuleConfig } from '../../../common/util/alerts'; +import { validateLookbackInterval } from '../validators'; export function registerJobsHealthAlertingRule( triggersActionsUi: TriggersAndActionsUIPublicPluginSetup, @@ -32,6 +33,7 @@ export function registerJobsHealthAlertingRule( errors: { includeJobs: new Array(), testsConfig: new Array(), + delayedData: new Array(), } as Record, }; @@ -53,6 +55,31 @@ export function registerJobsHealthAlertingRule( ); } + if ( + !!resultTestConfig.delayedData.timeInterval && + validateLookbackInterval(resultTestConfig.delayedData.timeInterval) + ) { + validationResult.errors.delayedData.push( + i18n.translate( + 'xpack.ml.alertTypes.jobsHealthAlertingRule.testsConfig.delayedData.timeIntervalErrorMessage', + { + defaultMessage: 'Invalid time interval', + } + ) + ); + } + + if (resultTestConfig.delayedData.docsCount === 0) { + validationResult.errors.delayedData.push( + i18n.translate( + 'xpack.ml.alertTypes.jobsHealthAlertingRule.testsConfig.delayedData.docsCountErrorMessage', + { + defaultMessage: 'Invalid number of documents', + } + ) + ); + } + return validationResult; }, requiresAppContext: false, @@ -68,6 +95,9 @@ export function registerJobsHealthAlertingRule( \\{\\{#memory_status\\}\\}Memory status: \\{\\{memory_status\\}\\} \\{\\{/memory_status\\}\\} \\{\\{#log_time\\}\\}Memory logging time: \\{\\{log_time\\}\\} \\{\\{/log_time\\}\\} \\{\\{#failed_category_count\\}\\}Failed category count: \\{\\{failed_category_count\\}\\} \\{\\{/failed_category_count\\}\\} + \\{\\{#annotation\\}\\}Annotation: \\{\\{annotation\\}\\} \\{\\{/annotation\\}\\} + \\{\\{#missed_docs_count\\}\\}Number of missed documents: \\{\\{missed_docs_count\\}\\} \\{\\{/missed_docs_count\\}\\} + \\{\\{#end_timestamp\\}\\}Latest finalized bucket with missing docs: \\{\\{end_timestamp\\}\\} \\{\\{/end_timestamp\\}\\} \\{\\{/context.results\\}\\} `, } diff --git a/x-pack/plugins/ml/public/alerting/jobs_health_rule/tests_selection_control.tsx b/x-pack/plugins/ml/public/alerting/jobs_health_rule/tests_selection_control.tsx index 07fbc63346cd44..b78c963670da24 100644 --- a/x-pack/plugins/ml/public/alerting/jobs_health_rule/tests_selection_control.tsx +++ b/x-pack/plugins/ml/public/alerting/jobs_health_rule/tests_selection_control.tsx @@ -5,12 +5,22 @@ * 2.0. */ -import React, { FC, Fragment, useCallback } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiFormFieldset, EuiFormRow, EuiSpacer, EuiSwitch } from '@elastic/eui'; +import React, { FC, useCallback } from 'react'; +import { + EuiDescribedFormGroup, + EuiFieldNumber, + EuiForm, + EuiFormRow, + EuiIcon, + EuiSpacer, + EuiSwitch, + EuiToolTip, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { JobsHealthRuleTestsConfig, JobsHealthTests } from '../../../common/types/alerts'; import { getResultJobsHealthRuleConfig } from '../../../common/util/alerts'; import { HEALTH_CHECK_NAMES } from '../../../common/constants/alerts'; +import { TimeIntervalControl } from '../time_interval_control'; interface TestsSelectionControlProps { config: JobsHealthRuleTestsConfig; @@ -18,53 +28,127 @@ interface TestsSelectionControlProps { errors?: string[]; } -export const TestsSelectionControl: FC = ({ - config, - onChange, - errors, -}) => { - const uiConfig = getResultJobsHealthRuleConfig(config); +export const TestsSelectionControl: FC = React.memo( + ({ config, onChange, errors }) => { + const uiConfig = getResultJobsHealthRuleConfig(config); - const updateCallback = useCallback( - (update: Partial>) => { - onChange({ - ...(config ?? {}), - ...update, - }); - }, - [onChange, config] - ); + const updateCallback = useCallback( + (update: Partial>) => { + onChange({ + ...(config ?? {}), + ...update, + }); + }, + [onChange, config] + ); - return ( - - {Object.entries(uiConfig).map(([name, conf], i) => { - return ( - - + {(Object.entries(uiConfig) as Array< + [JobsHealthTests, typeof uiConfig[JobsHealthTests]] + >).map(([name, conf], i) => { + return ( + {HEALTH_CHECK_NAMES[name]?.name}} + description={HEALTH_CHECK_NAMES[name]?.description} > - - - - - ); - })} - - ); -}; + + + } + onChange={updateCallback.bind(null, { + [name]: { + ...uiConfig[name], + enabled: !uiConfig[name].enabled, + }, + })} + checked={uiConfig[name].enabled} + /> + + + + {name === 'delayedData' ? ( + <> + + + + } + > + + + + } + > + { + updateCallback({ + [name]: { + ...uiConfig[name], + docsCount: Number(e.target.value), + }, + }); + }} + min={1} + /> + + + + + + + + } + > + + + + } + value={uiConfig.delayedData.timeInterval} + onChange={(e) => { + updateCallback({ + [name]: { + ...uiConfig[name], + timeInterval: e, + }, + }); + }} + /> + + + + ) : null} + + ); + })} + + ); + } +); diff --git a/x-pack/plugins/ml/public/alerting/time_interval_control.tsx b/x-pack/plugins/ml/public/alerting/time_interval_control.tsx index 8030d340a37745..4ab73500958ae7 100644 --- a/x-pack/plugins/ml/public/alerting/time_interval_control.tsx +++ b/x-pack/plugins/ml/public/alerting/time_interval_control.tsx @@ -27,7 +27,7 @@ export const TimeIntervalControl: FC = ({ const validationErrors = useMemo(() => validators(value), [value]); - const isInvalid = value !== undefined && !!validationErrors; + const isInvalid = !!value && !!validationErrors; return ( { const mlClient = ({ @@ -20,12 +23,15 @@ describe('JobsHealthService', () => { jobs = [ ({ job_id: 'test_job_01', + analysis_config: { bucket_span: '1h' }, } as unknown) as MlJob, ({ job_id: 'test_job_02', + analysis_config: { bucket_span: '15m' }, } as unknown) as MlJob, ({ job_id: 'test_job_03', + analysis_config: { bucket_span: '8m' }, } as unknown) as MlJob, ]; } @@ -34,6 +40,7 @@ describe('JobsHealthService', () => { jobs = [ ({ job_id: jobIds[0], + analysis_config: { bucket_span: '1h' }, } as unknown) as MlJob, ]; } @@ -84,13 +91,32 @@ describe('JobsHealthService', () => { return Promise.resolve( jobIds.map((j) => { return { + job_id: j, datafeed_id: j.replace('job', 'datafeed'), + query_delay: '3m', }; }) ); }), } as unknown) as jest.Mocked; + const annotationService = ({ + getDelayedDataAnnotations: jest.fn().mockImplementation(({ jobIds }: { jobIds: string[] }) => { + return Promise.resolve( + jobIds.map((jobId) => { + return { + job_id: jobId, + annotation: `Datafeed has missed ${ + jobId === 'test_job_01' ? 11 : 8 + } documents due to ingest latency, latest bucket with missing data is [2021-07-30T13:50:00.000Z]. Consider increasing query_delay`, + modified_time: 1627660295141, + end_timestamp: 1627653300000, + }; + }) + ); + }), + } as unknown) as jest.Mocked; + const logger = ({ warn: jest.fn(), info: jest.fn(), @@ -100,13 +126,19 @@ describe('JobsHealthService', () => { const jobHealthService: JobsHealthService = jobsHealthServiceProvider( mlClient, datafeedsService, + annotationService, logger ); - beforeEach(() => {}); + let dateNowSpy: jest.SpyInstance; + + beforeEach(() => { + dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => MOCK_DATE_NOW); + }); afterEach(() => { jest.clearAllMocks(); + dateNowSpy.mockRestore(); }); test('returns empty results when no jobs provided', async () => { @@ -131,7 +163,11 @@ describe('JobsHealthService', () => { enabled: false, }, behindRealtime: null, - delayedData: null, + delayedData: { + enabled: false, + docsCount: null, + timeInterval: null, + }, errorMessages: null, mml: { enabled: false, @@ -149,6 +185,54 @@ describe('JobsHealthService', () => { expect(executionResult).toEqual([]); }); + test('takes into account delayed data params', async () => { + const executionResult = await jobHealthService.getTestsResults('testRule_04', { + testsConfig: { + delayedData: { + enabled: true, + docsCount: 10, + timeInterval: '4h', + }, + behindRealtime: { enabled: false, timeInterval: null }, + mml: { enabled: false }, + datafeed: { enabled: false }, + errorMessages: { enabled: false }, + }, + includeJobs: { + jobIds: [], + groupIds: ['test_group'], + }, + excludeJobs: { + jobIds: ['test_job_03'], + groupIds: [], + }, + }); + + expect(annotationService.getDelayedDataAnnotations).toHaveBeenCalledWith({ + jobIds: ['test_job_01', 'test_job_02'], + // 1487076708000 - 4h + earliestMs: 1487062308000, + }); + + expect(executionResult).toEqual([ + { + name: 'Data delay has occurred', + context: { + results: [ + { + job_id: 'test_job_01', + annotation: + 'Datafeed has missed 11 documents due to ingest latency, latest bucket with missing data is [2021-07-30T13:50:00.000Z]. Consider increasing query_delay', + end_timestamp: 1627653300000, + missed_docs_count: 11, + }, + ], + message: '1 job is suffering from delayed data.', + }, + }, + ]); + }); + test('returns results based on provided selection', async () => { const executionResult = await jobHealthService.getTestsResults('testRule_03', { testsConfig: null, @@ -169,11 +253,17 @@ describe('JobsHealthService', () => { 'test_job_01', 'test_job_02', ]); + expect(datafeedsService.getDatafeedByJobId).toHaveBeenCalledTimes(1); expect(mlClient.getJobStats).toHaveBeenCalledWith({ job_id: 'test_job_01,test_job_02' }); expect(mlClient.getDatafeedStats).toHaveBeenCalledWith({ datafeed_id: 'test_datafeed_01,test_datafeed_02', }); expect(mlClient.getJobStats).toHaveBeenCalledTimes(1); + expect(annotationService.getDelayedDataAnnotations).toHaveBeenCalledWith({ + jobIds: ['test_job_01', 'test_job_02'], + earliestMs: 1487069268000, + }); + expect(executionResult).toEqual([ { name: 'Datafeed is not started', @@ -203,6 +293,28 @@ describe('JobsHealthService', () => { '1 job reached the hard model memory limit. Assign the job more memory and restore from a snapshot from prior to reaching the hard limit.', }, }, + { + name: 'Data delay has occurred', + context: { + results: [ + { + job_id: 'test_job_01', + annotation: + 'Datafeed has missed 11 documents due to ingest latency, latest bucket with missing data is [2021-07-30T13:50:00.000Z]. Consider increasing query_delay', + end_timestamp: 1627653300000, + missed_docs_count: 11, + }, + { + job_id: 'test_job_02', + annotation: + 'Datafeed has missed 8 documents due to ingest latency, latest bucket with missing data is [2021-07-30T13:50:00.000Z]. Consider increasing query_delay', + end_timestamp: 1627653300000, + missed_docs_count: 8, + }, + ], + message: '2 jobs are suffering from delayed data.', + }, + }, ]); }); }); diff --git a/x-pack/plugins/ml/server/lib/alerts/jobs_health_service.ts b/x-pack/plugins/ml/server/lib/alerts/jobs_health_service.ts index 5c34a84abd9719..52e17fed7a4143 100644 --- a/x-pack/plugins/ml/server/lib/alerts/jobs_health_service.ts +++ b/x-pack/plugins/ml/server/lib/alerts/jobs_health_service.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { memoize, keyBy } from 'lodash'; import { KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; import { i18n } from '@kbn/i18n'; import { Logger } from 'kibana/server'; -import { MlJobStats } from '@elastic/elasticsearch/api/types'; +import { MlJob } from '@elastic/elasticsearch/api/types'; import { MlClient } from '../ml_client'; import { AnomalyDetectionJobsHealthRuleParams, @@ -20,10 +21,18 @@ import { DatafeedStats } from '../../../common/types/anomaly_detection_jobs'; import { GetGuards } from '../../shared_services/shared_services'; import { AnomalyDetectionJobsHealthAlertContext, + DelayedDataResponse, MmlTestResponse, NotStartedDatafeedResponse, } from './register_jobs_monitoring_rule_type'; -import { getResultJobsHealthRuleConfig } from '../../../common/util/alerts'; +import { + getResultJobsHealthRuleConfig, + resolveLookbackInterval, +} from '../../../common/util/alerts'; +import { AnnotationService } from '../../models/annotation_service/annotation'; +import { annotationServiceProvider } from '../../models/annotation_service'; +import { parseInterval } from '../../../common/util/parse_interval'; +import { isDefined } from '../../../common/types/guards'; interface TestResult { name: string; @@ -35,14 +44,18 @@ type TestsResults = TestResult[]; export function jobsHealthServiceProvider( mlClient: MlClient, datafeedsService: DatafeedsService, + annotationService: AnnotationService, logger: Logger ) { /** - * Extracts result list of job ids based on included and excluded selection of jobs and groups. + * Extracts result list of jobs based on included and excluded selection of jobs and groups. * @param includeJobs * @param excludeJobs */ - const getResultJobIds = async (includeJobs: JobSelection, excludeJobs?: JobSelection | null) => { + const getResultJobs = async ( + includeJobs: JobSelection, + excludeJobs?: JobSelection | null + ): Promise => { const jobAndGroupIds = [...(includeJobs.jobIds ?? []), ...(includeJobs.groupIds ?? [])]; const includeAllJobs = jobAndGroupIds.some((id) => id === ALL_JOBS_SELECTION); @@ -54,7 +67,7 @@ export function jobsHealthServiceProvider( }) ).body.jobs; - let resultJobIds = jobsResponse.map((v) => v.job_id); + let resultJobs = jobsResponse; if (excludeJobs && (!!excludeJobs.jobIds.length || !!excludeJobs?.groupIds.length)) { const excludedJobAndGroupIds = [ @@ -69,33 +82,41 @@ export function jobsHealthServiceProvider( const excludedJobsIds: Set = new Set(excludedJobsResponse.map((v) => v.job_id)); - resultJobIds = resultJobIds.filter((v) => !excludedJobsIds.has(v)); + resultJobs = resultJobs.filter((v) => !excludedJobsIds.has(v.job_id)); } - return resultJobIds; + return resultJobs; }; - const getJobStats: (jobIds: string[]) => Promise = (() => { - const cachedStats = new Map(); + /** + * Resolves the timestamp for delayed data check. + * + * @param timeInterval - Custom time interval provided by the user. + * @param defaultLookbackInterval - Interval derived from the jobs and datefeeds configs. + */ + const getDelayedDataLookbackTimestamp = ( + timeInterval: string | null, + defaultLookbackInterval: string + ): number => { + const currentTimestamp = Date.now(); - return async (jobIds: string[]) => { - if (jobIds.every((j) => cachedStats.has(j))) { - logger.debug(`Return jobs stats from cache`); - return Array.from(cachedStats.values()); - } + const defaultLookbackTimestamp = + currentTimestamp - parseInterval(defaultLookbackInterval)!.asMilliseconds(); - const { - body: { jobs: jobsStats }, - } = await mlClient.getJobStats({ job_id: jobIds.join(',') }); + const customIntervalOffsetTimestamp = timeInterval + ? currentTimestamp - parseInterval(timeInterval)!.asMilliseconds() + : null; - // update cache - jobsStats.forEach((v) => { - cachedStats.set(v.job_id, v); - }); + return Math.min(...[defaultLookbackTimestamp, customIntervalOffsetTimestamp].filter(isDefined)); + }; - return jobsStats; - }; - })(); + const getJobIds = memoize((jobs: MlJob[]) => jobs.map((j) => j.job_id)); + + const getDatafeeds = memoize(datafeedsService.getDatafeedByJobId); + + const getJobStats = memoize( + async (jobIds: string[]) => (await mlClient.getJobStats({ job_id: jobIds.join(',') })).body.jobs + ); return { /** @@ -103,7 +124,7 @@ export function jobsHealthServiceProvider( * @param jobIds */ async getNotStartedDatafeeds(jobIds: string[]): Promise { - const datafeeds = await datafeedsService.getDatafeedByJobId(jobIds); + const datafeeds = await getDatafeeds(jobIds); if (datafeeds) { const jobsStats = await getJobStats(jobIds); @@ -154,6 +175,67 @@ export function jobsHealthServiceProvider( }; }); }, + /** + * Returns annotations about delayed data. + * + * @param jobs + * @param timeInterval - Custom time interval provided by the user. + * @param docsCount - The threshold for a number of missing documents to alert upon. + */ + async getDelayedDataReport( + jobs: MlJob[], + timeInterval: string | null, + docsCount: number | null + ): Promise { + const jobIds = getJobIds(jobs); + const datafeeds = await getDatafeeds(jobIds); + + const datafeedsMap = keyBy(datafeeds, 'job_id'); + + // We shouldn't check jobs that don't have associated datafeeds + const resultJobs = jobs.filter((j) => datafeedsMap[j.job_id] !== undefined); + const resultJobIds = getJobIds(resultJobs); + const jobsMap = keyBy(resultJobs, 'job_id'); + + const defaultLookbackInterval = resolveLookbackInterval(resultJobs, datafeeds!); + const earliestMs = getDelayedDataLookbackTimestamp(timeInterval, defaultLookbackInterval); + + const annotations: DelayedDataResponse[] = ( + await annotationService.getDelayedDataAnnotations({ + jobIds: resultJobIds, + earliestMs, + }) + ) + .map((v) => { + const match = v.annotation.match(/Datafeed has missed (\d+)\s/); + const missedDocsCount = match ? parseInt(match[1], 10) : 0; + return { + annotation: v.annotation, + // end_timestamp is always defined for delayed_data annotation + end_timestamp: v.end_timestamp!, + missed_docs_count: missedDocsCount, + job_id: v.job_id, + }; + }) + .filter((v) => { + // As we retrieved annotations based on the longest bucket span and query delay, + // we need to check end_timestamp against appropriate job configuration. + + const job = jobsMap[v.job_id]; + const datafeed = datafeedsMap[v.job_id]; + + const isDocCountExceededThreshold = docsCount ? v.missed_docs_count >= docsCount : true; + + const jobLookbackInterval = resolveLookbackInterval([job], [datafeed]); + + const isEndTimestampWithinRange = + v.end_timestamp > getDelayedDataLookbackTimestamp(timeInterval, jobLookbackInterval); + + return isDocCountExceededThreshold && isEndTimestampWithinRange; + }); + + return annotations; + }, /** * Retrieves report grouped by test. */ @@ -165,7 +247,8 @@ export function jobsHealthServiceProvider( const results: TestsResults = []; - const jobIds = await getResultJobIds(includeJobs, excludeJobs); + const jobs = await getResultJobs(includeJobs, excludeJobs); + const jobIds = getJobIds(jobs); if (jobIds.length === 0) { logger.warn(`Rule "${ruleInstanceName}" does not have associated jobs.`); @@ -178,7 +261,7 @@ export function jobsHealthServiceProvider( const response = await this.getNotStartedDatafeeds(jobIds); if (response && response.length > 0) { results.push({ - name: HEALTH_CHECK_NAMES.datafeed, + name: HEALTH_CHECK_NAMES.datafeed.name, context: { results: response, message: i18n.translate( @@ -200,7 +283,7 @@ export function jobsHealthServiceProvider( }, 0); results.push({ - name: HEALTH_CHECK_NAMES.mml, + name: HEALTH_CHECK_NAMES.mml.name, context: { results: response, message: @@ -226,6 +309,31 @@ export function jobsHealthServiceProvider( } } + if (config.delayedData.enabled) { + const response = await this.getDelayedDataReport( + jobs, + config.delayedData.timeInterval, + config.delayedData.docsCount + ); + + if (response.length > 0) { + results.push({ + name: HEALTH_CHECK_NAMES.delayedData.name, + context: { + results: response, + message: i18n.translate( + 'xpack.ml.alertTypes.jobsHealthAlertingRule.delayedDataMessage', + { + defaultMessage: + '{jobsCount, plural, one {# job is} other {# jobs are}} suffering from delayed data.', + values: { jobsCount: response.length }, + } + ), + }, + }); + } + } + return results; }, }; @@ -251,6 +359,7 @@ export function getJobsHealthServiceProvider(getGuards: GetGuards) { jobsHealthServiceProvider( mlClient, datafeedsProvider(scopedClient, mlClient), + annotationServiceProvider(scopedClient), logger ).getTestsResults(...args) ); diff --git a/x-pack/plugins/ml/server/lib/alerts/register_jobs_monitoring_rule_type.ts b/x-pack/plugins/ml/server/lib/alerts/register_jobs_monitoring_rule_type.ts index 0e4270fb94e3be..063d8ad5a89803 100644 --- a/x-pack/plugins/ml/server/lib/alerts/register_jobs_monitoring_rule_type.ts +++ b/x-pack/plugins/ml/server/lib/alerts/register_jobs_monitoring_rule_type.ts @@ -42,7 +42,20 @@ export interface NotStartedDatafeedResponse { job_state: MlJobState; } -export type AnomalyDetectionJobHealthResult = MmlTestResponse | NotStartedDatafeedResponse; +export interface DelayedDataResponse { + job_id: string; + /** Annotation string */ + annotation: string; + /** Number of missed documents */ + missed_docs_count: number; + /** Timestamp of the latest finalized bucket with missing docs */ + end_timestamp: number; +} + +export type AnomalyDetectionJobHealthResult = + | MmlTestResponse + | NotStartedDatafeedResponse + | DelayedDataResponse; export type AnomalyDetectionJobsHealthAlertContext = { results: AnomalyDetectionJobHealthResult[]; @@ -107,7 +120,7 @@ export function registerJobsMonitoringRuleType({ producer: PLUGIN_ID, minimumLicenseRequired: MINIMUM_FULL_LICENSE, isExportable: true, - async executor({ services, params, alertId, state, previousStartedAt, startedAt, name }) { + async executor({ services, params, alertId, state, previousStartedAt, startedAt, name, rule }) { const fakeRequest = {} as KibanaRequest; const { getTestsResults } = mlServicesProviders.jobsHealthServiceProvider( services.savedObjectsClient, diff --git a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts index c5f3c152ddc7ae..9552735a57d358 100644 --- a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts +++ b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts @@ -9,6 +9,7 @@ import Boom from '@hapi/boom'; import { each, get } from 'lodash'; import { IScopedClusterClient } from 'kibana/server'; +import { estypes } from '@elastic/elasticsearch'; import { ANNOTATION_EVENT_USER, ANNOTATION_TYPE } from '../../../common/constants/annotations'; import { PARTITION_FIELDS } from '../../../common/constants/anomalies'; import { @@ -25,6 +26,7 @@ import { getAnnotationFieldValue, EsAggregationResult, } from '../../../common/types/annotations'; +import { JobId } from '../../../common/types/anomaly_detection_jobs'; // TODO All of the following interface/type definitions should // eventually be replaced by the proper upstream definitions @@ -46,6 +48,7 @@ export interface IndexAnnotationArgs { fields?: FieldToBucket[]; detectorIndex?: number; entities?: any[]; + event?: Annotation['event']; } export interface AggTerm { @@ -60,7 +63,7 @@ export interface GetParams { export interface GetResponse { success: true; - annotations: Record; + annotations: Record; aggregations: EsAggregationResult; } @@ -116,7 +119,8 @@ export function annotationProvider({ asInternalUser }: IScopedClusterClient) { fields, detectorIndex, entities, - }: IndexAnnotationArgs) { + event, + }: IndexAnnotationArgs): Promise { const obj: GetResponse = { success: true, annotations: {}, @@ -190,6 +194,12 @@ export function annotationProvider({ asInternalUser }: IScopedClusterClient) { exists: { field: 'annotation' }, }); + if (event) { + boolCriteria.push({ + term: { event }, + }); + } + if (jobIds && jobIds.length > 0 && !(jobIds.length === 1 && jobIds[0] === '*')) { let jobIdFilterStr = ''; each(jobIds, (jobId, i: number) => { @@ -332,6 +342,68 @@ export function annotationProvider({ asInternalUser }: IScopedClusterClient) { } } + /** + * Fetches the latest delayed data annotation per job. + * @param jobIds + * @param earliestMs - Timestamp for the end_timestamp range query. + */ + async function getDelayedDataAnnotations({ + jobIds, + earliestMs, + }: { + jobIds: string[]; + earliestMs?: number; + }): Promise { + const params: estypes.SearchRequest = { + index: ML_ANNOTATIONS_INDEX_ALIAS_READ, + size: 0, + body: { + query: { + bool: { + filter: [ + ...(earliestMs ? [{ range: { end_timestamp: { gte: earliestMs } } }] : []), + { + term: { event: { value: 'delayed_data' } }, + }, + { terms: { job_id: jobIds } }, + ], + }, + }, + aggs: { + by_job: { + terms: { field: 'job_id', size: jobIds.length }, + aggs: { + latest_delayed: { + top_hits: { + size: 1, + sort: [ + { + end_timestamp: { + order: 'desc', + }, + }, + ], + }, + }, + }, + }, + }, + }, + }; + + const { body } = await asInternalUser.search(params); + + const annotations = (body.aggregations!.by_job as estypes.AggregationsTermsAggregate<{ + key: string; + doc_count: number; + latest_delayed: Pick, 'hits'>; + }>).buckets.map((bucket) => { + return bucket.latest_delayed.hits.hits[0]._source!; + }); + + return annotations; + } + async function deleteAnnotation(id: string) { const params: DeleteParams = { index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE, @@ -347,5 +419,8 @@ export function annotationProvider({ asInternalUser }: IScopedClusterClient) { getAnnotations, indexAnnotation, deleteAnnotation, + getDelayedDataAnnotations, }; } + +export type AnnotationService = ReturnType; diff --git a/x-pack/plugins/ml/server/routes/schemas/alerting_schema.ts b/x-pack/plugins/ml/server/routes/schemas/alerting_schema.ts index 4e0f9a9aa7c928..ed58d8d5c9313e 100644 --- a/x-pack/plugins/ml/server/routes/schemas/alerting_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/alerting_schema.ts @@ -79,7 +79,7 @@ export const anomalyDetectionJobsHealthRuleParams = schema.object({ delayedData: schema.nullable( schema.object({ enabled: schema.boolean({ defaultValue: true }), - docsCount: schema.nullable(schema.number()), + docsCount: schema.nullable(schema.number({ min: 1 })), timeInterval: schema.nullable(schema.string()), }) ), diff --git a/x-pack/plugins/ml/server/routes/schemas/annotations_schema.ts b/x-pack/plugins/ml/server/routes/schemas/annotations_schema.ts index 96edbeb0fce0ec..73e876f0a91220 100644 --- a/x-pack/plugins/ml/server/routes/schemas/annotations_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/annotations_schema.ts @@ -21,7 +21,15 @@ export const indexAnnotationSchema = schema.object({ create_username: schema.maybe(schema.string()), modified_time: schema.maybe(schema.number()), modified_username: schema.maybe(schema.string()), - event: schema.maybe(schema.string()), + event: schema.maybe( + schema.oneOf([ + schema.literal('user'), + schema.literal('delayed_data'), + schema.literal('model_snapshot_stored'), + schema.literal('model_change'), + schema.literal('categorization_status_change'), + ]) + ), detector_index: schema.maybe(schema.number()), partition_field_name: schema.maybe(schema.string()), partition_field_value: schema.maybe(schema.string()), diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts index 964de86ddf377e..b61af3a61c3dc1 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts @@ -5,11 +5,11 @@ * 2.0. */ +import type { FieldFormat as IFieldFormat } from 'src/plugins/field_formats/common'; import { SavedObjectNotFound } from '../../../../../../../../src/plugins/kibana_utils/public'; import { DataPublicPluginStart, IndexPattern, - FieldFormat as IFieldFormat, IndexPatternSpec, } from '../../../../../../../../src/plugins/data/public'; import { rumFieldFormats } from '../configurations/rum/field_formats'; diff --git a/x-pack/plugins/observability/public/services/call_observability_api/index.ts b/x-pack/plugins/observability/public/services/call_observability_api/index.ts index c87a97fb1dc8a9..7b1b9d222b36e8 100644 --- a/x-pack/plugins/observability/public/services/call_observability_api/index.ts +++ b/x-pack/plugins/observability/public/services/call_observability_api/index.ts @@ -5,7 +5,9 @@ * 2.0. */ -import { formatRequest } from '@kbn/server-route-repository/target/format_request'; +// @ts-expect-error +import { formatRequest } from '@kbn/server-route-repository/target_node/format_request'; +import type { formatRequest as formatRequestType } from '@kbn/server-route-repository/target_types/format_request'; import type { HttpSetup } from 'kibana/public'; import type { AbstractObservabilityClient, ObservabilityClient } from './types'; @@ -17,7 +19,9 @@ export function createCallObservabilityApi(http: HttpSetup) { const client: AbstractObservabilityClient = (options) => { const { params: { path, body, query } = {}, endpoint, ...rest } = options; - const { method, pathname } = formatRequest(endpoint, path); + const { method, pathname } = formatRequest(endpoint, path) as ReturnType< + typeof formatRequestType + >; return http[method](pathname, { ...rest, diff --git a/x-pack/plugins/reporting/server/export_types/csv/execute_job.test.ts b/x-pack/plugins/reporting/server/export_types/csv/execute_job.test.ts index 65b53b3b77eb49..835eddc03d9f91 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/execute_job.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/execute_job.test.ts @@ -14,7 +14,11 @@ import moment from 'moment'; import Puid from 'puid'; import sinon from 'sinon'; import { ReportingConfig, ReportingCore } from '../../'; -import { fieldFormats, UI_SETTINGS } from '../../../../../../src/plugins/data/server'; +import { + FieldFormatsRegistry, + StringFormat, + FORMATS_UI_SETTINGS, +} from '../../../../../../src/plugins/field_formats/common'; import { CSV_QUOTE_VALUES_SETTING, CSV_SEPARATOR_SETTING, @@ -104,15 +108,13 @@ describe('CSV Execute Job', function () { setFieldFormats({ fieldFormatServiceFactory() { const uiConfigMock = {}; - (uiConfigMock as any)[UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP] = { + (uiConfigMock as any)[FORMATS_UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP] = { _default_: { id: 'string', params: {} }, }; - const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); + const fieldFormatsRegistry = new FieldFormatsRegistry(); - fieldFormatsRegistry.init((key) => (uiConfigMock as any)[key], {}, [ - fieldFormats.StringFormat, - ]); + fieldFormatsRegistry.init((key) => (uiConfigMock as any)[key], {}, [StringFormat]); return Promise.resolve(fieldFormatsRegistry); }, diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/field_format_map.test.ts b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/field_format_map.test.ts index ed303fb0c27bd4..6e1ad21397e73a 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/field_format_map.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/field_format_map.test.ts @@ -6,7 +6,13 @@ */ import expect from '@kbn/expect'; -import { fieldFormats, FieldFormatsGetConfigFn, UI_SETTINGS } from 'src/plugins/data/server'; +import { + FieldFormatsGetConfigFn, + FieldFormatsRegistry, + BytesFormat, + NumberFormat, + FORMATS_UI_SETTINGS, +} from 'src/plugins/field_formats/common'; import { IndexPatternSavedObjectDeprecatedCSV } from '../types'; import { fieldFormatMapFactory } from './field_format_map'; @@ -22,16 +28,16 @@ describe('field format map', function () { }, }; const configMock: Record = {}; - configMock[UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP] = { + configMock[FORMATS_UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP] = { number: { id: 'number', params: {} }, }; - configMock[UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN] = '0,0.[000]'; + configMock[FORMATS_UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN] = '0,0.[000]'; const getConfig = ((key: string) => configMock[key]) as FieldFormatsGetConfigFn; const testValue = '4000'; const mockTimezone = 'Browser'; - const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); - fieldFormatsRegistry.init(getConfig, {}, [fieldFormats.BytesFormat, fieldFormats.NumberFormat]); + const fieldFormatsRegistry = new FieldFormatsRegistry(); + fieldFormatsRegistry.init(getConfig, {}, [BytesFormat, NumberFormat]); const formatMap = fieldFormatMapFactory( indexPatternSavedObject, diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/field_format_map.ts b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/field_format_map.ts index e5508f42f8c0d6..9d094f4308ed73 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/field_format_map.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/field_format_map.ts @@ -6,8 +6,11 @@ */ import _ from 'lodash'; -import { FieldFormat } from 'src/plugins/data/common'; -import { FieldFormatConfig, IFieldFormatsRegistry } from 'src/plugins/data/server'; +import { + FieldFormat, + IFieldFormatsRegistry, + FieldFormatConfig, +} from 'src/plugins/field_formats/common'; import { IndexPatternSavedObjectDeprecatedCSV } from '../types'; /** diff --git a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/format_csv_values.ts b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/format_csv_values.ts index 440dcc69b1c730..006aa41c6a35e4 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/generate_csv/format_csv_values.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/generate_csv/format_csv_values.ts @@ -6,7 +6,7 @@ */ import { isNull, isObject, isUndefined } from 'lodash'; -import { FieldFormat } from 'src/plugins/data/common'; +import { FieldFormat } from 'src/plugins/field_formats/common'; import { RawValue } from '../types'; export function createFormatCsvValues( diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts index b0a3a8c7001b18..87512005044059 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.test.ts @@ -14,7 +14,8 @@ import { savedObjectsClientMock, uiSettingsServiceMock, } from 'src/core/server/mocks'; -import { FieldFormatsRegistry, ISearchStartSearchSource } from 'src/plugins/data/common'; +import { ISearchStartSearchSource } from 'src/plugins/data/common'; +import { FieldFormatsRegistry } from 'src/plugins/field_formats/common'; import { searchSourceInstanceMock } from 'src/plugins/data/common/search/search_source/mocks'; import { IScopedSearchClient } from 'src/plugins/data/server'; import { dataPluginMock } from 'src/plugins/data/server/mocks'; diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts index 3855eff3821b9a..8488b54b77f00a 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts @@ -15,9 +15,6 @@ import { ReportingConfig } from '../../..'; import { cellHasFormulas, ES_SEARCH_STRATEGY, - FieldFormat, - FieldFormatConfig, - IFieldFormatsRegistry, IndexPattern, ISearchSource, ISearchStartSearchSource, @@ -25,6 +22,11 @@ import { SearchSourceFields, tabifyDocs, } from '../../../../../../../src/plugins/data/common'; +import { + FieldFormat, + FieldFormatConfig, + IFieldFormatsRegistry, +} from '../../../../../../../src/plugins/field_formats/common'; import { KbnServerError } from '../../../../../../../src/plugins/kibana_utils/server'; import { CancellationToken } from '../../../../common'; import { CONTENT_TYPE_CSV } from '../../../../common/constants'; diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts index 695f29dd8d6324..8ad2e62d98c522 100644 --- a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts +++ b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts @@ -12,7 +12,7 @@ jest.mock('../browsers'); import _ from 'lodash'; import * as Rx from 'rxjs'; import { coreMock, elasticsearchServiceMock } from 'src/core/server/mocks'; -import { fieldFormats } from 'src/plugins/data/server'; +import { FieldFormatsRegistry } from 'src/plugins/field_formats/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { dataPluginMock } from 'src/plugins/data/server/mocks'; import { ReportingConfig, ReportingCore } from '../'; @@ -171,7 +171,7 @@ export const createMockReportingCore = async ( setFieldFormats({ fieldFormatServiceFactory() { - const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); + const fieldFormatsRegistry = new FieldFormatsRegistry(); return Promise.resolve(fieldFormatsRegistry); }, }); diff --git a/x-pack/plugins/reporting/tsconfig.json b/x-pack/plugins/reporting/tsconfig.json index c28086b96aea28..406fe9965b8a03 100644 --- a/x-pack/plugins/reporting/tsconfig.json +++ b/x-pack/plugins/reporting/tsconfig.json @@ -24,6 +24,7 @@ { "path": "../../../src/plugins/share/tsconfig.json" }, { "path": "../../../src/plugins/ui_actions/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, + { "path": "../../../src/plugins/field_formats/tsconfig.json" }, { "path": "../features/tsconfig.json" }, { "path": "../licensing/tsconfig.json" }, { "path": "../security/tsconfig.json" }, diff --git a/x-pack/plugins/rule_registry/README.md b/x-pack/plugins/rule_registry/README.md index 19e276aeec4895..16e4b8f3e01e65 100644 --- a/x-pack/plugins/rule_registry/README.md +++ b/x-pack/plugins/rule_registry/README.md @@ -124,7 +124,6 @@ The following fields are defined in the technical field component template and s - `rule.uuid`: the saved objects id of the rule. - `rule.name`: the name of the rule (as specified by the user). - `rule.category`: the name of the rule type (as defined by the rule type producer) -- `kibana.alert.producer`: the producer of the rule type. Usually a Kibana plugin. e.g., `APM`. - `kibana.alert.owner`: the feature which produced the alert. Usually a Kibana feature id like `apm`, `siem`... - `kibana.alert.id`: the id of the alert, that is unique within the context of the rule execution it was created in. E.g., for a rule that monitors latency for all services in all environments, this might be `opbeans-java:production`. - `kibana.alert.uuid`: the unique identifier for the alert during its lifespan. If an alert recovers (or closes), this identifier is re-generated when it is opened again. diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/ecs_field_map.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/ecs_field_map.ts index ff81e05851f7e5..859070bd498e32 100644 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/ecs_field_map.ts +++ b/x-pack/plugins/rule_registry/common/assets/field_maps/ecs_field_map.ts @@ -2148,7 +2148,7 @@ export const ecsFieldMap = { 'rule.id': { type: 'keyword', array: false, - required: false, + required: true, }, 'rule.license': { type: 'keyword', diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts index ebe7c88f756b20..11e572260d1335 100644 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts +++ b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts @@ -20,9 +20,9 @@ export const technicalRuleFieldMap = { Fields.RULE_CATEGORY, Fields.TAGS ), - [Fields.ALERT_OWNER]: { type: 'keyword' }, + [Fields.ALERT_OWNER]: { type: 'keyword', required: true }, [Fields.ALERT_PRODUCER]: { type: 'keyword' }, - [Fields.SPACE_IDS]: { type: 'keyword', array: true }, + [Fields.SPACE_IDS]: { type: 'keyword', array: true, required: true }, [Fields.ALERT_UUID]: { type: 'keyword' }, [Fields.ALERT_ID]: { type: 'keyword' }, [Fields.ALERT_START]: { type: 'date' }, diff --git a/x-pack/plugins/rule_registry/server/mocks.ts b/x-pack/plugins/rule_registry/server/mocks.ts index cc5c3cfd484a7c..395b53e229d643 100644 --- a/x-pack/plugins/rule_registry/server/mocks.ts +++ b/x-pack/plugins/rule_registry/server/mocks.ts @@ -5,8 +5,10 @@ * 2.0. */ +import { ruleDataPluginServiceMock } from './rule_data_plugin_service/rule_data_plugin_service.mock'; import { createLifecycleAlertServicesMock } from './utils/lifecycle_alert_services_mock'; export const ruleRegistryMocks = { createLifecycleAlertServices: createLifecycleAlertServicesMock, + createRuleDataPluginService: ruleDataPluginServiceMock.create, }; diff --git a/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.test.ts b/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.test.ts index d89eb305545e88..073a48248f89ad 100644 --- a/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.test.ts +++ b/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.test.ts @@ -13,6 +13,7 @@ import { CONSUMERS, ECS_VERSION, RULE_ID, + SPACE_IDS, TIMESTAMP, VERSION, } from '@kbn/rule-data-utils'; @@ -33,6 +34,7 @@ const getMockAlert = (): ParsedTechnicalFields => ({ [ALERT_OWNER]: 'apm', [ALERT_STATUS]: 'open', [ALERT_RULE_RISK_SCORE]: 20, + [SPACE_IDS]: ['fake-space-id'], [ALERT_RULE_SEVERITY]: 'warning', }); diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts index 275d68621864fc..348c0fc6b1cfce 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts @@ -6,11 +6,11 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; -import { RuleDataPluginService, RuleDataPluginServiceConstructorOptions } from './'; +import { RuleDataPluginService } from './'; type Schema = PublicMethodsOf; -const createRuleDataPluginServiceMock = (_: RuleDataPluginServiceConstructorOptions) => { +const createRuleDataPluginService = () => { const mocked: jest.Mocked = { init: jest.fn(), isReady: jest.fn(), @@ -27,9 +27,7 @@ const createRuleDataPluginServiceMock = (_: RuleDataPluginServiceConstructorOpti }; export const ruleDataPluginServiceMock: { - create: ( - _: RuleDataPluginServiceConstructorOptions - ) => jest.Mocked>; + create: () => jest.Mocked>; } = { - create: createRuleDataPluginServiceMock, + create: createRuleDataPluginService, }; diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts index 80b75b8c74732d..037efadabd8def 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.test.ts @@ -23,6 +23,9 @@ import { ALERT_STATUS, EVENT_ACTION, EVENT_KIND, + RULE_ID, + ALERT_OWNER, + SPACE_IDS, } from '../../common/technical_rule_data_field_names'; import { createRuleDataClientMock } from '../rule_data_client/create_rule_data_client_mock'; import { createLifecycleExecutor } from './create_lifecycle_executor'; @@ -128,12 +131,16 @@ describe('createLifecycleExecutor', () => { { fields: { [ALERT_ID]: 'TEST_ALERT_0', + [ALERT_OWNER]: 'CONSUMER', + [RULE_ID]: 'RULE_TYPE_ID', labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc }, }, { fields: { [ALERT_ID]: 'TEST_ALERT_1', + [ALERT_OWNER]: 'CONSUMER', + [RULE_ID]: 'RULE_TYPE_ID', labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc }, }, @@ -222,6 +229,9 @@ describe('createLifecycleExecutor', () => { fields: { '@timestamp': '', [ALERT_ID]: 'TEST_ALERT_0', + [ALERT_OWNER]: 'CONSUMER', + [RULE_ID]: 'RULE_TYPE_ID', + [SPACE_IDS]: ['fake-space-id'], labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must show up in the written doc }, }, @@ -229,6 +239,9 @@ describe('createLifecycleExecutor', () => { fields: { '@timestamp': '', [ALERT_ID]: 'TEST_ALERT_1', + [ALERT_OWNER]: 'CONSUMER', + [RULE_ID]: 'RULE_TYPE_ID', + [SPACE_IDS]: ['fake-space-id'], labels: { LABEL_0_KEY: 'LABEL_0_VALUE' }, // this must not show up in the written doc }, }, diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts index 50ac8afb945b43..23ae24cb91bc40 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts @@ -30,6 +30,7 @@ import { EVENT_ACTION, EVENT_KIND, ALERT_OWNER, + RULE_ID, RULE_UUID, TIMESTAMP, SPACE_IDS, @@ -154,6 +155,8 @@ export const createLifecycleExecutor = ( currentAlerts[id] = { ...fields, [ALERT_ID]: id, + [RULE_ID]: rule.ruleTypeId, + [ALERT_OWNER]: rule.consumer, }; return alertInstanceFactory(id); }, @@ -226,6 +229,8 @@ export const createLifecycleExecutor = ( alertsDataMap[alertId] = { ...fields, [ALERT_ID]: alertId, + [RULE_ID]: rule.ruleTypeId, + [ALERT_OWNER]: rule.consumer, }; }); } diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 6d4a2b78840ea6..0ae42d4baaec45 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -16,6 +16,7 @@ export const allowedExperimentalValues = Object.freeze({ ruleRegistryEnabled: false, tGridEnabled: false, trustedAppsByPolicyEnabled: false, + excludePoliciesInFilterEnabled: false, uebaEnabled: false, }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/login.ts b/x-pack/plugins/security_solution/cypress/tasks/login.ts index 2eba6aba0c5e22..243bfd113bfd23 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/login.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/login.ts @@ -205,11 +205,6 @@ const credentialsProvidedByEnvironment = (): boolean => * Kibana's `/internal/security/login` endpoint, bypassing the login page (for speed). */ const loginViaEnvironmentCredentials = () => { - const providerName = - Cypress.env('protocol') === 'http' || Cypress.config().baseUrl!.includes('staging') - ? 'basic' - : 'cloud-basic'; - cy.log( `Authenticating via environment credentials from the \`CYPRESS_${ELASTICSEARCH_USERNAME}\` and \`CYPRESS_${ELASTICSEARCH_PASSWORD}\` environment variables` ); @@ -218,7 +213,7 @@ const loginViaEnvironmentCredentials = () => { cy.request({ body: { providerType: 'basic', - providerName, + providerName: 'basic', currentURL: '/', params: { username: Cypress.env(ELASTICSEARCH_USERNAME), diff --git a/x-pack/plugins/security_solution/public/common/components/help_menu/index.tsx b/x-pack/plugins/security_solution/public/common/components/help_menu/index.tsx index 20bb4f51c075b0..28ff98d5c9c880 100644 --- a/x-pack/plugins/security_solution/public/common/components/help_menu/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/help_menu/index.tsx @@ -32,7 +32,7 @@ export const HelpMenu = React.memo(() => { content: i18n.translate('xpack.securitySolution.chrome.helpMenu.documentation.ecs', { defaultMessage: 'ECS documentation', }), - href: `${docLinks.ELASTIC_WEBSITE_URL}guide/en/ecs/current/index.html`, + href: docLinks.links.ecs.guide, iconType: 'documents', linkType: 'custom', target: '_blank', diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx index cc6ac5355f90b8..d7d62d82a95b04 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx @@ -197,7 +197,7 @@ export const MlPopover = React.memo(() => { values={{ mlDocs: ( diff --git a/x-pack/plugins/security_solution/public/common/store/app/reducer.ts b/x-pack/plugins/security_solution/public/common/store/app/reducer.ts index 5b0a2330a408d0..6ab572490f5d7f 100644 --- a/x-pack/plugins/security_solution/public/common/store/app/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/app/reducer.ts @@ -19,6 +19,7 @@ export const initialAppState: AppState = { errors: [], enableExperimental: { trustedAppsByPolicyEnabled: false, + excludePoliciesInFilterEnabled: false, metricsEntitiesEnabled: false, ruleRegistryEnabled: false, tGridEnabled: false, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.test.tsx index 935a5ff336c00f..70b98eb22fd89a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.test.tsx @@ -20,6 +20,9 @@ describe('UpdatePrePackagedRulesCallOut', () => { docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '', + links: { + siem: { ruleChangeLog: '' }, + }, }, }, }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.tsx index 754acea40ed9bd..9d67a1e6b9ae86 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/update_callout.tsx @@ -56,10 +56,7 @@ const UpdatePrePackagedRulesCallOutComponent: React.FC {prepackagedRulesOrTimelines?.callOutMessage}
- + {i18n.RELEASE_NOTES_HELP}

diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine_no_index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine_no_index.tsx index b65cb5069d399b..19219208b4079a 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine_no_index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine_no_index.tsx @@ -33,7 +33,7 @@ const DetectionEngineNoIndexComponent: React.FC<{ detections: { icon: 'documents', label: i18n.GO_TO_DOCUMENTATION, - url: `${docLinks.ELASTIC_WEBSITE_URL}guide/en/security/${docLinks.DOC_LINK_VERSION}/detections-permissions-section.html`, + url: `${docLinks.links.siem.detectionsReq}`, target: '_blank', }, }), diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine_user_unauthenticated.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine_user_unauthenticated.tsx index 19dd14c46a4a94..c725bae54b06c7 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine_user_unauthenticated.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine_user_unauthenticated.tsx @@ -18,7 +18,7 @@ export const DetectionEngineUserUnauthenticated = React.memo(() => { detectionUnauthenticated: { icon: 'documents', label: i18n.GO_TO_DOCUMENTATION, - url: `${docLinks.ELASTIC_WEBSITE_URL}guide/en/security/${docLinks.DOC_LINK_VERSION}/detections-permissions-section.html`, + url: `${docLinks.links.siem.detectionsReq}`, target: '_blank', }, }), diff --git a/x-pack/plugins/security_solution/public/management/common/routing.test.ts b/x-pack/plugins/security_solution/public/management/common/routing.test.ts index 82b7a15d642e40..e0e6ed2a080371 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.test.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.test.ts @@ -106,26 +106,28 @@ describe('routing', () => { }); it('builds proper path when only page size provided', () => { - expect(getTrustedAppsListPath({ page_size: 20 })).toEqual( - '/administration/trusted_apps?page_size=20' + const pageSize = 20; + expect(getTrustedAppsListPath({ page_size: pageSize })).toEqual( + `/administration/trusted_apps?page_size=${pageSize}` ); }); it('builds proper path when only page index provided', () => { - expect(getTrustedAppsListPath({ page_index: 2 })).toEqual( - '/administration/trusted_apps?page_index=2' + const pageIndex = 2; + expect(getTrustedAppsListPath({ page_index: pageIndex })).toEqual( + `/administration/trusted_apps?page_index=${pageIndex}` ); }); it('builds proper path when only "show" provided', () => { - expect(getTrustedAppsListPath({ show: 'create' })).toEqual( - '/administration/trusted_apps?show=create' - ); + const show = 'create'; + expect(getTrustedAppsListPath({ show })).toEqual(`/administration/trusted_apps?show=${show}`); }); it('builds proper path when only view type provided', () => { - expect(getTrustedAppsListPath({ view_type: 'list' })).toEqual( - '/administration/trusted_apps?view_type=list' + const viewType = 'list'; + expect(getTrustedAppsListPath({ view_type: viewType })).toEqual( + `/administration/trusted_apps?view_type=${viewType}` ); }); @@ -135,56 +137,82 @@ describe('routing', () => { page_size: 20, show: 'create', view_type: 'list', - filter: '', + filter: 'test', + included_policies: 'globally', + excluded_policies: 'unassigned', }; expect(getTrustedAppsListPath(location)).toEqual( - '/administration/trusted_apps?page_index=2&page_size=20&view_type=list&show=create' + `/administration/trusted_apps?page_index=${location.page_index}&page_size=${location.page_size}&view_type=${location.view_type}&show=${location.show}&filter=${location.filter}&included_policies=${location.included_policies}&excluded_policies=${location.excluded_policies}` ); }); it('builds proper path when page index is equal to default', () => { - const path = getTrustedAppsListPath({ + const location: TrustedAppsListPageLocation = { page_index: MANAGEMENT_DEFAULT_PAGE, page_size: 20, show: 'create', view_type: 'list', - }); + filter: '', + included_policies: '', + excluded_policies: '', + }; + const path = getTrustedAppsListPath(location); - expect(path).toEqual('/administration/trusted_apps?page_size=20&view_type=list&show=create'); + expect(path).toEqual( + `/administration/trusted_apps?page_size=${location.page_size}&view_type=${location.view_type}&show=${location.show}` + ); }); it('builds proper path when page size is equal to default', () => { - const path = getTrustedAppsListPath({ + const location: TrustedAppsListPageLocation = { page_index: 2, page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, show: 'create', view_type: 'list', - }); + filter: '', + included_policies: '', + excluded_policies: '', + }; + const path = getTrustedAppsListPath(location); - expect(path).toEqual('/administration/trusted_apps?page_index=2&view_type=list&show=create'); + expect(path).toEqual( + `/administration/trusted_apps?page_index=${location.page_index}&view_type=${location.view_type}&show=${location.show}` + ); }); it('builds proper path when "show" is equal to default', () => { - const path = getTrustedAppsListPath({ + const location: TrustedAppsListPageLocation = { page_index: 2, page_size: 20, show: undefined, view_type: 'list', - }); + filter: '', + included_policies: '', + excluded_policies: '', + }; + const path = getTrustedAppsListPath(location); - expect(path).toEqual('/administration/trusted_apps?page_index=2&page_size=20&view_type=list'); + expect(path).toEqual( + `/administration/trusted_apps?page_index=${location.page_index}&page_size=${location.page_size}&view_type=${location.view_type}` + ); }); it('builds proper path when view type is equal to default', () => { - const path = getTrustedAppsListPath({ + const location: TrustedAppsListPageLocation = { page_index: 2, page_size: 20, show: 'create', view_type: 'grid', - }); + filter: '', + included_policies: '', + excluded_policies: '', + }; + const path = getTrustedAppsListPath(location); - expect(path).toEqual('/administration/trusted_apps?page_index=2&page_size=20&show=create'); + expect(path).toEqual( + `/administration/trusted_apps?page_index=${location.page_index}&page_size=${location.page_size}&show=${location.show}` + ); }); it('builds proper path when params are equal to default', () => { diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index e6c02ffefa3c51..d044fc0f1f2f60 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -140,6 +140,12 @@ const normalizeTrustedAppsPageLocation = ( ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}), ...(!isDefaultOrMissing(location.id, undefined) ? { id: location.id } : {}), ...(!isDefaultOrMissing(location.filter, '') ? { filter: location.filter } : ''), + ...(!isDefaultOrMissing(location.included_policies, '') + ? { included_policies: location.included_policies } + : ''), + ...(!isDefaultOrMissing(location.excluded_policies, '') + ? { excluded_policies: location.excluded_policies } + : ''), }; } else { return {}; @@ -196,12 +202,26 @@ const extractFilter = (query: querystring.ParsedUrlQuery): string => { return extractFirstParamValue(query, 'filter') || ''; }; +const extractIncludedPolicies = (query: querystring.ParsedUrlQuery): string => { + return extractFirstParamValue(query, 'included_policies') || ''; +}; + +const extractExcludedPolicies = (query: querystring.ParsedUrlQuery): string => { + return extractFirstParamValue(query, 'excluded_policies') || ''; +}; + export const extractListPaginationParams = (query: querystring.ParsedUrlQuery) => ({ page_index: extractPageIndex(query), page_size: extractPageSize(query), filter: extractFilter(query), }); +export const extractTrustedAppsListPaginationParams = (query: querystring.ParsedUrlQuery) => ({ + ...extractListPaginationParams(query), + included_policies: extractIncludedPolicies(query), + excluded_policies: extractExcludedPolicies(query), +}); + export const extractTrustedAppsListPageLocation = ( query: querystring.ParsedUrlQuery ): TrustedAppsListPageLocation => { @@ -211,7 +231,7 @@ export const extractTrustedAppsListPageLocation = ( ) as TrustedAppsListPageLocation['show']; return { - ...extractListPaginationParams(query), + ...extractTrustedAppsListPaginationParams(query), view_type: extractFirstParamValue(query, 'view_type') === 'list' ? 'list' : 'grid', show: showParamValue && ['edit', 'create'].includes(showParamValue) ? showParamValue : undefined, diff --git a/x-pack/plugins/security_solution/public/management/common/utils.ts b/x-pack/plugins/security_solution/public/management/common/utils.ts index 616e395c8ad479..3fbe5662f338c8 100644 --- a/x-pack/plugins/security_solution/public/management/common/utils.ts +++ b/x-pack/plugins/security_solution/public/management/common/utils.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { isEmpty } from 'lodash/fp'; + export const parseQueryFilterToKQL = (filter: string, fields: Readonly): string => { if (!filter) return ''; const kuery = fields @@ -19,3 +21,31 @@ export const parseQueryFilterToKQL = (filter: string, fields: Readonly return `(${kuery})`; }; + +const getPolicyQuery = (policyId: string): string => { + if (policyId === 'global') return 'exception-list-agnostic.attributes.tags:"policy:all"'; + if (policyId === 'unassigned') return '(not exception-list-agnostic.attributes.tags:*)'; + return `exception-list-agnostic.attributes.tags:"policy:${policyId}"`; +}; + +export const parsePoliciesToKQL = (includedPolicies: string, excludedPolicies: string): string => { + if (isEmpty(includedPolicies) && isEmpty(excludedPolicies)) return ''; + + const parsedIncludedPolicies = includedPolicies ? includedPolicies.split(',') : undefined; + const parsedExcludedPolicies = excludedPolicies ? excludedPolicies.split(',') : undefined; + + const includedPoliciesKuery = parsedIncludedPolicies + ? parsedIncludedPolicies.map(getPolicyQuery).join(' OR ') + : ''; + + const excludedPoliciesKuery = parsedExcludedPolicies + ? parsedExcludedPolicies.map((policyId) => `not ${getPolicyQuery(policyId)}`).join(' AND ') + : ''; + + const kuery = []; + + if (includedPoliciesKuery) kuery.push(includedPoliciesKuery); + if (excludedPoliciesKuery) kuery.push(excludedPoliciesKuery); + + return `(${kuery.join(' AND ')})`; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/management_empty_state.tsx b/x-pack/plugins/security_solution/public/management/components/management_empty_state.tsx index a3b1597377fa09..33e2980dcc1124 100644 --- a/x-pack/plugins/security_solution/public/management/components/management_empty_state.tsx +++ b/x-pack/plugins/security_solution/public/management/components/management_empty_state.tsx @@ -24,6 +24,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import onboardingLogo from '../images/security_administration_onboarding.svg'; +import { useKibana } from '../../common/lib/kibana'; const TEXT_ALIGN_CENTER: CSSProperties = Object.freeze({ textAlign: 'center', @@ -44,6 +45,7 @@ const PolicyEmptyState = React.memo<{ onActionClick: (event: MouseEvent) => void; actionDisabled?: boolean; }>(({ loading, onActionClick, actionDisabled }) => { + const docLinks = useKibana().services.docLinks; return (
{loading ? ( @@ -83,7 +85,7 @@ const PolicyEmptyState = React.memo<{ id="xpack.securitySolution.endpoint.policyList.onboardingSectionThree" defaultMessage="To get started, add the Endpoint Security integration to your Agents. For more information, " /> - + { + let getElement: (params: Partial) => RenderResult; + beforeEach(() => { + onChangeSelectionMock = jest.fn(); + useIsExperimentalFeatureEnabledMock.mockReturnValue(false); + getElement = (params: Partial) => { + return render( + + + + ); + }; + }); + const generator = new EndpointDocGenerator('policy-list'); + const policy = generator.generatePolicyPackagePolicy(); + policy.name = 'test policy A'; + policy.id = 'abc123'; + + describe('When click on policy', () => { + it('should have a default value', () => { + const defaultIncludedPolicies = 'abc123'; + const defaultExcludedPolicies = 'global'; + const element = getElement({ defaultExcludedPolicies, defaultIncludedPolicies }); + act(() => { + fireEvent.click(element.getByTestId('policiesSelectorButton')); + }); + expect(element.getByText(policy.name)).toHaveTextContent(policy.name); + act(() => { + fireEvent.click(element.getByText('Unassigned entries')); + }); + expect(onChangeSelectionMock).toHaveBeenCalledWith([ + { checked: 'on', id: 'abc123', name: 'test policy A' }, + { checked: 'off', id: 'global', name: 'Global entries' }, + { checked: 'on', id: 'unassigned', name: 'Unassigned entries' }, + ]); + }); + + it('should disable enabled default value', () => { + useIsExperimentalFeatureEnabledMock.mockReturnValue(true); + const defaultIncludedPolicies = 'abc123'; + const defaultExcludedPolicies = 'global'; + const element = getElement({ defaultExcludedPolicies, defaultIncludedPolicies }); + act(() => { + fireEvent.click(element.getByTestId('policiesSelectorButton')); + }); + act(() => { + fireEvent.click(element.getByText(policy.name)); + }); + expect(onChangeSelectionMock).toHaveBeenCalledWith([ + { checked: 'off', id: 'abc123', name: 'test policy A' }, + { checked: 'off', id: 'global', name: 'Global entries' }, + { checked: undefined, id: 'unassigned', name: 'Unassigned entries' }, + ]); + }); + + it('should remove disabled default value', () => { + const defaultIncludedPolicies = 'abc123'; + const defaultExcludedPolicies = 'global'; + const element = getElement({ defaultExcludedPolicies, defaultIncludedPolicies }); + act(() => { + fireEvent.click(element.getByTestId('policiesSelectorButton')); + }); + act(() => { + fireEvent.click(element.getByText('Global entries')); + }); + expect(onChangeSelectionMock).toHaveBeenCalledWith([ + { checked: 'on', id: 'abc123', name: 'test policy A' }, + { checked: undefined, id: 'global', name: 'Global entries' }, + { checked: undefined, id: 'unassigned', name: 'Unassigned entries' }, + ]); + }); + }); + + describe('When filter policy', () => { + it('should filter policy by name', () => { + const element = getElement({}); + act(() => { + fireEvent.click(element.getByTestId('policiesSelectorButton')); + }); + act(() => { + fireEvent.change(element.getByTestId('policiesSelectorSearch'), { + target: { value: policy.name }, + }); + }); + expect(element.queryAllByText('Global entries')).toStrictEqual([]); + expect(element.getByText(policy.name)).toHaveTextContent(policy.name); + }); + it('should filter with no results', () => { + const element = getElement({}); + act(() => { + fireEvent.click(element.getByTestId('policiesSelectorButton')); + }); + act(() => { + fireEvent.change(element.getByTestId('policiesSelectorSearch'), { + target: { value: 'no results' }, + }); + }); + expect(element.queryAllByText('Global entries')).toStrictEqual([]); + expect(element.queryAllByText('Unassigned entries')).toStrictEqual([]); + expect(element.queryAllByText(policy.name)).toStrictEqual([]); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/components/policies_selector/policies_selector.tsx b/x-pack/plugins/security_solution/public/management/components/policies_selector/policies_selector.tsx new file mode 100644 index 00000000000000..b86c8f6de7abd1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/policies_selector/policies_selector.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useCallback, useMemo, useState, useEffect, ChangeEvent } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFilterGroup, + EuiPopover, + EuiPopoverTitle, + EuiFieldSearch, + EuiFilterButton, + EuiFilterSelectItem, + FilterChecked, + EuiText, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ImmutableArray, PolicyData } from '../../../../common/endpoint/types'; +import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; + +export interface PoliciesSelectorProps { + policies: ImmutableArray; + defaultIncludedPolicies?: string; + defaultExcludedPolicies?: string; + onChangeSelection: (items: PolicySelectionItem[]) => void; +} + +export interface PolicySelectionItem { + name: string; + id?: string; + checked?: FilterChecked; +} + +interface DefaultPoliciesByKey { + [key: string]: boolean; +} + +const GLOBAL_ENTRIES = i18n.translate( + 'xpack.securitySolution.management.policiesSelector.globalEntries', + { + defaultMessage: 'Global entries', + } +); +const UNASSIGNED_ENTRIES = i18n.translate( + 'xpack.securitySolution.management.policiesSelector.unassignedEntries', + { + defaultMessage: 'Unassigned entries', + } +); + +export const PoliciesSelector = memo( + ({ policies, onChangeSelection, defaultExcludedPolicies, defaultIncludedPolicies }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [query, setQuery] = useState(''); + const [itemsList, setItemsList] = useState([]); + + const isExcludePoliciesInFilterEnabled = useIsExperimentalFeatureEnabled( + 'excludePoliciesInFilterEnabled' + ); + + useEffect(() => { + const defaultIncludedPoliciesByKey: DefaultPoliciesByKey = defaultIncludedPolicies + ? defaultIncludedPolicies.split(',').reduce((acc, val) => ({ ...acc, [val]: true }), {}) + : {}; + + const defaultExcludedPoliciesByKey: DefaultPoliciesByKey = defaultExcludedPolicies + ? defaultExcludedPolicies.split(',').reduce((acc, val) => ({ ...acc, [val]: true }), {}) + : {}; + + const getCheckedValue = (id: string): FilterChecked | undefined => + defaultIncludedPoliciesByKey[id] + ? 'on' + : defaultExcludedPoliciesByKey[id] + ? 'off' + : undefined; + + setItemsList([ + ...policies.map((policy) => ({ + name: policy.name, + id: policy.id, + checked: getCheckedValue(policy.id), + })), + { name: GLOBAL_ENTRIES, id: 'global', checked: getCheckedValue('global') }, + { name: UNASSIGNED_ENTRIES, id: 'unassigned', checked: getCheckedValue('unassigned') }, + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [policies]); + + const onButtonClick = useCallback(() => { + setIsPopoverOpen((prevIsPopoverOpen) => !prevIsPopoverOpen); + }, []); + + const closePopover = useCallback(() => { + setIsPopoverOpen(false); + }, []); + + const onChange = useCallback((ev: ChangeEvent) => { + const value = ev.target.value || ''; + setQuery(value); + }, []); + + const updateItem = useCallback( + (index: number) => { + if (!itemsList[index]) { + return; + } + + const newItems = [...itemsList]; + + switch (newItems[index].checked) { + case 'on': + newItems[index].checked = isExcludePoliciesInFilterEnabled ? 'off' : undefined; + break; + + case 'off': + newItems[index].checked = undefined; + break; + + default: + newItems[index].checked = 'on'; + } + + setItemsList(newItems); + onChangeSelection(newItems); + }, + [itemsList, onChangeSelection, isExcludePoliciesInFilterEnabled] + ); + + const dropdownItems = useMemo( + () => + itemsList.map((item, index) => + item.name.match(new RegExp(query, 'i')) ? ( + updateItem(index)} + > + {item.name} + + ) : null + ), + [itemsList, query, updateItem] + ); + + const button = useMemo( + () => ( + item.checked === 'on')} + numActiveFilters={itemsList.filter((item) => item.checked === 'on').length} + > + + + + + ), + [isPopoverOpen, itemsList, onButtonClick] + ); + + return ( + + + + + + + +
{dropdownItems}
+
+
+
+
+ ); + } +); + +PoliciesSelector.displayName = 'PoliciesSelector'; diff --git a/x-pack/plugins/security_solution/public/management/components/search_bar/index.tsx b/x-pack/plugins/security_solution/public/management/components/search_bar/index.tsx deleted file mode 100644 index 5ace2b901da11c..00000000000000 --- a/x-pack/plugins/security_solution/public/management/components/search_bar/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { memo, useCallback, useState } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiButton } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -export interface SearchBarProps { - defaultValue?: string; - placeholder: string; - onSearch(value: string): void; -} - -export const SearchBar = memo(({ defaultValue = '', onSearch, placeholder }) => { - const [query, setQuery] = useState(defaultValue); - - const handleOnChangeSearchField = useCallback( - (ev: React.ChangeEvent) => setQuery(ev.target.value), - [setQuery] - ); - const handleOnSearch = useCallback(() => onSearch(query), [query, onSearch]); - - return ( - - - - - - - {i18n.translate('xpack.securitySolution.management.search.button', { - defaultMessage: 'Refresh', - })} - - - - ); -}); - -SearchBar.displayName = 'SearchBar'; diff --git a/x-pack/plugins/apm/common/ui_settings_keys.ts b/x-pack/plugins/security_solution/public/management/components/search_exceptions/index.ts similarity index 76% rename from x-pack/plugins/apm/common/ui_settings_keys.ts rename to x-pack/plugins/security_solution/public/management/components/search_exceptions/index.ts index 427c30605e71b8..6a870dbb06c66c 100644 --- a/x-pack/plugins/apm/common/ui_settings_keys.ts +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export const enableServiceOverview = 'apm:enableServiceOverview'; +export { SearchExceptions, SearchExceptionsProps } from './search_exceptions'; diff --git a/x-pack/plugins/security_solution/public/management/components/search_bar/index.test.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx similarity index 89% rename from x-pack/plugins/security_solution/public/management/components/search_bar/index.test.tsx rename to x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx index 707a96938655a7..5c909e062ceb9c 100644 --- a/x-pack/plugins/security_solution/public/management/components/search_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx @@ -8,7 +8,7 @@ import { mount } from 'enzyme'; import React from 'react'; -import { SearchBar } from '.'; +import { SearchExceptions } from '.'; let onSearchMock: jest.Mock; @@ -16,13 +16,17 @@ interface EuiFieldSearchPropsFake { onSearch(value: string): void; } -describe('Search bar', () => { +describe('Search exceptions', () => { beforeEach(() => { onSearchMock = jest.fn(); }); const getElement = (defaultValue: string = '') => ( - + ); it('should have a default value', () => { @@ -45,7 +49,7 @@ describe('Search bar', () => { searchFieldProps.onSearch(expectedDefaultValue); expect(onSearchMock).toHaveBeenCalledTimes(1); - expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue); + expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue, '', ''); }); it('should dispatch search action when click on button', () => { @@ -55,6 +59,6 @@ describe('Search bar', () => { element.find('[data-test-subj="searchButton"]').first().simulate('click'); expect(onSearchMock).toHaveBeenCalledTimes(1); - expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue); + expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue, '', ''); }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx new file mode 100644 index 00000000000000..a737b53e2d9b9a --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useCallback, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { PolicySelectionItem, PoliciesSelector } from '../policies_selector'; +import { ImmutableArray, PolicyData } from '../../../../common/endpoint/types'; + +export interface SearchExceptionsProps { + defaultValue?: string; + placeholder: string; + hasPolicyFilter?: boolean; + policyList?: ImmutableArray; + defaultExcludedPolicies?: string; + defaultIncludedPolicies?: string; + onSearch(query: string, includedPolicies?: string, excludedPolicies?: string): void; +} + +export const SearchExceptions = memo( + ({ + defaultValue = '', + onSearch, + placeholder, + hasPolicyFilter, + policyList, + defaultIncludedPolicies, + defaultExcludedPolicies, + }) => { + const [query, setQuery] = useState(defaultValue); + const [includedPolicies, setIncludedPolicies] = useState(defaultIncludedPolicies || ''); + const [excludedPolicies, setExcludedPolicies] = useState(defaultExcludedPolicies || ''); + + const onChangeSelection = useCallback( + (items: PolicySelectionItem[]) => { + const includePoliciesNew = items + .filter((item) => item.checked === 'on') + .map((item) => item.id) + .join(','); + const excludePoliciesNew = items + .filter((item) => item.checked === 'off') + .map((item) => item.id) + .join(','); + + setIncludedPolicies(includePoliciesNew); + setExcludedPolicies(excludePoliciesNew); + + onSearch(query, includePoliciesNew, excludePoliciesNew); + }, + [onSearch, query] + ); + + const handleOnChangeSearchField = useCallback( + (ev: React.ChangeEvent) => setQuery(ev.target.value), + [setQuery] + ); + const handleOnSearch = useCallback(() => onSearch(query, includedPolicies, excludedPolicies), [ + onSearch, + query, + includedPolicies, + excludedPolicies, + ]); + + const handleOnSearchQuery = useCallback( + (value) => { + onSearch(value, includedPolicies, excludedPolicies); + }, + [onSearch, includedPolicies, excludedPolicies] + ); + + return ( + + + + + {hasPolicyFilter && policyList ? ( + + + + ) : null} + + + + {i18n.translate('xpack.securitySolution.management.search.button', { + defaultMessage: 'Refresh', + })} + + + + ); + } +); + +SearchExceptions.displayName = 'SearchExceptions'; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx index 95f3e856a6ff6d..3c537320bc92af 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx @@ -42,7 +42,7 @@ import { } from '../../../../common/components/exceptions/viewer/exception_item'; import { EventFilterDeleteModal } from './components/event_filter_delete_modal'; -import { SearchBar } from '../../../components/search_bar'; +import { SearchExceptions } from '../../../components/search_exceptions'; import { BackToExternalAppButton } from '../../../components/back_to_external_app_button'; import { ABOUT_EVENT_FILTERS } from './translations'; @@ -226,7 +226,7 @@ export const EventFiltersListPage = memo(() => { {doesDataExist && ( <> - ({ id: undefined, view_type: 'grid', filter: '', + included_policies: '', + excluded_policies: '', }, active: false, forceRefresh: false, diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts index 9624987c8af567..f64003ec6ad910 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts @@ -26,6 +26,7 @@ import { initialTrustedAppsPageState } from './builders'; import { trustedAppsPageReducer } from './reducer'; import { createTrustedAppsPageMiddleware } from './middleware'; import { Immutable } from '../../../../../common/endpoint/types'; +import { getGeneratedPolicyResponse } from './mocks'; const initialNow = 111111; const dateNowMock = jest.fn(); @@ -189,8 +190,10 @@ describe('middleware', () => { const location = createLocationState(); const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); + const policiesResponse = getGeneratedPolicyResponse(); service.getTrustedAppsList.mockResolvedValue(createGetTrustedListAppsResponse(pagination)); + service.getPolicyList.mockResolvedValue(policiesResponse); store.dispatch(createUserChangedUrlAction('/administration/trusted_apps')); @@ -215,10 +218,15 @@ describe('middleware', () => { }); await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); + await spyMiddleware.waitForAction('trustedAppsPoliciesStateChanged'); expect(store.getState()).toStrictEqual({ ...initialState, ...entriesExistLoadedState(), + policies: { + data: policiesResponse, + type: 'LoadedResourceState', + }, listView: createLoadedListViewWithPagination(newNow, pagination), active: true, location, @@ -304,9 +312,11 @@ describe('middleware', () => { it('submits successfully when entry is defined', async () => { const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); + const policiesResponse = getGeneratedPolicyResponse(); service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); service.deleteTrustedApp.mockResolvedValue(); + service.getPolicyList.mockResolvedValue(policiesResponse); store.dispatch(createUserChangedUrlAction('/administration/trusted_apps')); @@ -335,6 +345,10 @@ describe('middleware', () => { expect(store.getState()).toStrictEqual({ ...testStartState, ...entriesExistLoadedState(), + policies: { + data: policiesResponse, + type: 'LoadedResourceState', + }, listView: listViewNew, }); expect(service.deleteTrustedApp).toBeCalledWith({ id: '3' }); @@ -344,9 +358,11 @@ describe('middleware', () => { it('does not submit twice', async () => { const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); + const policiesResponse = getGeneratedPolicyResponse(); service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); service.deleteTrustedApp.mockResolvedValue(); + service.getPolicyList.mockResolvedValue(policiesResponse); store.dispatch(createUserChangedUrlAction('/administration/trusted_apps')); @@ -376,6 +392,10 @@ describe('middleware', () => { expect(store.getState()).toStrictEqual({ ...testStartState, ...entriesExistLoadedState(), + policies: { + data: policiesResponse, + type: 'LoadedResourceState', + }, listView: listViewNew, }); expect(service.deleteTrustedApp).toBeCalledWith({ id: '3' }); @@ -385,9 +405,11 @@ describe('middleware', () => { it('does not submit when server response with failure', async () => { const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); + const policiesResponse = getGeneratedPolicyResponse(); service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); service.deleteTrustedApp.mockRejectedValue({ body: notFoundError }); + service.getPolicyList.mockResolvedValue(policiesResponse); store.dispatch(createUserChangedUrlAction('/administration/trusted_apps')); @@ -409,10 +431,15 @@ describe('middleware', () => { }); await spyMiddleware.waitForAction('trustedAppDeletionSubmissionResourceStateChanged'); + await spyMiddleware.waitForAction('trustedAppsPoliciesStateChanged'); expect(store.getState()).toStrictEqual({ ...testStartState, ...entriesExistLoadedState(), + policies: { + data: policiesResponse, + type: 'LoadedResourceState', + }, deletionDialog: { entry, confirmed: true, diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts index da6394a9ab8969..cf7cff30d6d6d1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts @@ -26,6 +26,7 @@ import { isLoadedResourceState, isLoadingResourceState, isStaleResourceState, + isUninitialisedResourceState, StaleResourceState, TrustedAppsListData, TrustedAppsListPageState, @@ -63,8 +64,10 @@ import { editingTrustedApp, getListItems, editItemState, + getCurrentLocationIncludedPolicies, + getCurrentLocationExcludedPolicies, } from './selectors'; -import { parseQueryFilterToKQL } from '../../../common/utils'; +import { parsePoliciesToKQL, parseQueryFilterToKQL } from '../../../common/utils'; import { toUpdateTrustedApp } from '../../../../../common/endpoint/service/trusted_apps/to_update_trusted_app'; import { SEARCHABLE_FIELDS } from '../constants'; @@ -95,11 +98,21 @@ const refreshListIfNeeded = async ( const pageIndex = getCurrentLocationPageIndex(store.getState()); const pageSize = getCurrentLocationPageSize(store.getState()); const filter = getCurrentLocationFilter(store.getState()); + const includedPolicies = getCurrentLocationIncludedPolicies(store.getState()); + const excludedPolicies = getCurrentLocationExcludedPolicies(store.getState()); + + const kuery = []; + + const filterKuery = parseQueryFilterToKQL(filter, SEARCHABLE_FIELDS) || undefined; + if (filterKuery) kuery.push(filterKuery); + + const policiesKuery = parsePoliciesToKQL(includedPolicies, excludedPolicies) || undefined; + if (policiesKuery) kuery.push(policiesKuery); const response = await trustedAppsService.getTrustedAppsList({ page: pageIndex + 1, per_page: pageSize, - kuery: parseQueryFilterToKQL(filter, SEARCHABLE_FIELDS) || undefined, + kuery: kuery.join(' AND ') || undefined, }); store.dispatch( @@ -112,6 +125,8 @@ const refreshListIfNeeded = async ( totalItemsCount: response.total, timestamp: Date.now(), filter, + includedPolicies, + excludedPolicies, }, }) ); @@ -311,8 +326,9 @@ export const retrieveListOfPoliciesIfNeeded = async ( const isLoading = isLoadingResourceState(currentPoliciesState); const isPageActive = trustedAppsListPageActive(currentState); const isCreateFlow = isCreationDialogLocation(currentState); + const isUninitialized = isUninitialisedResourceState(currentPoliciesState); - if (isPageActive && isCreateFlow && !isLoading) { + if (isPageActive && ((isCreateFlow && !isLoading) || isUninitialized)) { dispatch({ type: 'trustedAppsPoliciesStateChanged', payload: { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/mocks.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/mocks.ts new file mode 100644 index 00000000000000..c97dd37db6bbf0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/mocks.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { GetPolicyListResponse } from '../../policy/types'; + +import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; + +export const getGeneratedPolicyResponse = (): GetPolicyListResponse => ({ + items: [new EndpointDocGenerator('seed').generatePolicyPackagePolicy()], + total: 1, + perPage: 1, + page: 1, +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts index ac4d29a6016b2a..71bea620306760 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts @@ -31,7 +31,7 @@ describe('reducer', () => { initialState, createUserChangedUrlAction( '/administration/trusted_apps', - '?page_index=5&page_size=50&show=create&view_type=list&filter=test' + '?page_index=5&page_size=50&show=create&view_type=list&filter=test&included_policies=global&excluded_policies=unassigned' ) ); @@ -44,6 +44,8 @@ describe('reducer', () => { view_type: 'list', id: undefined, filter: 'test', + included_policies: 'global', + excluded_policies: 'unassigned', }, active: true, }); @@ -53,7 +55,14 @@ describe('reducer', () => { const result = trustedAppsPageReducer( { ...initialState, - location: { page_index: 5, page_size: 50, view_type: 'grid', filter: '' }, + location: { + page_index: 5, + page_size: 50, + view_type: 'grid', + filter: '', + included_policies: '', + excluded_policies: '', + }, }, createUserChangedUrlAction( '/administration/trusted_apps', @@ -68,7 +77,14 @@ describe('reducer', () => { const result = trustedAppsPageReducer( { ...initialState, - location: { page_index: 5, page_size: 50, view_type: 'grid', filter: '' }, + location: { + page_index: 5, + page_size: 50, + view_type: 'grid', + filter: '', + included_policies: '', + excluded_policies: '', + }, }, createUserChangedUrlAction('/administration/trusted_apps') ); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts index 4d8ce097a72633..387a354fe38c4a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts @@ -94,6 +94,8 @@ describe('selectors', () => { page_size: 10, view_type: 'grid', filter: '', + included_policies: '', + excluded_policies: '', }; expect(needsRefreshOfListData({ ...initialState, listView, active: true, location })).toBe( @@ -176,6 +178,8 @@ describe('selectors', () => { page_size: 10, view_type: 'grid', filter: '', + included_policies: '', + excluded_policies: '', }; expect(getCurrentLocationPageIndex({ ...initialState, location })).toBe(3); @@ -189,6 +193,8 @@ describe('selectors', () => { page_size: 20, view_type: 'grid', filter: '', + included_policies: '', + excluded_policies: '', }; expect(getCurrentLocationPageSize({ ...initialState, location })).toBe(20); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts index 338f30b447a8a7..388d8b5e6ed66d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts @@ -74,6 +74,18 @@ export const getCurrentLocationFilter = (state: Immutable +): string => { + return state.location.included_policies; +}; + +export const getCurrentLocationExcludedPolicies = ( + state: Immutable +): string => { + return state.location.excluded_policies; +}; + export const getListTotalItemsCount = (state: Immutable): number => { return getLastLoadedResourceState(state.listView.listResourceState)?.data.totalItemsCount || 0; }; @@ -188,6 +200,14 @@ export const entriesExist: (state: Immutable) => boole } ); +export const prevEntriesExist: ( + state: Immutable +) => boolean = createSelector(entriesExistState, (doEntriesExists) => { + return ( + isLoadingResourceState(doEntriesExists) && !!getLastLoadedResourceState(doEntriesExists)?.data + ); +}); + export const trustedAppsListPageActive: (state: Immutable) => boolean = ( state ) => state.active; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts index 7783ac96e192d4..cad8f606f85bba 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts @@ -80,6 +80,8 @@ export const createTrustedAppsListData = ( totalItemsCount: fullPagination.totalItemCount, timestamp, filter: '', + excludedPolicies: '', + includedPolicies: '', }; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index 5c627d1d7a8373..2ba357a349b5db 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -173,9 +173,9 @@ describe('When on the Trusted Apps Page', () => { expect(addButton.textContent).toBe('Add Trusted Application'); }); - it('should display the searchbar', async () => { + it('should display the searchExceptions', async () => { const renderResult = await renderWithListData(); - expect(await renderResult.findByTestId('searchBar')).not.toBeNull(); + expect(await renderResult.findByTestId('searchExceptions')).not.toBeNull(); }); describe('and the Grid view is being displayed', () => { @@ -774,6 +774,20 @@ describe('When on the Trusted Apps Page', () => { return releaseListResponse(); } } + + if (path === PACKAGE_POLICY_API_ROUTES.LIST_PATTERN) { + const policy = generator.generatePolicyPackagePolicy(); + policy.name = 'test policy A'; + policy.id = 'abc123'; + + const response: GetPackagePoliciesResponse = { + items: [policy], + page: 1, + perPage: 1000, + total: 1, + }; + return response; + } if (priorMockImplementation) { return priorMockImplementation(path); } @@ -874,12 +888,12 @@ describe('When on the Trusted Apps Page', () => { expect(await renderResult.findByTestId('trustedAppEmptyState')).not.toBeNull(); }); - it('should not display the searchbar', async () => { + it('should not display the searchExceptions', async () => { const renderResult = render(); await act(async () => { await waitForAction('trustedAppsExistStateChanged'); }); - expect(renderResult.queryByTestId('searchBar')).toBeNull(); + expect(renderResult.queryByTestId('searchExceptions')).toBeNull(); }); }); @@ -922,6 +936,27 @@ describe('When on the Trusted Apps Page', () => { backButtonUrl: '/fleet', }); }); + + const priorMockImplementation = coreStart.http.get.getMockImplementation(); + // @ts-ignore + coreStart.http.get.mockImplementation((path, options) => { + if (path === PACKAGE_POLICY_API_ROUTES.LIST_PATTERN) { + const policy = generator.generatePolicyPackagePolicy(); + policy.name = 'test policy A'; + policy.id = 'abc123'; + + const response: GetPackagePoliciesResponse = { + items: [policy], + page: 1, + perPage: 1000, + total: 1, + }; + return response; + } + if (priorMockImplementation) { + return priorMockImplementation(path); + } + }); }); it('back button is present', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx index ec80b4c5ae21b5..48ff54a0e3b56f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx @@ -26,6 +26,8 @@ import { entriesExist, getCurrentLocation, getListTotalItemsCount, + listOfPolicies, + prevEntriesExist, } from '../store/selectors'; import { useTrustedAppsNavigateCallback, useTrustedAppsSelector } from './hooks'; import { AdministrationListPage } from '../../../components/administration_list_page'; @@ -38,18 +40,31 @@ import { TrustedAppsNotifications } from './trusted_apps_notifications'; import { AppAction } from '../../../../common/store/actions'; import { ABOUT_TRUSTED_APPS, SEARCH_TRUSTED_APP_PLACEHOLDER } from './translations'; import { EmptyState } from './components/empty_state'; -import { SearchBar } from '../../../components/search_bar'; +import { SearchExceptions } from '../../../components/search_exceptions'; import { BackToExternalAppButton } from '../../../components/back_to_external_app_button'; import { ListPageRouteState } from '../../../../../common/endpoint/types'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; export const TrustedAppsPage = memo(() => { + const isTrustedAppsByPolicyEnabled = useIsExperimentalFeatureEnabled( + 'trustedAppsByPolicyEnabled' + ); const dispatch = useDispatch>(); const { state: routeState } = useLocation(); const location = useTrustedAppsSelector(getCurrentLocation); const totalItemsCount = useTrustedAppsSelector(getListTotalItemsCount); const isCheckingIfEntriesExists = useTrustedAppsSelector(checkingIfEntriesExist); - const doEntriesExist = useTrustedAppsSelector(entriesExist) === true; - const navigationCallback = useTrustedAppsNavigateCallback((query: string) => ({ filter: query })); + const policyList = useTrustedAppsSelector(listOfPolicies); + const doEntriesExist = useTrustedAppsSelector(entriesExist); + const didEntriesExist = useTrustedAppsSelector(prevEntriesExist); + const navigationCallbackQuery = useTrustedAppsNavigateCallback( + (query: string, includedPolicies?: string, excludedPolicies?: string) => ({ + filter: query, + included_policies: includedPolicies, + excluded_policies: excludedPolicies, + }) + ); + const handleAddButtonClick = useTrustedAppsNavigateCallback(() => ({ show: 'create', id: undefined, @@ -61,12 +76,13 @@ export const TrustedAppsPage = memo(() => { const handleViewTypeChange = useTrustedAppsNavigateCallback((viewType: ViewType) => ({ view_type: viewType, })); + const handleOnSearch = useCallback( - (query: string) => { + (query: string, includedPolicies?: string, excludedPolicies?: string) => { dispatch({ type: 'trustedAppForceRefresh', payload: { forceRefresh: true } }); - navigationCallback(query); + navigationCallbackQuery(query, includedPolicies, excludedPolicies); }, - [dispatch, navigationCallback] + [dispatch, navigationCallbackQuery] ); const showCreateFlyout = !!location.show; @@ -105,12 +121,16 @@ export const TrustedAppsPage = memo(() => { /> )} - {doEntriesExist ? ( + {doEntriesExist || (isCheckingIfEntriesExists && didEntriesExist) ? ( <> - { > - {isCheckingIfEntriesExists ? ( + {isCheckingIfEntriesExists && !didEntriesExist ? ( } diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx index 4c738958e9be87..63a4571ca11e52 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx @@ -32,7 +32,12 @@ jest.mock('../../../common/lib/kibana', () => { embeddable: { EmbeddablePanel: jest.fn(() =>
), }, - docLinks: { ELASTIC_WEBSITE_URL: 'ELASTIC_WEBSITE_URL' }, + docLinks: { + ELASTIC_WEBSITE_URL: 'ELASTIC_WEBSITE_URL', + links: { + siem: { networkMap: '' }, + }, + }, }, }), }; diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx index 6e36b85e3819ca..f2cb974a5b5c1a 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx @@ -221,10 +221,7 @@ export const EmbeddedMapComponent = ({ - + {i18n.EMBEDDABLE_HEADER_HELP} diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/index_patterns_missing_prompt.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/index_patterns_missing_prompt.tsx index 43e57e519e2388..b0e2e6cd7f1862 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/index_patterns_missing_prompt.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/index_patterns_missing_prompt.tsx @@ -39,7 +39,7 @@ export const IndexPatternsMissingPromptComponent = () => { ), beats: ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/rule_registry_dapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/rule_registry_adapter.ts similarity index 76% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/rule_registry_dapter.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/rule_registry_adapter.ts index 3f56b26d32a097..90574528a9338d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/rule_registry_dapter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/rule_registry_adapter.ts @@ -6,17 +6,18 @@ */ import { merge } from 'lodash'; -import { RuleDataPluginService } from '../../../../../../rule_registry/server'; import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { IRuleStatusSOAttributes } from '../../rules/types'; import { RuleRegistryLogClient } from '../rule_registry_log_client/rule_registry_log_client'; import { + CreateExecutionLogArgs, ExecutionMetric, ExecutionMetricArgs, FindBulkExecutionLogArgs, FindExecutionLogArgs, + IRuleDataPluginService, IRuleExecutionLogClient, LogStatusChangeArgs, + UpdateExecutionLogArgs, } from '../types'; /** @@ -25,7 +26,7 @@ import { export class RuleRegistryAdapter implements IRuleExecutionLogClient { private ruleRegistryClient: RuleRegistryLogClient; - constructor(ruleDataService: RuleDataPluginService) { + constructor(ruleDataService: IRuleDataPluginService) { this.ruleRegistryClient = new RuleRegistryLogClient(ruleDataService); } @@ -58,37 +59,45 @@ export class RuleRegistryAdapter implements IRuleExecutionLogClient { return merge(statusesById, lastErrorsById); } - public async create(event: IRuleStatusSOAttributes, spaceId: string) { - if (event.status) { + public async create({ attributes, spaceId }: CreateExecutionLogArgs) { + if (attributes.status) { await this.ruleRegistryClient.logStatusChange({ - ruleId: event.alertId, - newStatus: event.status, + ruleId: attributes.alertId, + newStatus: attributes.status, spaceId, }); } - if (event.bulkCreateTimeDurations) { + if (attributes.bulkCreateTimeDurations) { await this.ruleRegistryClient.logExecutionMetric({ - ruleId: event.alertId, + ruleId: attributes.alertId, metric: ExecutionMetric.indexingDurationMax, - value: Math.max(...event.bulkCreateTimeDurations.map(Number)), + value: Math.max(...attributes.bulkCreateTimeDurations.map(Number)), spaceId, }); } - if (event.gap) { + if (attributes.gap) { await this.ruleRegistryClient.logExecutionMetric({ - ruleId: event.alertId, + ruleId: attributes.alertId, metric: ExecutionMetric.executionGap, - value: Number(event.gap), + value: Number(attributes.gap), spaceId, }); } + + return { + id: '', + type: '', + score: 0, + attributes, + references: [], + }; } - public async update(id: string, event: IRuleStatusSOAttributes, spaceId: string) { + public async update({ attributes, spaceId }: UpdateExecutionLogArgs) { // execution events are immutable, so we just use 'create' here instead of 'update' - await this.create(event, spaceId); + await this.create({ attributes, spaceId }); } public async delete(id: string) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/saved_objects_adapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/saved_objects_adapter.ts index 55f65caf34b037..444e11dc5b9f09 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/saved_objects_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/adapters/saved_objects_adapter.ts @@ -6,18 +6,19 @@ */ import { SavedObjectsClientContract } from '../../../../../../../../src/core/server'; -import { IRuleStatusSOAttributes } from '../../rules/types'; import { RuleStatusSavedObjectsClient, ruleStatusSavedObjectsClientFactory, } from '../../signals/rule_status_saved_objects_client'; import { + CreateExecutionLogArgs, ExecutionMetric, ExecutionMetricArgs, FindBulkExecutionLogArgs, FindExecutionLogArgs, IRuleExecutionLogClient, LogStatusChangeArgs, + UpdateExecutionLogArgs, } from '../types'; export class SavedObjectsAdapter implements IRuleExecutionLogClient { @@ -41,12 +42,12 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { return this.ruleStatusClient.findBulk(ruleIds, logsCount); } - public async create(event: IRuleStatusSOAttributes) { - await this.ruleStatusClient.create(event); + public async create({ attributes }: CreateExecutionLogArgs) { + return this.ruleStatusClient.create(attributes); } - public async update(id: string, event: IRuleStatusSOAttributes) { - await this.ruleStatusClient.update(id, event); + public async update({ id, attributes }: UpdateExecutionLogArgs) { + await this.ruleStatusClient.update(id, attributes); } public async delete(id: string) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts index 286238b292cb71..26b36c367bda68 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts @@ -6,21 +6,22 @@ */ import { SavedObjectsClientContract } from '../../../../../../../src/core/server'; -import { RuleDataPluginService } from '../../../../../rule_registry/server'; -import { IRuleStatusSOAttributes } from '../rules/types'; -import { RuleRegistryAdapter } from './adapters/rule_registry_dapter'; +import { RuleRegistryAdapter } from './adapters/rule_registry_adapter'; import { SavedObjectsAdapter } from './adapters/saved_objects_adapter'; import { + CreateExecutionLogArgs, ExecutionMetric, ExecutionMetricArgs, FindBulkExecutionLogArgs, FindExecutionLogArgs, + IRuleDataPluginService, IRuleExecutionLogClient, LogStatusChangeArgs, + UpdateExecutionLogArgs, } from './types'; export interface RuleExecutionLogClientArgs { - ruleDataService: RuleDataPluginService; + ruleDataService: IRuleDataPluginService; savedObjectsClient: SavedObjectsClientContract; } @@ -45,14 +46,12 @@ export class RuleExecutionLogClient implements IRuleExecutionLogClient { return this.client.findBulk(args); } - // TODO args as an object - public async create(event: IRuleStatusSOAttributes, spaceId: string) { - return this.client.create(event, spaceId); + public async create(args: CreateExecutionLogArgs) { + return this.client.create(args); } - // TODO args as an object - public async update(id: string, event: IRuleStatusSOAttributes, spaceId: string) { - return this.client.update(id, event, spaceId); + public async update(args: UpdateExecutionLogArgs) { + return this.client.update(args); } public async delete(id: string) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_execution_log_bootstrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_execution_log_bootstrapper.ts index 9b13fbb1d21d66..2c10811b21c48e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_execution_log_bootstrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_execution_log_bootstrapper.ts @@ -7,14 +7,14 @@ import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../../../../../rule_registry/common/assets'; import { mappingFromFieldMap } from '../../../../../../rule_registry/common/mapping_from_field_map'; -import { RuleDataPluginService } from '../../../../../../rule_registry/server'; +import { IRuleDataPluginService } from '../types'; import { ruleExecutionFieldMap } from './rule_execution_field_map'; /** * @deprecated bootstrapRuleExecutionLog is kept here only as a reference. It will be superseded with EventLog implementation */ export const bootstrapRuleExecutionLog = async ( - ruleDataService: RuleDataPluginService, + ruleDataService: IRuleDataPluginService, indexAlias: string ) => { const indexPattern = `${indexAlias}*`; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_registry_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_registry_log_client.ts index 5094f9a8c6e3ce..e2c5c98702f12b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_registry_log_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_registry_log_client.ts @@ -6,20 +6,32 @@ */ import { estypes } from '@elastic/elasticsearch'; -import { EVENT_ACTION, EVENT_KIND, RULE_ID, SPACE_IDS, TIMESTAMP } from '@kbn/rule-data-utils'; +import { + ALERT_OWNER, + EVENT_ACTION, + EVENT_KIND, + RULE_ID, + SPACE_IDS, + TIMESTAMP, +} from '@kbn/rule-data-utils'; import { once } from 'lodash/fp'; import moment from 'moment'; -import { RuleDataClient, RuleDataPluginService } from '../../../../../../rule_registry/server'; +import { RuleDataClient } from '../../../../../../rule_registry/server'; import { SERVER_APP_ID } from '../../../../../common/constants'; import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas'; import { invariant } from '../../../../../common/utils/invariant'; import { IRuleStatusSOAttributes } from '../../rules/types'; import { makeFloatString } from '../../signals/utils'; -import { ExecutionMetric, ExecutionMetricArgs, LogStatusChangeArgs } from '../types'; +import { + ExecutionMetric, + ExecutionMetricArgs, + IRuleDataPluginService, + LogStatusChangeArgs, +} from '../types'; import { EVENTS_INDEX_PREFIX, - MESSAGE, EVENT_SEQUENCE, + MESSAGE, RULE_STATUS, RULE_STATUS_SEVERITY, } from './constants'; @@ -65,7 +77,7 @@ export class RuleRegistryLogClient implements IRuleRegistryLogClient { private sequence = 0; private ruleDataClient: RuleDataClient; - constructor(ruleDataService: RuleDataPluginService) { + constructor(ruleDataService: IRuleDataPluginService) { this.ruleDataClient = ruleDataService.getRuleDataClient( SERVER_APP_ID, EVENTS_INDEX_PREFIX, @@ -73,7 +85,7 @@ export class RuleRegistryLogClient implements IRuleRegistryLogClient { ); } - private initialize = once(async (ruleDataService: RuleDataPluginService, indexAlias: string) => { + private initialize = once(async (ruleDataService: IRuleDataPluginService, indexAlias: string) => { await bootstrapRuleExecutionLog(ruleDataService, indexAlias); }); @@ -216,6 +228,7 @@ export class RuleRegistryLogClient implements IRuleRegistryLogClient { [getMetricField(metric)]: value, [RULE_ID]: ruleId, [TIMESTAMP]: new Date().toISOString(), + [ALERT_OWNER]: 'siem', }, namespace ); @@ -239,6 +252,7 @@ export class RuleRegistryLogClient implements IRuleRegistryLogClient { [RULE_STATUS_SEVERITY]: statusSeverityDict[newStatus], [RULE_STATUS]: newStatus, [TIMESTAMP]: new Date().toISOString(), + [ALERT_OWNER]: 'siem', }, namespace ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts index ca589fd1d584fc..42b9a3bbd66cce 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts @@ -5,7 +5,9 @@ * 2.0. */ -import { SavedObjectsFindResult } from '../../../../../../../src/core/server'; +import { PublicMethodsOf } from '@kbn/utility-types'; +import { SavedObject, SavedObjectsFindResult } from '../../../../../../../src/core/server'; +import { RuleDataPluginService } from '../../../../../rule_registry/server'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; import { IRuleStatusSOAttributes } from '../rules/types'; @@ -16,6 +18,8 @@ export enum ExecutionMetric { 'indexingLookback' = 'indexingLookback', } +export type IRuleDataPluginService = PublicMethodsOf; + export type ExecutionMetricValue = { [ExecutionMetric.executionGap]: number; [ExecutionMetric.searchDurationMax]: number; @@ -43,6 +47,17 @@ export interface LogStatusChangeArgs { message?: string; } +export interface UpdateExecutionLogArgs { + id: string; + attributes: IRuleStatusSOAttributes; + spaceId: string; +} + +export interface CreateExecutionLogArgs { + attributes: IRuleStatusSOAttributes; + spaceId: string; +} + export interface ExecutionMetricArgs { ruleId: string; spaceId: string; @@ -60,8 +75,8 @@ export interface IRuleExecutionLogClient { args: FindExecutionLogArgs ) => Promise>>; findBulk: (args: FindBulkExecutionLogArgs) => Promise; - create: (event: IRuleStatusSOAttributes, spaceId: string) => Promise; - update: (id: string, event: IRuleStatusSOAttributes, spaceId: string) => Promise; + create: (args: CreateExecutionLogArgs) => Promise>; + update: (args: UpdateExecutionLogArgs) => Promise; delete: (id: string) => Promise; // TODO These methods are intended to supersede ones provided by RuleStatusService logStatusChange: (args: LogStatusChangeArgs) => Promise; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/with_rule_execution_log.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/with_rule_execution_log.ts index 95598ea943d8f3..a78001ee4f674c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/with_rule_execution_log.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/with_rule_execution_log.ts @@ -11,10 +11,10 @@ import { AlertTypeParams, AlertTypeState, } from '../../../../../alerting/common'; -import { AlertTypeWithExecutor, RuleDataPluginService } from '../../../../../rule_registry/server'; +import { AlertTypeWithExecutor } from '../../../../../rule_registry/server'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; import { RuleExecutionLogClient } from './rule_execution_log_client'; -import { IRuleExecutionLogClient } from './types'; +import { IRuleDataPluginService, IRuleExecutionLogClient } from './types'; export interface ExecutionLogServices { ruleExecutionLogClient: IRuleExecutionLogClient; @@ -23,7 +23,7 @@ export interface ExecutionLogServices { type WithRuleExecutionLog = (args: { logger: Logger; - ruleDataService: RuleDataPluginService; + ruleDataService: IRuleDataPluginService; }) => < TState extends AlertTypeState, TParams extends AlertTypeParams, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts index 60dcbb3a4e77e7..0fa2bcc270a167 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts @@ -17,6 +17,7 @@ import { ConfigType } from '../../../../config'; import { AlertAttributes } from '../../signals/types'; import { createRuleMock } from './rule'; import { listMock } from '../../../../../../lists/server/mocks'; +import { ruleRegistryMocks } from '../../../../../../rule_registry/server/mocks'; export const createRuleTypeMocks = () => { /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -85,6 +86,7 @@ export const createRuleTypeMocks = () => { })), isWriteEnabled: jest.fn(() => true), } as unknown) as RuleDataClient, + ruleDataService: ruleRegistryMocks.createRuleDataPluginService(), }, services, scheduleActions, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_factory.ts index 71d922ed543c18..168502d120b8fb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_factory.ts @@ -12,7 +12,6 @@ import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; import { ListArray } from '@kbn/securitysolution-io-ts-list-types'; import { toError } from '@kbn/securitysolution-list-api'; import { createPersistenceRuleTypeFactory } from '../../../../../rule_registry/server'; -import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_saved_objects_client'; import { ruleStatusServiceFactory } from '../signals/rule_status_service'; import { buildRuleMessageFactory } from './factories/build_rule_message_factory'; import { @@ -33,6 +32,7 @@ import { import { getNotificationResultsLink } from '../notifications/utils'; import { createResultObject } from './utils'; import { bulkCreateFactory, wrapHitsFactory } from './factories'; +import { RuleExecutionLogClient } from '../rule_execution_log/rule_execution_log_client'; /* eslint-disable complexity */ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = ({ @@ -41,6 +41,7 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = ({ logger, mergeStrategy, ruleDataClient, + ruleDataService, }) => (type) => { const persistenceRuleType = createPersistenceRuleTypeFactory({ ruleDataClient, logger }); return persistenceRuleType({ @@ -62,8 +63,9 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = ({ const esClient = scopedClusterClient.asCurrentUser; - const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); + const ruleStatusClient = new RuleExecutionLogClient({ savedObjectsClient, ruleDataService }); const ruleStatusService = await ruleStatusServiceFactory({ + spaceId, alertId, ruleStatusClient, }); @@ -189,8 +191,10 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = ({ ); const wrapHits = wrapHitsFactory({ - ruleSO, + logger, mergeStrategy, + ruleSO, + spaceId, }); for (const tuple of tuples) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts new file mode 100644 index 00000000000000..f9874478e7a5d3 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.test.ts @@ -0,0 +1,419 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ALERT_OWNER, + ALERT_RULE_NAMESPACE, + ALERT_STATUS, + ALERT_WORKFLOW_STATUS, + SPACE_IDS, +} from '@kbn/rule-data-utils'; + +import { sampleDocNoSortIdWithTimestamp } from '../../../signals/__mocks__/es_results'; +import { flattenWithPrefix } from './flatten_with_prefix'; +import { + buildAlert, + buildParent, + buildAncestors, + additionalAlertFields, + removeClashes, +} from './build_alert'; +import { Ancestor, SignalSourceHit } from '../../../signals/types'; +import { + getRulesSchemaMock, + ANCHOR_DATE, +} from '../../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; +import { getListArrayMock } from '../../../../../../common/detection_engine/schemas/types/lists.mock'; +import { + ALERT_ANCESTORS, + ALERT_ORIGINAL_EVENT, + ALERT_ORIGINAL_TIME, +} from '../../field_maps/field_names'; +import { SERVER_APP_ID } from '../../../../../../common/constants'; + +type SignalDoc = SignalSourceHit & { + _source: Required['_source'] & { '@timestamp': string }; +}; + +const SPACE_ID = 'space'; + +describe('buildAlert', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('it builds an alert as expected without original_event if event does not exist', () => { + const doc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + delete doc._source.event; + const rule = getRulesSchemaMock(); + const alert = { + ...buildAlert([doc], rule, SPACE_ID), + ...additionalAlertFields(doc), + }; + const timestamp = alert['@timestamp']; + const expected = { + '@timestamp': timestamp, + [SPACE_IDS]: [SPACE_ID], + [ALERT_OWNER]: SERVER_APP_ID, + [ALERT_ANCESTORS]: [ + { + id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + ], + [ALERT_ORIGINAL_TIME]: '2020-04-20T21:27:45.000Z', + [ALERT_STATUS]: 'open', + [ALERT_WORKFLOW_STATUS]: 'open', + ...flattenWithPrefix(ALERT_RULE_NAMESPACE, { + author: [], + id: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9', + created_at: new Date(ANCHOR_DATE).toISOString(), + updated_at: new Date(ANCHOR_DATE).toISOString(), + created_by: 'elastic', + description: 'some description', + enabled: true, + false_positives: ['false positive 1', 'false positive 2'], + from: 'now-6m', + immutable: false, + name: 'Query with a rule id', + query: 'user.name: root or user.name: admin', + references: ['test 1', 'test 2'], + severity: 'high', + severity_mapping: [], + updated_by: 'elastic_kibana', + tags: ['some fake tag 1', 'some fake tag 2'], + to: 'now', + type: 'query', + threat: [], + version: 1, + status: 'succeeded', + status_date: '2020-02-22T16:47:50.047Z', + last_success_at: '2020-02-22T16:47:50.047Z', + last_success_message: 'succeeded', + output_index: '.siem-signals-default', + max_signals: 100, + risk_score: 55, + risk_score_mapping: [], + language: 'kuery', + rule_id: 'query-rule-id', + interval: '5m', + exceptions_list: getListArrayMock(), + }), + 'kibana.alert.depth': 1, + }; + expect(alert).toEqual(expected); + }); + + test('it builds an alert as expected with original_event if is present', () => { + const doc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + doc._source.event = { + action: 'socket_opened', + dataset: 'socket', + kind: 'event', + module: 'system', + }; + const rule = getRulesSchemaMock(); + const alert = { + ...buildAlert([doc], rule, SPACE_ID), + ...additionalAlertFields(doc), + }; + const timestamp = alert['@timestamp']; + const expected = { + '@timestamp': timestamp, + [SPACE_IDS]: [SPACE_ID], + [ALERT_OWNER]: SERVER_APP_ID, + [ALERT_ANCESTORS]: [ + { + id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + ], + [ALERT_ORIGINAL_TIME]: '2020-04-20T21:27:45.000Z', + [ALERT_ORIGINAL_EVENT]: { + action: 'socket_opened', + dataset: 'socket', + kind: 'event', + module: 'system', + }, + [ALERT_STATUS]: 'open', + [ALERT_WORKFLOW_STATUS]: 'open', + ...flattenWithPrefix(ALERT_RULE_NAMESPACE, { + author: [], + id: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9', + created_at: new Date(ANCHOR_DATE).toISOString(), + updated_at: new Date(ANCHOR_DATE).toISOString(), + created_by: 'elastic', + description: 'some description', + enabled: true, + false_positives: ['false positive 1', 'false positive 2'], + from: 'now-6m', + immutable: false, + name: 'Query with a rule id', + query: 'user.name: root or user.name: admin', + references: ['test 1', 'test 2'], + severity: 'high', + severity_mapping: [], + updated_by: 'elastic_kibana', + tags: ['some fake tag 1', 'some fake tag 2'], + to: 'now', + type: 'query', + threat: [], + version: 1, + status: 'succeeded', + status_date: '2020-02-22T16:47:50.047Z', + last_success_at: '2020-02-22T16:47:50.047Z', + last_success_message: 'succeeded', + output_index: '.siem-signals-default', + max_signals: 100, + risk_score: 55, + risk_score_mapping: [], + language: 'kuery', + rule_id: 'query-rule-id', + interval: '5m', + exceptions_list: getListArrayMock(), + }), + 'kibana.alert.depth': 1, + }; + expect(alert).toEqual(expected); + }); + + test('it builds an ancestor correctly if the parent does not exist', () => { + const doc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + doc._source.event = { + action: 'socket_opened', + dataset: 'socket', + kind: 'event', + module: 'system', + }; + const parent = buildParent(doc); + const expected: Ancestor = { + id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }; + expect(parent).toEqual(expected); + }); + + test('it builds an ancestor correctly if the parent does exist', () => { + const doc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + doc._source.event = { + action: 'socket_opened', + dataset: 'socket', + kind: 'event', + module: 'system', + }; + doc._source.signal = { + parents: [ + { + id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + ], + ancestors: [ + { + id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + ], + depth: 1, + rule: { + id: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', + }, + }; + const parent = buildParent(doc); + const expected: Ancestor = { + rule: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', + id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', + type: 'signal', + index: 'myFakeSignalIndex', + depth: 1, + }; + expect(parent).toEqual(expected); + }); + + test('it builds an alert ancestor correctly if the parent does not exist', () => { + const sampleDoc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + const doc: SignalDoc = { + ...sampleDoc, + _source: { + ...sampleDoc._source, + '@timestamp': new Date().toISOString(), + }, + }; + doc._source.event = { + action: 'socket_opened', + dataset: 'socket', + kind: 'event', + module: 'system', + }; + const ancestor = buildAncestors(doc); + const expected: Ancestor[] = [ + { + id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + ]; + expect(ancestor).toEqual(expected); + }); + + test('it builds an alert ancestor correctly if the parent does exist', () => { + const sampleDoc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + const doc: SignalDoc = { + ...sampleDoc, + _source: { + ...sampleDoc._source, + '@timestamp': new Date().toISOString(), + }, + }; + doc._source.event = { + action: 'socket_opened', + dataset: 'socket', + kind: 'event', + module: 'system', + }; + doc._source.signal = { + parents: [ + { + id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + ], + ancestors: [ + { + id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + ], + rule: { + id: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', + }, + depth: 1, + }; + const ancestors = buildAncestors(doc); + const expected: Ancestor[] = [ + { + id: '730ddf9e-5a00-4f85-9ddf-5878ca511a87', + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + { + rule: '98c0bf9e-4d38-46f4-9a6a-8a820426256b', + id: 'd5e8eb51-a6a0-456d-8a15-4b79bfec3d71', + type: 'signal', + index: 'myFakeSignalIndex', + depth: 1, + }, + ]; + expect(ancestors).toEqual(expected); + }); + + describe('removeClashes', () => { + test('it will call renameClashes with a regular doc and not mutate it if it does not have a signal clash', () => { + const sampleDoc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + const doc: SignalDoc = { + ...sampleDoc, + _source: { + ...sampleDoc._source, + '@timestamp': new Date().toISOString(), + }, + }; + const output = removeClashes(doc); + expect(output).toBe(doc); // reference check + }); + + test('it will call renameClashes with a regular doc and not change anything', () => { + const sampleDoc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + const doc: SignalDoc = { + ...sampleDoc, + _source: { + ...sampleDoc._source, + '@timestamp': new Date().toISOString(), + }, + }; + const output = removeClashes(doc); + expect(output).toEqual(doc); // deep equal check + }); + + test('it will remove a "signal" numeric clash', () => { + const sampleDoc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + const doc = ({ + ...sampleDoc, + _source: { + ...sampleDoc._source, + signal: 127, + }, + } as unknown) as SignalDoc; + const output = removeClashes(doc); + const timestamp = output._source['@timestamp']; + expect(output).toEqual({ + ...sampleDoc, + _source: { + ...sampleDoc._source, + '@timestamp': timestamp, + }, + }); + }); + + test('it will remove a "signal" object clash', () => { + const sampleDoc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + const doc = ({ + ...sampleDoc, + _source: { + ...sampleDoc._source, + signal: { child_1: { child_2: 'Test nesting' } }, + }, + } as unknown) as SignalDoc; + const output = removeClashes(doc); + const timestamp = output._source['@timestamp']; + expect(output).toEqual({ + ...sampleDoc, + _source: { + ...sampleDoc._source, + '@timestamp': timestamp, + }, + }); + }); + + test('it will not remove a "signal" if that is signal is one of our signals', () => { + const sampleDoc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71'); + const doc = ({ + ...sampleDoc, + _source: { + ...sampleDoc._source, + signal: { rule: { id: '123' } }, + }, + } as unknown) as SignalDoc; + const output = removeClashes(doc); + const timestamp = output._source['@timestamp']; + const expected = { + ...sampleDoc, + _source: { + ...sampleDoc._source, + signal: { rule: { id: '123' } }, + '@timestamp': timestamp, + }, + }; + expect(output).toEqual(expected); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts index fbd033a7f4ec48..641b37cb54bc40 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts @@ -5,124 +5,113 @@ * 2.0. */ -import { ALERT_STATUS, ALERT_WORKFLOW_STATUS } from '@kbn/rule-data-utils'; -import { SearchTypes } from '../../../../../../common/detection_engine/types'; +import { + ALERT_OWNER, + ALERT_RULE_NAMESPACE, + ALERT_STATUS, + ALERT_WORKFLOW_STATUS, + SPACE_IDS, +} from '@kbn/rule-data-utils'; import { RulesSchema } from '../../../../../../common/detection_engine/schemas/response/rules_schema'; import { isEventTypeSignal } from '../../../signals/build_event_type_signal'; +import { Ancestor, BaseSignalHit, SimpleHit } from '../../../signals/types'; import { - Ancestor, - BaseSignalHit, - SignalHit, - SignalSourceHit, - ThresholdResult, -} from '../../../signals/types'; -import { getValidDateFromDoc } from '../../../signals/utils'; + getField, + getValidDateFromDoc, + isWrappedRACAlert, + isWrappedSignalHit, +} from '../../../signals/utils'; import { invariant } from '../../../../../../common/utils/invariant'; -import { DEFAULT_MAX_SIGNALS } from '../../../../../../common/constants'; +import { RACAlert } from '../../types'; +import { flattenWithPrefix } from './flatten_with_prefix'; +import { + ALERT_ANCESTORS, + ALERT_DEPTH, + ALERT_ORIGINAL_EVENT, + ALERT_ORIGINAL_TIME, +} from '../../field_maps/field_names'; +import { SERVER_APP_ID } from '../../../../../../common/constants'; /** - * Takes a parent signal or event document and extracts the information needed for the corresponding entry in the child - * signal's `signal.parents` array. - * @param doc The parent signal or event + * Takes an event document and extracts the information needed for the corresponding entry in the child + * alert's ancestors array. + * @param doc The parent event */ -export const buildParent = (doc: BaseSignalHit): Ancestor => { - if (doc._source?.signal != null) { - return { - rule: doc._source?.signal.rule.id, - id: doc._id, - type: 'signal', - index: doc._index, - // We first look for signal.depth and use that if it exists. If it doesn't exist, this should be a pre-7.10 signal - // and should have signal.parent.depth instead. signal.parent.depth in this case is treated as equivalent to signal.depth. - depth: doc._source?.signal.depth ?? doc._source?.signal.parent?.depth ?? 1, - }; - } else { - return { - id: doc._id, - type: 'event', - index: doc._index, - depth: 0, - }; +export const buildParent = (doc: SimpleHit): Ancestor => { + const isSignal: boolean = isWrappedSignalHit(doc) || isWrappedRACAlert(doc); + const parent: Ancestor = { + id: doc._id, + type: isSignal ? 'signal' : 'event', + index: doc._index, + depth: isSignal ? getField(doc, 'signal.depth') ?? 1 : 0, + }; + if (isSignal) { + parent.rule = getField(doc, 'signal.rule.id'); } + return parent; }; /** - * Takes a parent signal or event document with N ancestors and adds the parent document to the ancestry array, + * Takes a parent event document with N ancestors and adds the parent document to the ancestry array, * creating an array of N+1 ancestors. - * @param doc The parent signal/event for which to extend the ancestry. + * @param doc The parent event for which to extend the ancestry. */ -export const buildAncestors = (doc: BaseSignalHit): Ancestor[] => { +export const buildAncestors = (doc: SimpleHit): Ancestor[] => { const newAncestor = buildParent(doc); - const existingAncestors = doc._source?.signal?.ancestors; - if (existingAncestors != null) { - return [...existingAncestors, newAncestor]; - } else { - return [newAncestor]; - } + const existingAncestors: Ancestor[] = getField(doc, 'signal.ancestors') ?? []; + return [...existingAncestors, newAncestor]; }; /** - * This removes any signal name clashes such as if a source index has + * This removes any alert name clashes such as if a source index has * "signal" but is not a signal object we put onto the object. If this * is our "signal object" then we don't want to remove it. * @param doc The source index doc to a signal. */ -export const removeClashes = (doc: BaseSignalHit): BaseSignalHit => { - invariant(doc._source, '_source field not found'); - const { signal, ...noSignal } = doc._source; - if (signal == null || isEventTypeSignal(doc)) { - return doc; - } else { - return { - ...doc, - _source: { ...noSignal }, - }; +export const removeClashes = (doc: SimpleHit) => { + if (isWrappedSignalHit(doc)) { + invariant(doc._source, '_source field not found'); + const { signal, ...noSignal } = doc._source; + if (signal == null || isEventTypeSignal(doc)) { + return doc; + } else { + return { + ...doc, + _source: { ...noSignal }, + }; + } } + return doc; }; /** - * Builds the `signal.*` fields that are common across all signals. - * @param docs The parent signals/events of the new signal to be built. - * @param rule The rule that is generating the new signal. + * Builds the `kibana.alert.*` fields that are common across all alerts. + * @param docs The parent alerts/events of the new alert to be built. + * @param rule The rule that is generating the new alert. */ -export const buildAlert = (doc: SignalSourceHit, rule: RulesSchema) => { - const removedClashes = removeClashes(doc); - const parent = buildParent(removedClashes); - const ancestors = buildAncestors(removedClashes); - const immutable = doc._source?.signal?.rule.immutable ? 'true' : 'false'; - - const source = doc._source as SignalHit; - const signal = source?.signal; - const signalRule = signal?.rule; +export const buildAlert = ( + docs: SimpleHit[], + rule: RulesSchema, + spaceId: string | null | undefined +): RACAlert => { + const removedClashes = docs.map(removeClashes); + const parents = removedClashes.map(buildParent); + const depth = parents.reduce((acc, parent) => Math.max(parent.depth, acc), 0) + 1; + const ancestors = removedClashes.reduce( + (acc: Ancestor[], doc) => acc.concat(buildAncestors(doc)), + [] + ); - return { - 'kibana.alert.ancestors': ancestors as object[], + return ({ + '@timestamp': new Date().toISOString(), + [ALERT_OWNER]: SERVER_APP_ID, + [SPACE_IDS]: spaceId != null ? [spaceId] : [], + [ALERT_ANCESTORS]: ancestors, [ALERT_STATUS]: 'open', [ALERT_WORKFLOW_STATUS]: 'open', - 'kibana.alert.depth': parent.depth, - 'kibana.alert.rule.false_positives': signalRule?.false_positives ?? [], - 'kibana.alert.rule.id': rule.id, - 'kibana.alert.rule.immutable': immutable, - 'kibana.alert.rule.index': signalRule?.index ?? [], - 'kibana.alert.rule.language': signalRule?.language ?? 'kuery', - 'kibana.alert.rule.max_signals': signalRule?.max_signals ?? DEFAULT_MAX_SIGNALS, - 'kibana.alert.rule.query': signalRule?.query ?? '*:*', - 'kibana.alert.rule.saved_id': signalRule?.saved_id ?? '', - 'kibana.alert.rule.threat_index': signalRule?.threat_index, - 'kibana.alert.rule.threat_indicator_path': signalRule?.threat_indicator_path, - 'kibana.alert.rule.threat_language': signalRule?.threat_language, - 'kibana.alert.rule.threat_mapping.field': '', // TODO - 'kibana.alert.rule.threat_mapping.value': '', // TODO - 'kibana.alert.rule.threat_mapping.type': '', // TODO - 'kibana.alert.rule.threshold.field': signalRule?.threshold?.field, - 'kibana.alert.rule.threshold.value': signalRule?.threshold?.value, - 'kibana.alert.rule.threshold.cardinality.field': '', // TODO - 'kibana.alert.rule.threshold.cardinality.value': 0, // TODO - }; -}; - -const isThresholdResult = (thresholdResult: SearchTypes): thresholdResult is ThresholdResult => { - return typeof thresholdResult === 'object'; + [ALERT_DEPTH]: depth, + ...flattenWithPrefix(ALERT_RULE_NAMESPACE, rule), + } as unknown) as RACAlert; }; /** @@ -131,17 +120,16 @@ const isThresholdResult = (thresholdResult: SearchTypes): thresholdResult is Thr * @param doc The parent signal/event of the new signal to be built. */ export const additionalAlertFields = (doc: BaseSignalHit) => { - const thresholdResult = doc._source?.threshold_result; - if (thresholdResult != null && !isThresholdResult(thresholdResult)) { - throw new Error(`threshold_result failed to validate: ${thresholdResult}`); - } const originalTime = getValidDateFromDoc({ doc, timestampOverride: undefined, }); - return { - 'kibana.alert.original_time': originalTime != null ? originalTime.toISOString() : undefined, - 'kibana.alert.original_event': doc._source?.event ?? undefined, - 'kibana.alert.threshold_result': thresholdResult, + const additionalFields: Record = { + [ALERT_ORIGINAL_TIME]: originalTime != null ? originalTime.toISOString() : undefined, }; + const event = doc._source?.event; + if (event != null) { + additionalFields[ALERT_ORIGINAL_EVENT] = event; + } + return additionalFields; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts index 8c868ece26cebf..ca5857e0ee3958 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts @@ -6,14 +6,21 @@ */ import { SavedObject } from 'src/core/types'; +import { BaseHit } from '../../../../../../common/detection_engine/types'; import type { ConfigType } from '../../../../../config'; -import { buildRuleWithOverrides } from '../../../signals/build_rule'; +import { buildRuleWithOverrides, buildRuleWithoutOverrides } from '../../../signals/build_rule'; import { getMergeStrategy } from '../../../signals/source_fields_merging/strategies'; -import { AlertAttributes, SignalSourceHit } from '../../../signals/types'; +import { AlertAttributes, SignalSource, SignalSourceHit } from '../../../signals/types'; import { RACAlert } from '../../types'; import { additionalAlertFields, buildAlert } from './build_alert'; import { filterSource } from './filter_source'; +const isSourceDoc = ( + hit: SignalSourceHit +): hit is BaseHit<{ '@timestamp': string; _source: SignalSource }> => { + return hit._source != null; +}; + /** * Formats the search_after result for insertion into the signals index. We first create a * "best effort" merged "fields" with the "_source" object, then build the signal object, @@ -24,17 +31,25 @@ import { filterSource } from './filter_source'; * @returns The body that can be added to a bulk call for inserting the signal. */ export const buildBulkBody = ( + spaceId: string | null | undefined, ruleSO: SavedObject, doc: SignalSourceHit, - mergeStrategy: ConfigType['alertMergeStrategy'] + mergeStrategy: ConfigType['alertMergeStrategy'], + applyOverrides: boolean ): RACAlert => { const mergedDoc = getMergeStrategy(mergeStrategy)({ doc }); - const rule = buildRuleWithOverrides(ruleSO, mergedDoc._source ?? {}); + const rule = applyOverrides + ? buildRuleWithOverrides(ruleSO, mergedDoc._source ?? {}) + : buildRuleWithoutOverrides(ruleSO); const filteredSource = filterSource(mergedDoc); - return { - ...filteredSource, - ...buildAlert(mergedDoc, rule), - ...additionalAlertFields(mergedDoc), - '@timestamp': new Date().toISOString(), - }; + if (isSourceDoc(mergedDoc)) { + return { + ...filteredSource, + ...buildAlert([mergedDoc], rule, spaceId), + ...additionalAlertFields(mergedDoc), + '@timestamp': new Date().toISOString(), + }; + } + + throw Error('Error building alert from source document.'); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/flatten_with_prefix.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/flatten_with_prefix.ts new file mode 100644 index 00000000000000..d472dc5885e57d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/flatten_with_prefix.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SearchTypes } from '../../../../../../common/detection_engine/types'; + +export const flattenWithPrefix = ( + prefix: string, + obj: Record +): Record => { + return Object.keys(obj).reduce((acc: Record, key) => { + return { + ...acc, + [`${prefix}.${key}`]: obj[key], + }; + }, {}); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts index 620e599e7a4993..0b00b2f656379c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { Logger } from 'kibana/server'; + import { SearchAfterAndBulkCreateParams, SignalSourceHit, WrapHits } from '../../signals/types'; import { buildBulkBody } from './utils/build_bulk_body'; import { generateId } from '../../signals/utils'; @@ -13,24 +15,33 @@ import type { ConfigType } from '../../../../config'; import { WrappedRACAlert } from '../types'; export const wrapHitsFactory = ({ - ruleSO, + logger, mergeStrategy, + ruleSO, + spaceId, }: { + logger: Logger; ruleSO: SearchAfterAndBulkCreateParams['ruleSO']; mergeStrategy: ConfigType['alertMergeStrategy']; + spaceId: string | null | undefined; }): WrapHits => (events) => { - const wrappedDocs: WrappedRACAlert[] = events.flatMap((doc) => [ - { - _index: '', - _id: generateId( - doc._index, - doc._id, - String(doc._version), - ruleSO.attributes.params.ruleId ?? '' - ), - _source: buildBulkBody(ruleSO, doc as SignalSourceHit, mergeStrategy), - }, - ]); + try { + const wrappedDocs: WrappedRACAlert[] = events.flatMap((doc) => [ + { + _index: '', + _id: generateId( + doc._index, + doc._id, + String(doc._version), + ruleSO.attributes.params.ruleId ?? '' + ), + _source: buildBulkBody(spaceId, ruleSO, doc as SignalSourceHit, mergeStrategy, true), + }, + ]); - return filterDuplicateSignals(ruleSO.id, wrappedDocs, true); + return filterDuplicateSignals(ruleSO.id, wrappedDocs, true); + } catch (error) { + logger.error(error); + return []; + } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/alerts.ts index 244905329f8ca2..7ab998fe16074c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/alerts.ts @@ -43,6 +43,21 @@ export const alertsFieldMap: FieldMap = { array: false, required: true, }, + 'kibana.alert.group': { + type: 'object', + array: false, + required: false, + }, + 'kibana.alert.group.id': { + type: 'keyword', + array: false, + required: false, + }, + 'kibana.alert.group.index': { + type: 'keyword', + array: false, + required: false, + }, 'kibana.alert.original_event': { type: 'object', array: false, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/field_names.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/field_names.ts new file mode 100644 index 00000000000000..41b7e6b02c9c6d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/field_maps/field_names.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ALERT_NAMESPACE } from '@kbn/rule-data-utils'; + +export const ALERT_ANCESTORS = `${ALERT_NAMESPACE}.ancestors`; +export const ALERT_DEPTH = `${ALERT_NAMESPACE}.depth`; +export const ALERT_ORIGINAL_EVENT = `${ALERT_NAMESPACE}.original_event`; +export const ALERT_ORIGINAL_TIME = `${ALERT_NAMESPACE}.original_time`; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts index 0127477e4800ac..a5ecc92df8f27b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts @@ -41,6 +41,7 @@ describe('Custom query alerts', () => { logger: dependencies.logger, mergeStrategy: 'allFields', ruleDataClient: dependencies.ruleDataClient, + ruleDataService: dependencies.ruleDataService, version: '1.0.0', }); @@ -88,6 +89,7 @@ describe('Custom query alerts', () => { logger: dependencies.logger, mergeStrategy: 'allFields', ruleDataClient: dependencies.ruleDataClient, + ruleDataService: dependencies.ruleDataService, version: '1.0.0', }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts index 3321597d8268fb..c487d8c93119dd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts @@ -7,16 +7,14 @@ import { Logger } from '@kbn/logging'; import { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; - import { PersistenceServices, RuleDataClient } from '../../../../../../rule_registry/server'; import { QUERY_ALERT_TYPE_ID } from '../../../../../common/constants'; import { ExperimentalFeatures } from '../../../../../common/experimental_features'; import { ConfigType } from '../../../../config'; import { SetupPlugins } from '../../../../plugin'; - +import { IRuleDataPluginService } from '../../rule_execution_log/types'; import { queryRuleParams, QueryRuleParams } from '../../schemas/rule_schemas'; import { queryExecutor } from '../../signals/executors/query'; - import { createSecurityRuleTypeFactory } from '../create_security_rule_type_factory'; export const createQueryAlertType = (createOptions: { @@ -27,6 +25,7 @@ export const createQueryAlertType = (createOptions: { mergeStrategy: ConfigType['alertMergeStrategy']; ruleDataClient: RuleDataClient; version: string; + ruleDataService: IRuleDataPluginService; }) => { const { experimentalFeatures, @@ -36,6 +35,7 @@ export const createQueryAlertType = (createOptions: { mergeStrategy, ruleDataClient, version, + ruleDataService, } = createOptions; const createSecurityRuleType = createSecurityRuleTypeFactory({ indexAlias, @@ -43,6 +43,7 @@ export const createQueryAlertType = (createOptions: { logger, mergeStrategy, ruleDataClient, + ruleDataService, }); return createSecurityRuleType({ id: QUERY_ALERT_TYPE_ID, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index dc4cbba680b256..20fb7213086008 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -28,6 +28,7 @@ import { import { BaseHit } from '../../../../common/detection_engine/types'; import { ConfigType } from '../../../config'; import { SetupPlugins } from '../../../plugin'; +import { IRuleDataPluginService } from '../rule_execution_log/types'; import { RuleParams } from '../schemas/rule_schemas'; import { BuildRuleMessage } from '../signals/rule_messages'; import { AlertAttributes, BulkCreate, WrapHits } from '../signals/types'; @@ -96,6 +97,7 @@ export type CreateSecurityRuleTypeFactory = (options: { logger: Logger; mergeStrategy: ConfigType['alertMergeStrategy']; ruleDataClient: RuleDataClient; + ruleDataService: IRuleDataPluginService; }) => < TParams extends RuleParams & { index: string[] | undefined }, TAlertInstanceContext extends AlertInstanceContext, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts index b727596cd3b025..c6a5c003802428 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts @@ -42,13 +42,13 @@ export const enableRule = async ({ // set current status for this rule to be 'going to run' if (ruleCurrentStatus && ruleCurrentStatus.length > 0) { const currentStatusToDisable = ruleCurrentStatus[0]; - await ruleStatusClient.update( - currentStatusToDisable.id, - { + await ruleStatusClient.update({ + id: currentStatusToDisable.id, + attributes: { ...currentStatusToDisable.attributes, status: RuleExecutionStatus['going to run'], }, - spaceId - ); + spaceId, + }); } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts index 5f4a9f5f7a422d..ed93c41035dca3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -168,6 +168,22 @@ export const sampleDocNoSortId = ( sort: [], }); +export const sampleDocNoSortIdWithTimestamp = ( + someUuid: string = sampleIdGuid, + ip?: string +): SignalSourceHit & { + _source: Required['_source'] & { '@timestamp': string }; +} => { + const doc = sampleDocNoSortId(someUuid, ip); + return { + ...doc, + _source: { + ...doc._source, + '@timestamp': new Date().toISOString(), + }, + }; +}; + export const sampleDocSeverity = (severity?: unknown, fieldName?: string): SignalSourceHit => { const doc = { _index: 'myFakeSignalIndex', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_duplicate_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_duplicate_signals.ts index fb562a2d11f0a6..460cf6894a73cb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_duplicate_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/filter_duplicate_signals.ts @@ -8,37 +8,21 @@ import { WrappedRACAlert } from '../rule_types/types'; import { Ancestor, SimpleHit, WrappedSignalHit } from './types'; -const isWrappedSignalHit = ( - signals: SimpleHit[], - isRuleRegistryEnabled: boolean -): signals is WrappedSignalHit[] => { - return !isRuleRegistryEnabled; -}; - -const isWrappedRACAlert = ( - signals: SimpleHit[], - isRuleRegistryEnabled: boolean -): signals is WrappedRACAlert[] => { - return isRuleRegistryEnabled; -}; - export const filterDuplicateSignals = ( ruleId: string, signals: SimpleHit[], isRuleRegistryEnabled: boolean ) => { - if (isWrappedSignalHit(signals, isRuleRegistryEnabled)) { - return signals.filter( + if (!isRuleRegistryEnabled) { + return (signals as WrappedSignalHit[]).filter( (doc) => !doc._source.signal?.ancestors.some((ancestor) => ancestor.rule === ruleId) ); - } else if (isWrappedRACAlert(signals, isRuleRegistryEnabled)) { - return signals.filter( + } else { + return (signals as WrappedRACAlert[]).filter( (doc) => !(doc._source['kibana.alert.ancestors'] as Ancestor[]).some( (ancestor) => ancestor.rule === ruleId ) ); - } else { - return signals; } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.test.ts index 5058056b169a32..c8ef0093291d58 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.test.ts @@ -34,6 +34,7 @@ describe('get_input_output_index', () => { index: ['test-input-index-1'], experimentalFeatures: { trustedAppsByPolicyEnabled: false, + excludePoliciesInFilterEnabled: false, metricsEntitiesEnabled: false, ruleRegistryEnabled: false, tGridEnabled: false, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_or_create_rule_statuses.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_or_create_rule_statuses.ts index 8c4ffdb2a6c4a2..0390c073354a6e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_or_create_rule_statuses.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_or_create_rule_statuses.ts @@ -8,47 +8,54 @@ import { SavedObject } from 'src/core/server'; import { IRuleStatusSOAttributes } from '../rules/types'; -import { RuleStatusSavedObjectsClient } from './rule_status_saved_objects_client'; -import { getRuleStatusSavedObjects } from './get_rule_status_saved_objects'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; +import { IRuleExecutionLogClient } from '../rule_execution_log/types'; +import { MAX_RULE_STATUSES } from './rule_status_service'; interface RuleStatusParams { alertId: string; - ruleStatusClient: RuleStatusSavedObjectsClient; + spaceId: string; + ruleStatusClient: IRuleExecutionLogClient; } export const createNewRuleStatus = async ({ alertId, + spaceId, ruleStatusClient, }: RuleStatusParams): Promise> => { const now = new Date().toISOString(); return ruleStatusClient.create({ - alertId, - statusDate: now, - status: RuleExecutionStatus['going to run'], - lastFailureAt: null, - lastSuccessAt: null, - lastFailureMessage: null, - lastSuccessMessage: null, - gap: null, - bulkCreateTimeDurations: [], - searchAfterTimeDurations: [], - lastLookBackDate: null, + spaceId, + attributes: { + alertId, + statusDate: now, + status: RuleExecutionStatus['going to run'], + lastFailureAt: null, + lastSuccessAt: null, + lastFailureMessage: null, + lastSuccessMessage: null, + gap: null, + bulkCreateTimeDurations: [], + searchAfterTimeDurations: [], + lastLookBackDate: null, + }, }); }; export const getOrCreateRuleStatuses = async ({ + spaceId, alertId, ruleStatusClient, }: RuleStatusParams): Promise>> => { - const ruleStatuses = await getRuleStatusSavedObjects({ - alertId, - ruleStatusClient, + const ruleStatuses = await ruleStatusClient.find({ + spaceId, + ruleId: alertId, + logsCount: MAX_RULE_STATUSES, }); if (ruleStatuses.length > 0) { return ruleStatuses; } - const newStatus = await createNewRuleStatus({ alertId, ruleStatusClient }); + const newStatus = await createNewRuleStatus({ alertId, spaceId, ruleStatusClient }); return [newStatus]; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts deleted file mode 100644 index dd3a2826018e2c..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_rule_status_saved_objects.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SavedObjectsFindResult } from 'kibana/server'; -import { IRuleStatusSOAttributes } from '../rules/types'; -import { MAX_RULE_STATUSES } from './rule_status_service'; -import { RuleStatusSavedObjectsClient } from './rule_status_saved_objects_client'; - -interface GetRuleStatusSavedObject { - alertId: string; - ruleStatusClient: RuleStatusSavedObjectsClient; -} - -export const getRuleStatusSavedObjects = async ({ - alertId, - ruleStatusClient, -}: GetRuleStatusSavedObject): Promise>> => { - return ruleStatusClient.find({ - perPage: MAX_RULE_STATUSES, - sortField: 'statusDate', - sortOrder: 'desc', - search: `${alertId}`, - searchFields: ['alertId'], - }); -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_saved_objects_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_saved_objects_client.ts index 62f02ad6a251ab..b7450091855248 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_saved_objects_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_saved_objects_client.ts @@ -34,6 +34,9 @@ export interface FindBulkResponse { [key: string]: IRuleStatusSOAttributes[] | undefined; } +/** + * @pdeprecated Use RuleExecutionLogClient instead + */ export const ruleStatusSavedObjectsClientFactory = ( savedObjectsClient: SavedObjectsClientContract ): RuleStatusSavedObjectsClient => ({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_service.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_service.test.ts index ec843351d74b50..9a36dd0103a606 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_service.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_service.test.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { ruleStatusSavedObjectsClientMock } from './__mocks__/rule_status_saved_objects_client.mock'; import { buildRuleStatusAttributes, RuleStatusService, @@ -14,6 +13,8 @@ import { } from './rule_status_service'; import { exampleRuleStatus, exampleFindRuleStatusResponse } from './__mocks__/es_results'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; +import { RuleExecutionLogClient } from '../rule_execution_log/__mocks__/rule_execution_log_client'; +import { UpdateExecutionLogArgs } from '../rule_execution_log/types'; const expectIsoDateString = expect.stringMatching(/2.*Z$/); const buildStatuses = (n: number) => @@ -87,27 +88,32 @@ describe('buildRuleStatusAttributes', () => { describe('ruleStatusService', () => { let currentStatus: ReturnType; - let ruleStatusClient: ReturnType; + let ruleStatusClient: ReturnType; let service: RuleStatusService; beforeEach(async () => { currentStatus = exampleRuleStatus(); - ruleStatusClient = ruleStatusSavedObjectsClientMock.create(); + ruleStatusClient = new RuleExecutionLogClient(); ruleStatusClient.find.mockResolvedValue(exampleFindRuleStatusResponse([currentStatus])); - service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + service = await ruleStatusServiceFactory({ + alertId: 'mock-alert-id', + ruleStatusClient, + spaceId: 'default', + }); }); describe('goingToRun', () => { it('updates the current status to "going to run"', async () => { await service.goingToRun(); - expect(ruleStatusClient.update).toHaveBeenCalledWith( - currentStatus.id, - expect.objectContaining({ + expect(ruleStatusClient.update).toHaveBeenCalledWith<[UpdateExecutionLogArgs]>({ + id: currentStatus.id, + spaceId: 'default', + attributes: expect.objectContaining({ status: 'going to run', statusDate: expectIsoDateString, - }) - ); + }), + }); }); }); @@ -115,15 +121,16 @@ describe('ruleStatusService', () => { it('updates the current status to "succeeded"', async () => { await service.success('hey, it worked'); - expect(ruleStatusClient.update).toHaveBeenCalledWith( - currentStatus.id, - expect.objectContaining({ + expect(ruleStatusClient.update).toHaveBeenCalledWith<[UpdateExecutionLogArgs]>({ + id: currentStatus.id, + spaceId: 'default', + attributes: expect.objectContaining({ status: 'succeeded', statusDate: expectIsoDateString, lastSuccessAt: expectIsoDateString, lastSuccessMessage: 'hey, it worked', - }) - ); + }), + }); }); }); @@ -136,15 +143,16 @@ describe('ruleStatusService', () => { it('updates the current status to "failed"', async () => { await service.error('oh no, it broke'); - expect(ruleStatusClient.update).toHaveBeenCalledWith( - currentStatus.id, - expect.objectContaining({ + expect(ruleStatusClient.update).toHaveBeenCalledWith<[UpdateExecutionLogArgs]>({ + id: currentStatus.id, + spaceId: 'default', + attributes: expect.objectContaining({ status: 'failed', statusDate: expectIsoDateString, lastFailureAt: expectIsoDateString, lastFailureMessage: 'oh no, it broke', - }) - ); + }), + }); }); it('does not delete statuses if we have less than the max number of statuses', async () => { @@ -158,7 +166,11 @@ describe('ruleStatusService', () => { ruleStatusClient.find.mockResolvedValue( exampleFindRuleStatusResponse(buildStatuses(MAX_RULE_STATUSES - 1)) ); - service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + service = await ruleStatusServiceFactory({ + alertId: 'mock-alert-id', + ruleStatusClient, + spaceId: 'default', + }); await service.error('oh no, it broke'); @@ -170,7 +182,11 @@ describe('ruleStatusService', () => { ruleStatusClient.find.mockResolvedValue( exampleFindRuleStatusResponse(buildStatuses(MAX_RULE_STATUSES)) ); - service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + service = await ruleStatusServiceFactory({ + alertId: 'mock-alert-id', + ruleStatusClient, + spaceId: 'default', + }); await service.error('oh no, it broke'); @@ -184,7 +200,11 @@ describe('ruleStatusService', () => { ruleStatusClient.find.mockResolvedValue( exampleFindRuleStatusResponse(buildStatuses(MAX_RULE_STATUSES + 1)) ); - service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + service = await ruleStatusServiceFactory({ + alertId: 'mock-alert-id', + ruleStatusClient, + spaceId: 'default', + }); await service.error('oh no, it broke'); @@ -200,7 +220,11 @@ describe('ruleStatusService', () => { ruleStatusClient.find.mockResolvedValue( exampleFindRuleStatusResponse(buildStatuses(MAX_RULE_STATUSES)) ); - service = await ruleStatusServiceFactory({ alertId: 'mock-alert-id', ruleStatusClient }); + service = await ruleStatusServiceFactory({ + alertId: 'mock-alert-id', + ruleStatusClient, + spaceId: 'default', + }); await service.error('oh no, it broke'); await service.error('oh no, it broke'); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_service.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_service.ts index 0d51a6663b709e..45eff57d304e64 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_service.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/rule_status_service.ts @@ -9,7 +9,7 @@ import { assertUnreachable } from '../../../../common/utility_types'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; import { IRuleStatusSOAttributes } from '../rules/types'; import { getOrCreateRuleStatuses } from './get_or_create_rule_statuses'; -import { RuleStatusSavedObjectsClient } from './rule_status_saved_objects_client'; +import { IRuleExecutionLogClient } from '../rule_execution_log/types'; // 1st is mutable status, followed by 5 most recent failures export const MAX_RULE_STATUSES = 6; @@ -78,51 +78,69 @@ export const buildRuleStatusAttributes: ( }; export const ruleStatusServiceFactory = async ({ + spaceId, alertId, ruleStatusClient, }: { + spaceId: string; alertId: string; - ruleStatusClient: RuleStatusSavedObjectsClient; + ruleStatusClient: IRuleExecutionLogClient; }): Promise => { return { goingToRun: async () => { const [currentStatus] = await getOrCreateRuleStatuses({ + spaceId, alertId, ruleStatusClient, }); - await ruleStatusClient.update(currentStatus.id, { - ...currentStatus.attributes, - ...buildRuleStatusAttributes(RuleExecutionStatus['going to run']), + await ruleStatusClient.update({ + id: currentStatus.id, + attributes: { + ...currentStatus.attributes, + ...buildRuleStatusAttributes(RuleExecutionStatus['going to run']), + }, + spaceId, }); }, success: async (message, attributes) => { const [currentStatus] = await getOrCreateRuleStatuses({ + spaceId, alertId, ruleStatusClient, }); - await ruleStatusClient.update(currentStatus.id, { - ...currentStatus.attributes, - ...buildRuleStatusAttributes(RuleExecutionStatus.succeeded, message, attributes), + await ruleStatusClient.update({ + id: currentStatus.id, + attributes: { + ...currentStatus.attributes, + ...buildRuleStatusAttributes(RuleExecutionStatus.succeeded, message, attributes), + }, + spaceId, }); }, partialFailure: async (message, attributes) => { const [currentStatus] = await getOrCreateRuleStatuses({ + spaceId, alertId, ruleStatusClient, }); - await ruleStatusClient.update(currentStatus.id, { - ...currentStatus.attributes, - ...buildRuleStatusAttributes(RuleExecutionStatus['partial failure'], message, attributes), + await ruleStatusClient.update({ + id: currentStatus.id, + attributes: { + ...currentStatus.attributes, + ...buildRuleStatusAttributes(RuleExecutionStatus['partial failure'], message, attributes), + }, + spaceId, }); }, error: async (message, attributes) => { const ruleStatuses = await getOrCreateRuleStatuses({ + spaceId, alertId, ruleStatusClient, }); @@ -134,8 +152,12 @@ export const ruleStatusServiceFactory = async ({ }; // We always update the newest status, so to 'persist' a failure we push a copy to the head of the list - await ruleStatusClient.update(currentStatus.id, failureAttributes); - const newStatus = await ruleStatusClient.create(failureAttributes); + await ruleStatusClient.update({ + id: currentStatus.id, + attributes: failureAttributes, + spaceId, + }); + const newStatus = await ruleStatusClient.create({ attributes: failureAttributes, spaceId }); // drop oldest failures const oldStatuses = [newStatus, ...ruleStatuses].slice(MAX_RULE_STATUSES); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index a14c678d275361..6435204d1b7dfd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -34,6 +34,7 @@ import { mlExecutor } from './executors/ml'; import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.mock'; import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { allowedExperimentalValues } from '../../../../common/experimental_features'; +import { ruleRegistryMocks } from '../../../../../rule_registry/server/mocks'; jest.mock('./rule_status_saved_objects_client'); jest.mock('./rule_status_service'); @@ -119,6 +120,7 @@ describe('signal_rule_alert_type', () => { let logger: ReturnType; let alertServices: AlertServicesMock; let ruleStatusService: Record; + let ruleDataService: ReturnType; beforeEach(() => { alertServices = alertsMock.createAlertServices(); @@ -130,6 +132,7 @@ describe('signal_rule_alert_type', () => { error: jest.fn(), partialFailure: jest.fn(), }; + ruleDataService = ruleRegistryMocks.createRuleDataPluginService(); (ruleStatusServiceFactory as jest.Mock).mockReturnValue(ruleStatusService); (getListsClient as jest.Mock).mockReturnValue({ listClient: getListClientMock(), @@ -196,6 +199,7 @@ describe('signal_rule_alert_type', () => { ml: mlMock, lists: listMock.createSetup(), mergeStrategy: 'missingFields', + ruleDataService, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index d1cb7194f86eed..7e467891e6d4d4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -47,7 +47,6 @@ import { } from '../notifications/schedule_notification_actions'; import { ruleStatusServiceFactory } from './rule_status_service'; import { buildRuleMessageFactory } from './rule_messages'; -import { ruleStatusSavedObjectsClientFactory } from './rule_status_saved_objects_client'; import { getNotificationResultsLink } from '../notifications/utils'; import { TelemetryEventsSender } from '../../telemetry/sender'; import { eqlExecutor } from './executors/eql'; @@ -70,6 +69,8 @@ import { wrapHitsFactory } from './wrap_hits_factory'; import { wrapSequencesFactory } from './wrap_sequences_factory'; import { ConfigType } from '../../../config'; import { ExperimentalFeatures } from '../../../../common/experimental_features'; +import { RuleExecutionLogClient } from '../rule_execution_log/rule_execution_log_client'; +import { IRuleDataPluginService } from '../rule_execution_log/types'; export const signalRulesAlertType = ({ logger, @@ -79,6 +80,7 @@ export const signalRulesAlertType = ({ ml, lists, mergeStrategy, + ruleDataService, }: { logger: Logger; eventsTelemetry: TelemetryEventsSender | undefined; @@ -87,6 +89,7 @@ export const signalRulesAlertType = ({ ml: SetupPlugins['ml']; lists: SetupPlugins['lists'] | undefined; mergeStrategy: ConfigType['alertMergeStrategy']; + ruleDataService: IRuleDataPluginService; }): SignalRuleAlertTypeDefinition => { return { id: SIGNALS_ID, @@ -124,8 +127,12 @@ export const signalRulesAlertType = ({ const searchAfterSize = Math.min(maxSignals, DEFAULT_SEARCH_AFTER_PAGE_SIZE); let hasError: boolean = false; let result = createSearchAfterReturnType(); - const ruleStatusClient = ruleStatusSavedObjectsClientFactory(services.savedObjectsClient); + const ruleStatusClient = new RuleExecutionLogClient({ + ruleDataService, + savedObjectsClient: services.savedObjectsClient, + }); const ruleStatusService = await ruleStatusServiceFactory({ + spaceId, alertId, ruleStatusClient, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 4ad734c3bf7d96..48d372853e6d04 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -33,6 +33,8 @@ import { BuildRuleMessage } from './rule_messages'; import { TelemetryEventsSender } from '../../telemetry/sender'; import { RuleParams } from '../schemas/rule_schemas'; import { GenericBulkCreateResponse } from './bulk_create_factory'; +import { EcsFieldMap } from '../../../../../rule_registry/common/assets/field_maps/ecs_field_map'; +import { TypeOfFieldMap } from '../../../../../rule_registry/common/field_map'; // used for gap detection code // eslint-disable-next-line @typescript-eslint/naming-convention @@ -166,6 +168,12 @@ export interface GetResponse { _source: SearchTypes; } +export type EventHit = Exclude, '@timestamp'> & { + '@timestamp': string; + [key: string]: SearchTypes; +}; +export type WrappedEventHit = BaseHit; + export type SignalSearchResponse = estypes.SearchResponse; export type SignalSourceHit = estypes.SearchHit; export type WrappedSignalHit = BaseHit; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index cb1bf9d774359a..72ac4f6d0f550c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -5,18 +5,20 @@ * 2.0. */ import { createHash } from 'crypto'; +import { chunk, get, isEmpty, partition } from 'lodash'; import moment from 'moment'; import uuidv5 from 'uuid/v5'; + import dateMath from '@elastic/datemath'; import type { estypes } from '@elastic/elasticsearch'; -import { chunk, isEmpty, partition } from 'lodash'; import { ApiResponse, Context } from '@elastic/elasticsearch/lib/Transport'; - +import { ALERT_ID } from '@kbn/rule-data-utils'; import type { ListArray, ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { MAX_EXCEPTION_LIST_SIZE } from '@kbn/securitysolution-list-constants'; import { hasLargeValueList } from '@kbn/securitysolution-list-utils'; import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; import { ElasticsearchClient } from '@kbn/securitysolution-es-utils'; + import { TimestampOverrideOrUndefined, Privilege, @@ -39,6 +41,8 @@ import { RuleRangeTuple, BaseSignalHit, SignalSourceHit, + SimpleHit, + WrappedEventHit, } from './types'; import { BuildRuleMessage } from './rule_messages'; import { ShardError } from '../../types'; @@ -52,6 +56,8 @@ import { ThreatRuleParams, ThresholdRuleParams, } from '../schemas/rule_schemas'; +import { WrappedRACAlert } from '../rule_types/types'; +import { SearchTypes } from '../../../../common/detection_engine/types'; interface SortExceptionsReturn { exceptionsWithValueLists: ExceptionListItemSchema[]; @@ -928,3 +934,25 @@ export const buildChunkedOrFilter = (field: string, values: string[], chunkSize: }) .join(' OR '); }; + +export const isWrappedEventHit = (event: SimpleHit): event is WrappedEventHit => { + return !isWrappedSignalHit(event) && !isWrappedRACAlert(event); +}; + +export const isWrappedSignalHit = (event: SimpleHit): event is WrappedSignalHit => { + return (event as WrappedSignalHit)?._source?.signal != null; +}; + +export const isWrappedRACAlert = (event: SimpleHit): event is WrappedRACAlert => { + return (event as WrappedRACAlert)?._source?.[ALERT_ID] != null; +}; + +export const getField = (event: SimpleHit, field: string): T | undefined => { + if (isWrappedRACAlert(event)) { + return event._source, field.replace('signal', 'kibana.alert') as T; // TODO: handle special cases + } else if (isWrappedSignalHit(event)) { + return get(event._source, field) as T; + } else if (isWrappedEventHit(event)) { + return get(event._source, field) as T; + } +}; diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 96171154f60071..fd3a32a2fa689d 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -187,7 +187,6 @@ export class Plugin implements IPlugin new RuleExecutionLogClient({ ruleDataService: plugins.ruleRegistry.ruleDataService, - // TODO check if savedObjects.client contains spaceId savedObjectsClient: context.core.savedObjects.client, }), }) @@ -262,6 +261,7 @@ export class Plugin implements IPlugin { }); expect(disableAlert).toHaveBeenCalled(); + await act(async () => { + await nextTick(); + wrapper.update(); + }); + // Enable the alert await act(async () => { wrapper.find('[data-test-subj="enableSwitch"] .euiSwitch__button').first().simulate('click'); @@ -546,6 +551,77 @@ describe('disable button', () => { // Ensure error banner is back expect(wrapper.find('[data-test-subj="dismiss-execution-error"]').length).toBeGreaterThan(0); }); + + it('should show the loading spinner when the rule enabled switch was clicked and the server responded with some delay', async () => { + const alert = mockAlert({ + enabled: true, + executionStatus: { + status: 'error', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: { + reason: AlertExecutionStatusErrorReasons.Execute, + message: 'Fail', + }, + }, + }); + + const alertType: AlertType = { + id: '.noop', + name: 'No Op', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup, + actionVariables: { context: [], state: [], params: [] }, + defaultActionGroupId: 'default', + producer: ALERTS_FEATURE_ID, + authorizedConsumers, + minimumLicenseRequired: 'basic', + enabledInLicense: true, + }; + + const disableAlert = jest.fn(async () => { + await new Promise((resolve) => setTimeout(resolve, 6000)); + }); + const enableAlert = jest.fn(); + const wrapper = mountWithIntl( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + // Dismiss the error banner + await act(async () => { + wrapper.find('[data-test-subj="dismiss-execution-error"]').first().simulate('click'); + await nextTick(); + }); + + // Disable the alert + await act(async () => { + wrapper.find('[data-test-subj="enableSwitch"] .euiSwitch__button').first().simulate('click'); + await nextTick(); + }); + expect(disableAlert).toHaveBeenCalled(); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + // Enable the alert + await act(async () => { + expect(wrapper.find('[data-test-subj="enableSpinner"]').length).toBeGreaterThan(0); + await nextTick(); + }); + }); }); describe('mute button', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 1328ba6479f687..2558993a13fe61 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -21,6 +21,7 @@ import { EuiSpacer, EuiButtonEmpty, EuiButton, + EuiLoadingSpinner, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { AlertExecutionStatusErrorReasons } from '../../../../../../alerting/common'; @@ -99,6 +100,8 @@ export const AlertDetails: React.FunctionComponent = ({ const alertActions = alert.actions; const uniqueActions = Array.from(new Set(alertActions.map((item: any) => item.actionTypeId))); const [isEnabled, setIsEnabled] = useState(alert.enabled); + const [isEnabledUpdating, setIsEnabledUpdating] = useState(false); + const [isMutedUpdating, setIsMutedUpdating] = useState(false); const [isMuted, setIsMuted] = useState(alert.muteAll); const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); const [dissmissAlertErrors, setDissmissAlertErrors] = useState(false); @@ -218,54 +221,92 @@ export const AlertDetails: React.FunctionComponent = ({ - { - if (isEnabled) { - setIsEnabled(false); - await disableAlert(alert); - // Reset dismiss if previously clicked - setDissmissAlertErrors(false); - } else { - setIsEnabled(true); - await enableAlert(alert); + {isEnabledUpdating ? ( + + + + + + + + + + + + ) : ( + { + setIsEnabledUpdating(true); + if (isEnabled) { + setIsEnabled(false); + await disableAlert(alert); + // Reset dismiss if previously clicked + setDissmissAlertErrors(false); + } else { + setIsEnabled(true); + await enableAlert(alert); + } + requestRefresh(); + setIsEnabledUpdating(false); + }} + label={ + } - requestRefresh(); - }} - label={ - - } - /> + /> + )} - { - if (isMuted) { - setIsMuted(false); - await unmuteAlert(alert); - } else { - setIsMuted(true); - await muteAlert(alert); + {isMutedUpdating ? ( + + + + + + + + + + + + ) : ( + { + setIsMutedUpdating(true); + if (isMuted) { + setIsMuted(false); + await unmuteAlert(alert); + } else { + setIsMuted(true); + await muteAlert(alert); + } + requestRefresh(); + setIsMutedUpdating(false); + }} + label={ + } - requestRefresh(); - }} - label={ - - } - /> + /> + )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx index 29290af0d02852..1583cb188f1c18 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx @@ -8,7 +8,7 @@ import React, { useState } from 'react'; import moment, { Duration } from 'moment'; import { i18n } from '@kbn/i18n'; -import { EuiBasicTable, EuiHealth, EuiSpacer, EuiSwitch, EuiToolTip } from '@elastic/eui'; +import { EuiBasicTable, EuiHealth, EuiSpacer, EuiToolTip } from '@elastic/eui'; // @ts-ignore import { RIGHT_ALIGNMENT, CENTER_ALIGNMENT } from '@elastic/eui/lib/services'; import { padStart, chunk } from 'lodash'; @@ -26,6 +26,7 @@ import { } from '../../common/components/with_bulk_alert_api_operations'; import { DEFAULT_SEARCH_PAGE_SIZE } from '../../../constants'; import './alert_instances.scss'; +import { RuleMutedSwitch } from './rule_muted_switch'; type AlertInstancesProps = { alert: Alert; @@ -112,17 +113,11 @@ export const alertInstancesTableColumns = ( ), render: (alertInstance: AlertInstanceListItem) => { return ( - <> - onMuteAction(alertInstance)} - /> - + await onMuteAction(alertInstance)} + alertInstance={alertInstance} + /> ); }, sortable: false, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/rule_muted_switch.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/rule_muted_switch.tsx new file mode 100644 index 00000000000000..bee0c8aef706d0 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/rule_muted_switch.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { EuiSwitch, EuiLoadingSpinner } from '@elastic/eui'; + +import { AlertInstanceListItem } from './alert_instances'; + +interface ComponentOpts { + alertInstance: AlertInstanceListItem; + onMuteAction: (instance: AlertInstanceListItem) => Promise; + disabled: boolean; +} + +export const RuleMutedSwitch: React.FunctionComponent = ({ + alertInstance, + onMuteAction, + disabled, +}: ComponentOpts) => { + const [isMuted, setIsMuted] = useState(alertInstance?.isMuted); + const [isUpdating, setIsUpdating] = useState(false); + + return isUpdating ? ( + + ) : ( + { + setIsUpdating(true); + await onMuteAction(alertInstance); + setIsMuted(!isMuted); + setIsUpdating(false); + }} + data-test-subj={`muteAlertInstanceButton_${alertInstance.instance}`} + showLabel={false} + label="mute" + /> + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.tsx index c031f189ffa4d0..49871549d27344 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.tsx @@ -5,9 +5,8 @@ * 2.0. */ -import { asyncScheduler } from 'rxjs'; -import React, { useEffect, useState } from 'react'; -import { EuiSwitch } from '@elastic/eui'; +import React, { useState, useEffect } from 'react'; +import { EuiSwitch, EuiLoadingSpinner } from '@elastic/eui'; import { Alert, AlertTableItem } from '../../../../types'; @@ -28,8 +27,11 @@ export const RuleEnabledSwitch: React.FunctionComponent = ({ useEffect(() => { setIsEnabled(item.enabled); }, [item.enabled]); + const [isUpdating, setIsUpdating] = useState(false); - return ( + return isUpdating ? ( + + ) : ( = ({ checked={isEnabled} data-test-subj="enableSwitch" onChange={async () => { - const enabled = isEnabled; - asyncScheduler.schedule(async () => { - if (enabled) { - await disableAlert({ ...item, enabled }); - } else { - await enableAlert({ ...item, enabled }); - } - onAlertChanged(); - }, 10); + setIsUpdating(true); + const enabled = item.enabled; + if (enabled) { + await disableAlert({ ...item, enabled }); + } else { + await enableAlert({ ...item, enabled }); + } setIsEnabled(!isEnabled); + setIsUpdating(false); + onAlertChanged(); }} label="" /> diff --git a/x-pack/plugins/watcher/kibana.json b/x-pack/plugins/watcher/kibana.json index b9df25d80e62e1..84fe2b509b2637 100644 --- a/x-pack/plugins/watcher/kibana.json +++ b/x-pack/plugins/watcher/kibana.json @@ -15,6 +15,7 @@ "ui": true, "requiredBundles": [ "esUiShared", - "kibanaReact" + "kibanaReact", + "fieldFormats" ] } diff --git a/x-pack/plugins/watcher/public/legacy/time_buckets.js b/x-pack/plugins/watcher/public/legacy/time_buckets.js index a65e069bb208ba..173369152f8e0d 100644 --- a/x-pack/plugins/watcher/public/legacy/time_buckets.js +++ b/x-pack/plugins/watcher/public/legacy/time_buckets.js @@ -7,7 +7,8 @@ import _ from 'lodash'; import moment from 'moment'; -import { search, FIELD_FORMAT_IDS, UI_SETTINGS } from '../../../../../src/plugins/data/public'; +import { search, UI_SETTINGS } from '../../../../../src/plugins/data/public'; +import { FIELD_FORMAT_IDS } from '../../../../../src/plugins/field_formats/common'; import { calcAutoIntervalLessThan, calcAutoIntervalNear } from './calc_auto_interval'; import { convertDurationToNormalizedEsInterval, diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts b/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts index a6ea27be21cc84..d2f9acf35d6323 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts @@ -116,7 +116,7 @@ export default function ({ getService }: FtrProviderContext) { }; before(async () => { - await ml.api.indexAnnotation(annotation, annotationId); + await ml.api.indexAnnotation(annotation as Partial, annotationId); }); it('displays the original annotation correctly', async () => { @@ -189,7 +189,7 @@ export default function ({ getService }: FtrProviderContext) { const annotationId = `delete-annotation-id-${Date.now()}`; before(async () => { - await ml.api.indexAnnotation(annotation, annotationId); + await ml.api.indexAnnotation(annotation as Partial, annotationId); }); it('displays the original annotation', async () => { diff --git a/x-pack/test/functional/es_archives/discover/default/data.json.gz b/x-pack/test/functional/es_archives/discover/default/data.json.gz deleted file mode 100644 index 047d890f6d4101..00000000000000 Binary files a/x-pack/test/functional/es_archives/discover/default/data.json.gz and /dev/null differ diff --git a/x-pack/test/functional/es_archives/discover/default/mappings.json b/x-pack/test/functional/es_archives/discover/default/mappings.json deleted file mode 100644 index 519af2dd75b9e1..00000000000000 --- a/x-pack/test/functional/es_archives/discover/default/mappings.json +++ /dev/null @@ -1,279 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": {} - }, - "index": ".kibana_1", - "mappings": { - "properties": { - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - } - } - }, - "dashboard": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "index-pattern": { - "dynamic": "strict", - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "fieldAttrs": { - "type": "text" - } - } - }, - "search": { - "dynamic": "strict", - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "dynamic": "strict", - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "timelion-sheet": { - "dynamic": "strict", - "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" - } - } - }, - "type": { - "type": "keyword" - }, - "url": { - "dynamic": "strict", - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchId": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - }, - "query": { - "properties": { - "title": { - "type": "text" - }, - "description": { - "type": "text" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "type": "keyword", - "index": false - } - } - }, - "filters": { - "type": "object", - "enabled": false - }, - "timefilter": { - "type": "object", - "enabled": false - } - } - } - } - }, - "settings": { - "index": { - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} diff --git a/x-pack/test/functional/fixtures/kbn_archiver/discover/default.json b/x-pack/test/functional/fixtures/kbn_archiver/discover/default.json new file mode 100644 index 00000000000000..2b0d414c67c990 --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/discover/default.json @@ -0,0 +1,65 @@ +{ + "attributes": { + "buildNum": 9007199254740991 + }, + "coreMigrationVersion": "7.15.0", + "id": "7.15.0", + "migrationVersion": { + "config": "7.13.0" + }, + "references": [], + "type": "config", + "updated_at": "2021-08-04T16:20:48.415Z", + "version": "WzUsMl0=" +} + +{ + "attributes": { + "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"nestedField.child\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true,\"subType\":{\"nested\":{\"path\":\"nestedField\"}}}]", + "timeFieldName": "@timestamp", + "title": "logstash-*" + }, + "coreMigrationVersion": "7.15.0", + "id": "logstash-*", + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [], + "type": "index-pattern", + "version": "WzIsMl0=" +} + +{ + "attributes": { + "columns": [ + "_source" + ], + "description": "A Saved Search Description", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":true,\"filter\":[],\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "A Saved Search", + "version": 1 + }, + "coreMigrationVersion": "7.15.0", + "id": "ab12e3c0-f231-11e6-9486-733b1ac9221a", + "migrationVersion": { + "search": "7.9.3" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "version": "WzMsMl0=" +} \ No newline at end of file diff --git a/x-pack/test/functional/page_objects/security_page.ts b/x-pack/test/functional/page_objects/security_page.ts index 074ecafddf3810..37fe0eccea31af 100644 --- a/x-pack/test/functional/page_objects/security_page.ts +++ b/x-pack/test/functional/page_objects/security_page.ts @@ -485,10 +485,7 @@ export class SecurityPageObject extends FtrService { if (roleObj.elasticsearch.indices[0].query) { await this.testSubjects.click('restrictDocumentsQuery0'); - await this.monacoEditor.setCodeEditorValue( - 0, - JSON.stringify(roleObj.elasticsearch.indices[0].query) - ); + await this.monacoEditor.setCodeEditorValue(roleObj.elasticsearch.indices[0].query); } const globalPrivileges = (roleObj.kibana as any).global; diff --git a/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts b/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts index 93dca78b34a826..3f02e64056325d 100644 --- a/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts +++ b/x-pack/test/search_sessions_integration/tests/apps/discover/async_search.ts @@ -25,15 +25,23 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ]); const searchSessions = getService('searchSessions'); const retry = getService('retry'); + const kibanaServer = getService('kibanaServer'); describe('discover async search', () => { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); - await esArchiver.load('x-pack/test/functional/es_archives/discover/default'); + await kibanaServer.importExport.load( + 'x-pack/test/functional/fixtures/kbn_archiver/discover/default' + ); await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.header.waitUntilLoadingHasFinished(); }); + after(async () => { + await kibanaServer.importExport.unload( + 'x-pack/test/functional/fixtures/kbn_archiver/discover/default' + ); + }); it('search session id should change between searches', async () => { const searchSessionId1 = await getSearchSessionId();