diff --git a/.eslintrc.js b/.eslintrc.js index a57ea5e03865a..2c55dd2528ef1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -470,11 +470,6 @@ module.exports = { errorMessage: 'Server modules cannot be imported into client modules or shared modules.', }, - { - target: ['src/**/*'], - from: ['x-pack/**/*'], - errorMessage: 'OSS cannot import x-pack files.', - }, { target: ['src/core/**/*'], from: ['plugins/**/*', 'src/plugins/**/*'], diff --git a/.i18nrc.json b/.i18nrc.json index 68bb343092f25..235b65d7502f4 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", @@ -35,6 +36,7 @@ "monaco": "packages/kbn-monaco/src", "esQuery": "packages/kbn-es-query/src", "presentationUtil": "src/plugins/presentation_util", + "indexPatternEditor": "src/plugins/index_pattern_editor", "indexPatternFieldEditor": "src/plugins/index_pattern_field_editor", "indexPatternManagement": "src/plugins/index_pattern_management", "interactiveSetup": "src/plugins/interactive_setup", diff --git a/api_docs/timelines.json b/api_docs/timelines.json index f273ee5fc24ba..4294381952216 100644 --- a/api_docs/timelines.json +++ b/api_docs/timelines.json @@ -10918,7 +10918,7 @@ "label": "alertConsumers", "description": [], "signature": [ - "ALERTS_CONSUMERS", + "AlertConsumers", "[] | undefined" ], "path": "x-pack/plugins/timelines/common/search_strategy/timeline/index.ts", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index eaaab3386a795..dc410f2e5f2a5 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -115,10 +115,18 @@ 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. +|{kib-repo}blob/{branch}/src/plugins/index_pattern_editor/README.md[indexPatternEditor] +|Create index patterns from within Kibana apps. + + |{kib-repo}blob/{branch}/src/plugins/index_pattern_field_editor/README.md[indexPatternFieldEditor] |The reusable field editor across Kibana! diff --git a/docs/development/core/public/kibana-plugin-core-public.overlayflyoutopenoptions.hideclosebutton.md b/docs/development/core/public/kibana-plugin-core-public.overlayflyoutopenoptions.hideclosebutton.md new file mode 100644 index 0000000000000..149a53f35d34d --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.overlayflyoutopenoptions.hideclosebutton.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [OverlayFlyoutOpenOptions](./kibana-plugin-core-public.overlayflyoutopenoptions.md) > [hideCloseButton](./kibana-plugin-core-public.overlayflyoutopenoptions.hideclosebutton.md) + +## OverlayFlyoutOpenOptions.hideCloseButton property + +Signature: + +```typescript +hideCloseButton?: boolean; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.overlayflyoutopenoptions.md b/docs/development/core/public/kibana-plugin-core-public.overlayflyoutopenoptions.md index 6665ebde295bc..fc4959b87a987 100644 --- a/docs/development/core/public/kibana-plugin-core-public.overlayflyoutopenoptions.md +++ b/docs/development/core/public/kibana-plugin-core-public.overlayflyoutopenoptions.md @@ -18,6 +18,7 @@ export interface OverlayFlyoutOpenOptions | ["data-test-subj"](./kibana-plugin-core-public.overlayflyoutopenoptions._data-test-subj_.md) | string | | | [className](./kibana-plugin-core-public.overlayflyoutopenoptions.classname.md) | string | | | [closeButtonAriaLabel](./kibana-plugin-core-public.overlayflyoutopenoptions.closebuttonarialabel.md) | string | | +| [hideCloseButton](./kibana-plugin-core-public.overlayflyoutopenoptions.hideclosebutton.md) | boolean | | | [maxWidth](./kibana-plugin-core-public.overlayflyoutopenoptions.maxwidth.md) | boolean | number | string | | | [ownFocus](./kibana-plugin-core-public.overlayflyoutopenoptions.ownfocus.md) | boolean | | | [size](./kibana-plugin-core-public.overlayflyoutopenoptions.size.md) | EuiFlyoutSize | | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.getconvertedobjectid.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.getconvertedobjectid.md new file mode 100644 index 0000000000000..c6a429d345ed1 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.getconvertedobjectid.md @@ -0,0 +1,28 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUtils](./kibana-plugin-core-server.savedobjectsutils.md) > [getConvertedObjectId](./kibana-plugin-core-server.savedobjectsutils.getconvertedobjectid.md) + +## SavedObjectsUtils.getConvertedObjectId() method + +Uses a single-namespace object's "legacy ID" to determine what its new ID will be after it is converted to a multi-namespace type. + +Signature: + +```typescript +static getConvertedObjectId(namespace: string | undefined, type: string, id: string): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| namespace | string | undefined | | +| type | string | | +| id | string | | + +Returns: + +`string` + +{string} The ID of the saved object after it is converted. + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md index 0148621e757b7..ab6382aca6a52 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md @@ -24,5 +24,6 @@ export declare class SavedObjectsUtils | Method | Modifiers | Description | | --- | --- | --- | | [generateId()](./kibana-plugin-core-server.savedobjectsutils.generateid.md) | static | Generates a random ID for a saved objects. | +| [getConvertedObjectId(namespace, type, id)](./kibana-plugin-core-server.savedobjectsutils.getconvertedobjectid.md) | static | Uses a single-namespace object's "legacy ID" to determine what its new ID will be after it is converted to a multi-namespace type. | | [isRandomId(id)](./kibana-plugin-core-server.savedobjectsutils.israndomid.md) | static | Validates that a saved object ID has been randomly generated. | 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 6e9f12d98d149..2c93ae6143b44 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.indexpatternaggrestrictions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggregationrestrictions.md similarity index 69% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternaggrestrictions.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggregationrestrictions.md index 324bd1e410c6c..b3d04027980ca 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternaggrestrictions.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggregationrestrictions.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternAggRestrictions](./kibana-plugin-plugins-data-public.indexpatternaggrestrictions.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggregationRestrictions](./kibana-plugin-plugins-data-public.aggregationrestrictions.md) -## IndexPatternAggRestrictions type +## AggregationRestrictions type 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 1aa9f460c4fac..0000000000000 --- 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 c6135d9313b23..90aa0b0a8a313 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 3d08df8544d6c..6addd931ce22d 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 4b2cad7b42882..b970a408e5130 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 4ea7ec8cd4f65..c7611ac761bb9 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 993634023c20c..54e64c309351e 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 344044b38f7de..a60e631835ea4 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 c7810b18c55a9..341ec0d7e514c 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 24d60613d913a..b5d5c5cfee5ad 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 e569733f15e7f..3b768404aab95 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 447e387f90eb5..2a26b009d7447 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 f877fa26b1b25..48a32cd9abe61 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 5c98ba4131ac3..79c92cfe52dd7 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 e38da6600696c..0000000000000 --- 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 ac3f256a9afc3..0000000000000 --- 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 454a816a60171..0000000000000 --- 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 0535585cb4718..0000000000000 --- 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 436124ac08387..0000000000000 --- 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 1d109a599d2d9..0000000000000 --- 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 ec497de59d236..0000000000000 --- 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 446e0c237ce13..0000000000000 --- 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 f4eeb5eed06a0..0000000000000 --- 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 59afdc25df350..0000000000000 --- 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 2032f0fc01112..0000000000000 --- 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 945ac7ededff6..0000000000000 --- 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 91c3ff4f2d9a3..0000000000000 --- 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 c6afa27fe5952..0000000000000 --- 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 640064af4be12..0000000000000 --- 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 1e7fd9d161429..0000000000000 --- 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 5825af4925d06..0000000000000 --- 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 41f5f2446f22a..0000000000000 --- 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 57ccca9136081..0000000000000 --- 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 b19246758f080..0000000000000 --- 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 48ec9456c56dd..0000000000000 --- 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 394a2e3cc9afb..0000000000000 --- 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 82441ee41d80d..0000000000000 --- 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 b179c314a56d3..0000000000000 --- 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 f856a3132eccb..0000000000000 --- 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 aad977643ad2f..0000000000000 --- 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 9f94d50a2001f..0000000000000 --- 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 838ea2b5d9f3a..0000000000000 --- 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 8e89a0262b438..0000000000000 --- 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 c54e549c42a44..0000000000000 --- 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 ac8ddce6e7016..0000000000000 --- 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 b25d313129634..247760305db9c 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.getfieldsoptions.allownoindex.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.allownoindex.md new file mode 100644 index 0000000000000..091fb4d8aaa44 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.allownoindex.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [GetFieldsOptions](./kibana-plugin-plugins-data-public.getfieldsoptions.md) > [allowNoIndex](./kibana-plugin-plugins-data-public.getfieldsoptions.allownoindex.md) + +## GetFieldsOptions.allowNoIndex property + +Signature: + +```typescript +allowNoIndex?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.lookback.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.lookback.md new file mode 100644 index 0000000000000..0e8c7e34b1fe8 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.lookback.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [GetFieldsOptions](./kibana-plugin-plugins-data-public.getfieldsoptions.md) > [lookBack](./kibana-plugin-plugins-data-public.getfieldsoptions.lookback.md) + +## GetFieldsOptions.lookBack property + +Signature: + +```typescript +lookBack?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.md new file mode 100644 index 0000000000000..056018174baf6 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [GetFieldsOptions](./kibana-plugin-plugins-data-public.getfieldsoptions.md) + +## GetFieldsOptions interface + +Signature: + +```typescript +export interface GetFieldsOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [allowNoIndex](./kibana-plugin-plugins-data-public.getfieldsoptions.allownoindex.md) | boolean | | +| [lookBack](./kibana-plugin-plugins-data-public.getfieldsoptions.lookback.md) | boolean | | +| [metaFields](./kibana-plugin-plugins-data-public.getfieldsoptions.metafields.md) | string[] | | +| [pattern](./kibana-plugin-plugins-data-public.getfieldsoptions.pattern.md) | string | | +| [rollupIndex](./kibana-plugin-plugins-data-public.getfieldsoptions.rollupindex.md) | string | | +| [type](./kibana-plugin-plugins-data-public.getfieldsoptions.type.md) | string | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.metafields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.metafields.md new file mode 100644 index 0000000000000..87c0f9d9bfe5b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.metafields.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [GetFieldsOptions](./kibana-plugin-plugins-data-public.getfieldsoptions.md) > [metaFields](./kibana-plugin-plugins-data-public.getfieldsoptions.metafields.md) + +## GetFieldsOptions.metaFields property + +Signature: + +```typescript +metaFields?: string[]; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.pattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.pattern.md new file mode 100644 index 0000000000000..c6c53b2cf7bc8 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.pattern.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [GetFieldsOptions](./kibana-plugin-plugins-data-public.getfieldsoptions.md) > [pattern](./kibana-plugin-plugins-data-public.getfieldsoptions.pattern.md) + +## GetFieldsOptions.pattern property + +Signature: + +```typescript +pattern: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.rollupindex.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.rollupindex.md new file mode 100644 index 0000000000000..4711e3bdfce92 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.rollupindex.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [GetFieldsOptions](./kibana-plugin-plugins-data-public.getfieldsoptions.md) > [rollupIndex](./kibana-plugin-plugins-data-public.getfieldsoptions.rollupindex.md) + +## GetFieldsOptions.rollupIndex property + +Signature: + +```typescript +rollupIndex?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.type.md new file mode 100644 index 0000000000000..cdc4c562b5611 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getfieldsoptions.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [GetFieldsOptions](./kibana-plugin-plugins-data-public.getfieldsoptions.md) > [type](./kibana-plugin-plugins-data-public.getfieldsoptions.type.md) + +## GetFieldsOptions.type property + +Signature: + +```typescript +type?: string; +``` 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 66effff722f3b..db741f74f538d 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 4fe738ddef5dc..0000000000000 --- 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 0f83e2bb2d423..0000000000000 --- 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 ec723ddc00e72..8fe65d5a86de1 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 b4b3198092604..6a798c3a4add1 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 3f8048d2913fb..99caa646c17b0 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 70a9b624099cb..7a704b917daaf 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 517dec992c090..cd91bdcebce60 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 9d26cf9730f46..774601daf4a87 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 fbd85a7d73230..052ccc2ae97b4 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 8891620ad01ef..39da5c0548da0 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 522a1c66bb80e..047a9861002b5 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 6c6904d6625c6..73d82c25228bb 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 35ebdb1473c4e..51d0f8a139da5 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 49c9d1d143e6b..3716f5cb4febf 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,7 @@ | [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) | | +| [GetFieldsOptions](./kibana-plugin-plugins-data-public.getfieldsoptions.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 +103,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 +112,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) | | @@ -145,6 +142,7 @@ | [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) | | | [AggGroupName](./kibana-plugin-plugins-data-public.agggroupname.md) | | | [AggParam](./kibana-plugin-plugins-data-public.aggparam.md) | | +| [AggregationRestrictions](./kibana-plugin-plugins-data-public.aggregationrestrictions.md) | | | [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) | AggsStart represents the actual external contract as AggsCommonStart is only used internally. The difference is that AggsStart includes the typings for the registry with initialized agg types. | | [AutocompleteStart](./kibana-plugin-plugins-data-public.autocompletestart.md) | \* | | [AutoRefreshDoneFn](./kibana-plugin-plugins-data-public.autorefreshdonefn.md) | | @@ -159,21 +157,14 @@ | [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) | | -| [IndexPatternAggRestrictions](./kibana-plugin-plugins-data-public.indexpatternaggrestrictions.md) | | | [IndexPatternLoadExpressionFunctionDefinition](./kibana-plugin-plugins-data-public.indexpatternloadexpressionfunctiondefinition.md) | | | [IndexPatternsContract](./kibana-plugin-plugins-data-public.indexpatternscontract.md) | | | [IndexPatternSelectProps](./kibana-plugin-plugins-data-public.indexpatternselectprops.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 850aff0f6cd85..cc1007655ecf3 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 a28dba5b1cd3f..48a41dc34497f 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 91dd7c13c472e..5d452d759c934 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 291dc90c3971d..eefb773f5727f 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 42db2d353befa..df78be16e6d01 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 977f875b214e5..b00a7f31318eb 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 bdfc881878efd..fba9afd6f6648 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 834fe302ad9f8..a3f92491e8983 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 a3c3ddc4e1649..0000000000000 --- 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 c5173fba22533..0000000000000 --- 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 5d5c13784bc57..0000000000000 --- 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 c5e5faef30860..0000000000000 --- 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 42563051bcdc4..0000000000000 --- 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 a3f6c709246ea..0000000000000 --- 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 c284d2c87fcaa..0000000000000 --- 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 9ac9ba1abc637..7f812fb08fd51 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 932afceb5d22b..93f91f6264dbc 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 9fe169233af13..829cc9c0752a0 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 8a06b394ae554..deb71a9df8cc5 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 15978b6414947..3beef6e08ed3f 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 e2810cc21a378..01367d931a841 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 bc96d3596ed12..c72ad5163d4ec 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 0bdb5d6726fa8..f56300757736a 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 2e76e3b0c6ad4..0363aa1ce6442 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 bd90f23b4ab59..5763cb2dacb7a 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 dd1f3806c1408..a31e4512cfcf1 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 648e23e597f4d..55badaf2d07bc 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 7a77c6c943cd7..26182e96a4a7e 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 387dac22e1c83..e1f002674ef6d 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/maps/asset-tracking-tutorial.asciidoc b/docs/maps/asset-tracking-tutorial.asciidoc new file mode 100644 index 0000000000000..822510e882c12 --- /dev/null +++ b/docs/maps/asset-tracking-tutorial.asciidoc @@ -0,0 +1,340 @@ +[role="xpack"] +[[asset-tracking-tutorial]] +== Track, visualize, and alert on assets in real time + +Are you interested in asset tracking? Good news! Visualizing and analyzing data that moves is easy with *Maps*. You can track the location of an IoT device and monitor a package or vehicle in transit. + +In this tutorial, you’ll look at live urban transit data from the city of Portland, Oregon. You’ll watch the city buses, use the data to visualize congestion, and notify a dispatch team when a bus enters a construction zone. + +You’ll learn to: + +- Use Logstash to ingest the TriMet REST API into Elasticsearch. +- Create a map with layers that visualize asset tracks and last-known locations. +- Use symbols and colors to style data values and show which direction an asset is heading. +- Set up tracking containment alerts to monitor moving vehicles. + +When you complete this tutorial, you’ll have a map that looks like this: + +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/construction_zones.png[] + +[float] +=== Prerequisites + +- If you don’t already have {kib}, set it up with https://www.elastic.co/cloud/elasticsearch-service/signup?baymax=docs-body&elektra=docs[our free trial]. Download the deployment credentials. +- Obtain an API key for https://developer.trimet.org/[TriMet web services] at https://developer.trimet.org/appid/registration/. +- https://www.elastic.co/guide/en/logstash/current/getting-started-with-logstash.html[Install Logstash]. + +[float] +=== Part 1: Ingest the Portland bus data +To get to the fun of visualizing and alerting on Portland buses, you must first create a Logstash pipeline to ingest the TriMet Portland bus data into {es}. + +[float] +==== Step 1: Set up an Elasticsearch index + +. In Kibana, open the main menu, then click *Dev Tools*. +. In *Console*, create the `tri_met_tracks` index: ++ +[source,js] +---------------------------------- +PUT tri_met_tracks +---------------------------------- + +. To configure the `tri_met_tracks` index mappings, run: ++ +[source,js] +---------------------------------- +PUT tri_met_tracks/_mapping +{ + "properties": { + "in_congestion": { + "type": "boolean" + }, + "location": { + "type": "geo_point" + }, + "route": { + "type": "keyword" + }, + "time": { + "type": "date", + "format": "epoch_millis" + }, + "type": { + "type": "keyword" + }, + "vehicle_id": { + "type": "keyword" + } + } +} +---------------------------------- + +[float] +==== Step 2: Start Logstash + +. In your `logstash/config` folder, create the file `trimet-pipeline.conf`. +. Copy the pipeline script into your `trimet-pipeline.conf` file. ++ +[source,yaml] +---------------------------------- +input { + http_poller { + urls => { + trimet => "https://developer.trimet.org/ws/v2/vehicles?appID=" + } + request_timeout => 60 + schedule => { cron => "* * * * * UTC"} + codec => "json" + } +} + +filter { + split { + field => "[resultSet][vehicle]" + } + + if ![resultSet][vehicle][inCongestion] { + mutate { + update => { + "[resultSet][vehicle][inCongestion]" => "false" + } + } + } + + mutate { + add_field => { + "bearing" => "%{[resultSet][vehicle][bearing]}" + "in_congestion" => "%{[resultSet][vehicle][inCongestion]}" + "location" => "%{[resultSet][vehicle][latitude]},%{[resultSet][vehicle][longitude]}" + "route" => "%{[resultSet][vehicle][routeNumber]}" + "time" => "%{[resultSet][vehicle][time]}" + "type" => "%{[resultSet][vehicle][type]}" + "vehicle_id" => "%{[resultSet][vehicle][vehicleID]}" + } + remove_field => [ "resultSet", "@version", "@timestamp" ] + } + + mutate { + convert => { + "bearing" => "float" + "in_congestion" => "boolean" + "time" => "integer" + } + } +} + +output { + stdout { + codec => rubydebug + } + + elasticsearch { + cloud_auth => "" + cloud_id => "" + index => "tri_met_tracks" + document_id => "%{[vehicle_id]}_%{[time]}" + } +} +---------------------------------- + +. Replace `` with your TriMet application id. +. Replace `` with your Elastic Cloud deployment credentials. +. Replace `` with your {ece}/ece-cloud-id.html[elastic cloud id]. +. Open a terminal window, and then navigate to the Logstash folder. +. In your `logstash` folder, run Logstash with the TriMet pipeline: ++ +[source,bash] +---------------------------------- +bin/logstash -f config/trimet-pipeline.conf +---------------------------------- + +. Wait for Logstash to initialize and confirm data is flowing. You should see messages similar to this: ++ +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/logstash_output.png[] +. Leave the terminal window open and Logstash running throughout this tutorial. + +[float] +==== Step 3: Create a {kib} index pattern for the tri_met_tracks {es} index + +. In Kibana, open the main menu, and click *Stack Management > Index Patterns*. +. Click *Create index pattern*. +. Give the index pattern a name: *tri_met_tracks**. +. Click *Next step*. +. Set the *Time field* to *time*. +. Click *Create index pattern*. + +{kib} shows the fields in your index pattern. + +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/index_pattern.png[] + +[float] +==== Step 4: Explore the Portland bus data + +. Open the main menu, and click *Discover*. +. Set the index pattern to *tri_met_tracks**. +. Open the <>, and set the time range to the last 15 minutes. +. Expand a document and explore some of the fields that you will use later in this tutorial: `bearing`, `in_congestion`, `location`, and `vehicle_id`. + +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/discover.png[] + +[float] +=== Part 2: Build an operational map +It's hard to get an overview of Portland buses by looking at individual events. Let's create a map to show the bus routes and current location for each bus, along with the direction the buses are headed. + +[float] +==== Step 1: Create your map +Create your map and set the theme for the default layer to dark mode. + +. Open the main menu, and click *Maps*. +. Click *Create map*. +. In the *Layers* list, click *Road map*, and then click *Edit layer settings*. +. Open the *Tile service* dropdown, and select *Road map - dark*. +. Click *Save & close*. + +[float] +==== Step 2. Add a tracks layer + +Add a layer to show the bus routes for the last 15 minutes. + +. Click *Add layer*. +. Click *Tracks*. +. Select the *tri_met_tracks** index pattern. +. Define the tracks: +.. Set *Entity* to *vehicle_id*. +.. Set *Sort* to *time*. +. Click *Add layer*. +. In Layer settings: +.. Set *Name* to *Buses*. +.. Set *Opacity* to 80%. +. Scroll to *Layer Style*, and set *Border color* to pink. +. Click *Save & close*. +. In the *Layers* list, click *Buses*, and then click *Fit to data*. + +At this point, you have a map with lines that represent the routes of the buses as they move around the city. + +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/tracks_layer.png[] + +[float] +==== Step 3. Indicate the direction of the bus tracks + +Add a layer that uses attributes in the data to set the style and orientation of the buses. You’ll see the direction buses are headed and what traffic is like. + +. Click *Add layer*, and then select *Top Hits per entity*. +. Select the *tri_met_tracks** index pattern. +. To display the most recent location per bus: +.. Set *Entity* to *vehicle_id*. +.. Set *Documents per entity* to 1. +.. Set *Sort field* to *time*. +.. Set *Sort order* to *descending*. +. Click *Add layer*. +. Scroll to *Layer Style*. +.. Set *Symbol type* to *icon*. +.. Set *Icon* to *arrow-es*. +.. Set the *Fill color*: +... Select *By value* styling, and set the field to *in_congestion*. +... Use a *Custom color palette*. +... Set the *Other* color to black. +... Add a green class for *false*, meaning the bus is not in traffic. +... Add a red class for *true*, meaning the bus is in congestion. +.. Set *Border width* to 0. +.. Change *Symbol orientation* to use *By value* and the *bearing* field. ++ +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/top_hits_layer_style.png[] +. Click *Save & close*. +. Open the <>, and set *Refresh every* to 10 seconds, and click *Start*. + +Your map should automatically refresh every 10 seconds to show the lastest bus positions and tracks. + +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/tracks_and_top_hits.png[] + +[float] +=== Part 3: Setup geo-fencing alerts +Let's make TriMet Portland bus data actionable and alert when buses enter construction zones. + +[float] +==== Step 1. Add a construction zone + +Add a layer for construction zones, which you will draw on the map. The construction zones will be used as your geofence boundary or threshold that serves as the basis for triggering alerts. + +. Click *Add layer*. +. Click *Create index*. +. Set *Index name* to *construction_zones*. +. Click *Create index*. +. Draw 2 or 3 construction zones on your map: +.. In the toolbar on left side of the map, select the bounding box icon image:maps/images/asset-tracking-tutorial/bounding_box_icon.png[bounding box icon]. +.. To draw a construction zone, click a start point on the map and drag. +.. Click an endpoint to finish. +. When you finish drawing the construction zones, click *Exit* under the layer name in the legend. +. In *Layer settings*, set *Name* to *Construction zones*. +. Scroll to *Layer Style*, and set *Fill color* to yellow. +. Click *Save & close*. +. *Save* the map. +.. Give the map a title. +.. Under *Add to dashboard*, select *None*. +.. Click *Save and add to library*. + +The map now represents an operational view of live bus traffic. You’ll see the direction that the buses are traveling, and whether they are near or have entered a construction zone. + +Your map is now complete. + +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/construction_zones.png[] + + +[float] +==== Step 2. Configure an alert + +Create a new alert by defining a rule and a connector. The rule includes the conditions that will trigger the alert, and the connector defines what action takes place once the alert is triggered. In this case, each alert will log a message to the Kibana log. + +. Open *Stack Management*, and then click *Rules and Connectors*. +. Click *Create rule*. +. Name the rule *Bus Alerts*. +. Set *Check every* to *5 seconds*. +. Notify *Only on status change*. ++ +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/rule_configuration.png[] +. Select the *Tracking containment* rule type. +. Set *Select entity*: +.. Set *INDEX* to *tri_met_tracks**. +.. Set *BY* to *vehicle_id*. +. Set *Select boundary* *INDEX* to *construction_zones*. ++ +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/tracking_containment_configuration.png[] +. Under *Actions*, select the *Server log* connector type. +. Click *Create a connector*. +. In the *Server log connector*: +.. Set *Connector name* to *Bus alert connector*. +.. Click *Save*. +. Complete the *Actions* configuration. +.. Set *Message* to : ++ +[source,js] +---------------------------------- +{ + "entityId": "{{context.entityId}}", + "entityDateTime": "{{context.entityDateTime}}", + "entityDocumentId": "{{context.entityDocumentId}}", + "detectionDateTime": "{{context.detectionDateTime}}", + "entityLocation": "{{context.entityLocation}}", + "containingBoundaryId": "{{context.containingBoundaryId}}", + "containingBoundaryName": "{{context.containingBoundaryName}}" +} +---------------------------------- + +. Click *Save*. + +The *Bus Alert connector* is added to the *Rules and Connectors* page. For more information on common connectors, refer to the <> and <> connectors. + +[role="screenshot"] +image::maps/images/asset-tracking-tutorial/rules_and_connectors.png[] + +Congratulations! You have completed the tutorial and have the recipe for tracking assets. You can now try replicating this same analysis with your own data. diff --git a/docs/maps/images/asset-tracking-tutorial/bounding_box_icon.png b/docs/maps/images/asset-tracking-tutorial/bounding_box_icon.png new file mode 100644 index 0000000000000..a7289c35fc0c3 Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/bounding_box_icon.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/construction_zones.png b/docs/maps/images/asset-tracking-tutorial/construction_zones.png new file mode 100644 index 0000000000000..0487c3d040a3b Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/construction_zones.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/discover.png b/docs/maps/images/asset-tracking-tutorial/discover.png new file mode 100644 index 0000000000000..d5ea0e55eedde Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/discover.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/index_pattern.png b/docs/maps/images/asset-tracking-tutorial/index_pattern.png new file mode 100644 index 0000000000000..e1aaecbe62d65 Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/index_pattern.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/logstash_output.png b/docs/maps/images/asset-tracking-tutorial/logstash_output.png new file mode 100644 index 0000000000000..9235e31e98504 Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/logstash_output.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/rule_configuration.png b/docs/maps/images/asset-tracking-tutorial/rule_configuration.png new file mode 100644 index 0000000000000..a94c5a9e1dba9 Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/rule_configuration.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/rules_and_connectors.png b/docs/maps/images/asset-tracking-tutorial/rules_and_connectors.png new file mode 100644 index 0000000000000..35d89dd42ab27 Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/rules_and_connectors.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/top_hits_layer_style.png b/docs/maps/images/asset-tracking-tutorial/top_hits_layer_style.png new file mode 100644 index 0000000000000..b4a869529ad45 Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/top_hits_layer_style.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/tracking_containment_configuration.png b/docs/maps/images/asset-tracking-tutorial/tracking_containment_configuration.png new file mode 100644 index 0000000000000..4baf34bb414f1 Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/tracking_containment_configuration.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/tracks_and_top_hits.png b/docs/maps/images/asset-tracking-tutorial/tracks_and_top_hits.png new file mode 100644 index 0000000000000..3dd856af4a18e Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/tracks_and_top_hits.png differ diff --git a/docs/maps/images/asset-tracking-tutorial/tracks_layer.png b/docs/maps/images/asset-tracking-tutorial/tracks_layer.png new file mode 100644 index 0000000000000..b1275ec9d502f Binary files /dev/null and b/docs/maps/images/asset-tracking-tutorial/tracks_layer.png differ diff --git a/docs/maps/index.asciidoc b/docs/maps/index.asciidoc index 0922a4cc94c22..e2b379b7eb8c2 100644 --- a/docs/maps/index.asciidoc +++ b/docs/maps/index.asciidoc @@ -74,6 +74,7 @@ Search across the layers in your map to focus in on just the data you want. Comb -- include::maps-getting-started.asciidoc[] +include::asset-tracking-tutorial.asciidoc[] include::heatmap-layer.asciidoc[] include::tile-layer.asciidoc[] include::vector-layer.asciidoc[] diff --git a/docs/user/monitoring/kibana-alerts.asciidoc b/docs/user/monitoring/kibana-alerts.asciidoc index beaae1fdb71b6..f00a3999ab277 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 107f7b7df23fe..2058eed5f024d 100644 --- a/package.json +++ b/package.json @@ -95,11 +95,11 @@ "dependencies": { "@elastic/apm-rum": "^5.8.0", "@elastic/apm-rum-react": "^1.2.11", - "@elastic/charts": "33.1.0", + "@elastic/charts": "33.2.0", "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", - "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.13", + "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.14", "@elastic/ems-client": "7.14.0", - "@elastic/eui": "36.0.0", + "@elastic/eui": "36.1.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "^9.0.1-kibana3", "@elastic/maki": "6.3.0", diff --git a/packages/kbn-es/src/utils/native_realm.js b/packages/kbn-es/src/utils/native_realm.js index a5051cdb0d89a..f7ee9da290dc6 100644 --- a/packages/kbn-es/src/utils/native_realm.js +++ b/packages/kbn-es/src/utils/native_realm.js @@ -11,6 +11,23 @@ const chalk = require('chalk'); const { log: defaultLog } = require('./log'); +/** + * Hack to skip the Product Check performed by the Elasticsearch-js client. + * We noticed a couple of bugs that may need to be fixed before taking full + * advantage of this feature. + * + * The bugs are detailed in this issue: https://github.com/elastic/kibana/issues/105557 + * + * The hack is copied from the test/utils in the elasticsearch-js repo + * (https://github.com/elastic/elasticsearch-js/blob/master/test/utils/index.js#L45-L56) + */ +function skipProductCheck(client) { + const tSymbol = Object.getOwnPropertySymbols(client.transport || client).filter( + (symbol) => symbol.description === 'product check' + )[0]; + (client.transport || client)[tSymbol] = 2; +} + exports.NativeRealm = class NativeRealm { constructor({ elasticPassword, port, log = defaultLog, ssl = false, caCert }) { this._client = new Client({ @@ -22,6 +39,8 @@ exports.NativeRealm = class NativeRealm { } : undefined, }); + // TODO: @elastic/es-clients I had to disable the product check here because the client is getting 404 while ES is initializing, but the requests here auto retry them. + skipProductCheck(this._client); this._elasticPassword = elasticPassword; this._log = log; } diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index e814fe9db5e3a..10ce4f7ab1d1f 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -8,12 +8,12 @@ pageLoadAssetSize: charts: 95000 cloud: 21076 console: 46091 - core: 432925 + core: 434325 crossClusterReplication: 65408 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 @@ -33,6 +34,7 @@ pageLoadAssetSize: indexLifecycleManagement: 107090 indexManagement: 140608 indexPatternManagement: 28222 + indexPatternEditor: 25000 infra: 184320 fleet: 465774 ingestPipelines: 58003 @@ -118,4 +120,3 @@ pageLoadAssetSize: expressionMetric: 22238 expressionShape: 34008 interactiveSetup: 18532 - \ No newline at end of file diff --git a/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts b/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts index 2d0b0ec4a726c..d3d20edffa286 100644 --- a/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts +++ b/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts @@ -15,7 +15,7 @@ * setting, with which the user can change the index prefix. */ -export const ALERTS_CONSUMERS = { +export const AlertConsumers = { APM: 'apm', LOGS: 'logs', INFRASTRUCTURE: 'infrastructure', @@ -23,9 +23,9 @@ export const ALERTS_CONSUMERS = { SIEM: 'siem', SYNTHETICS: 'synthetics', } as const; -export type ALERTS_CONSUMERS = typeof ALERTS_CONSUMERS[keyof typeof ALERTS_CONSUMERS]; +export type AlertConsumers = typeof AlertConsumers[keyof typeof AlertConsumers]; -export const mapConsumerToIndexName: Record = { +export const mapConsumerToIndexName: Record = { apm: '.alerts-observability-apm', logs: '.alerts-observability.logs', infrastructure: '.alerts-observability.metrics', 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 76e4a8803fd34..a29c1023caf67 100644 --- a/packages/kbn-rule-data-utils/src/technical_field_names.ts +++ b/packages/kbn-rule-data-utils/src/technical_field_names.ts @@ -34,6 +34,7 @@ const ALERT_EVALUATION_THRESHOLD = `${ALERT_NAMESPACE}.evaluation.threshold` as const ALERT_EVALUATION_VALUE = `${ALERT_NAMESPACE}.evaluation.value` as const; const ALERT_ID = `${ALERT_NAMESPACE}.id` as const; const ALERT_OWNER = `${ALERT_NAMESPACE}.owner` as const; +const ALERT_CONSUMERS = `${ALERT_NAMESPACE}.consumers` as const; const ALERT_PRODUCER = `${ALERT_NAMESPACE}.producer` as const; const ALERT_REASON = `${ALERT_NAMESPACE}.reason` as const; const ALERT_RISK_SCORE = `${ALERT_NAMESPACE}.risk_score` as const; @@ -70,6 +71,7 @@ const ALERT_RULE_SEVERITY_MAPPING = `${ALERT_RULE_NAMESPACE}.severity_mapping` a const ALERT_RULE_TAGS = `${ALERT_RULE_NAMESPACE}.tags` as const; const ALERT_RULE_TO = `${ALERT_RULE_NAMESPACE}.to` as const; const ALERT_RULE_TYPE = `${ALERT_RULE_NAMESPACE}.type` as const; +const ALERT_RULE_TYPE_ID = `${ALERT_RULE_NAMESPACE}.rule_type_id` as const; 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; @@ -99,6 +101,7 @@ const fields = { ALERT_EVALUATION_VALUE, ALERT_ID, ALERT_OWNER, + ALERT_CONSUMERS, ALERT_PRODUCER, ALERT_REASON, ALERT_RISK_SCORE, @@ -124,6 +127,7 @@ const fields = { ALERT_RULE_TAGS, ALERT_RULE_TO, ALERT_RULE_TYPE, + ALERT_RULE_TYPE_ID, ALERT_RULE_UPDATED_AT, ALERT_RULE_UPDATED_BY, ALERT_RULE_VERSION, @@ -151,6 +155,7 @@ export { ALERT_NAMESPACE, ALERT_RULE_NAMESPACE, ALERT_OWNER, + ALERT_CONSUMERS, ALERT_PRODUCER, ALERT_REASON, ALERT_RISK_SCORE, @@ -179,6 +184,7 @@ export { ALERT_RULE_TAGS, ALERT_RULE_TO, ALERT_RULE_TYPE, + ALERT_RULE_TYPE_ID, ALERT_RULE_UPDATED_AT, ALERT_RULE_UPDATED_BY, ALERT_RULE_VERSION, diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index af836d4436a1a..d2bc11f4db877 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -4843,20 +4843,29 @@ exports[`Header renders 1`] = ` truncate={true} > diff --git a/src/core/public/chrome/ui/header/__snapshots__/header_breadcrumbs.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header_breadcrumbs.test.tsx.snap index 5080b23e99c25..eb90df831fabc 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header_breadcrumbs.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header_breadcrumbs.test.tsx.snap @@ -1,34 +1,54 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`HeaderBreadcrumbs renders updates to the breadcrumbs$ observable 1`] = ` - - First - + + + First + + + `; exports[`HeaderBreadcrumbs renders updates to the breadcrumbs$ observable 2`] = ` Array [ - - First - , - + + First + + + , +
  • - Second - , + + + Second + + +
  • , ] `; diff --git a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap index cd8c669aae235..f723cf5bca23a 100644 --- a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap +++ b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap @@ -17,7 +17,8 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiBottomBar.customScreenReaderAnnouncement": [Function], "euiBottomBar.screenReaderAnnouncement": "There is a new region landmark with page level controls at the end of the document.", "euiBottomBar.screenReaderHeading": "Page level controls", - "euiBreadcrumbs.collapsedBadge.ariaLabel": "Show collapsed breadcrumbs", + "euiBreadcrumbs.collapsedBadge.ariaLabel": "See collapsed breadcrumbs", + "euiBreadcrumbs.nav.ariaLabel": "Breadcrumbs", "euiCardSelect.select": "Select", "euiCardSelect.selected": "Selected", "euiCardSelect.unavailable": "Unavailable", @@ -193,11 +194,13 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiSelectableTemplateSitewide.searchPlaceholder": "Search for anything...", "euiStat.loadingText": "Statistic is loading", "euiStepStrings.complete": [Function], + "euiStepStrings.current": [Function], "euiStepStrings.disabled": [Function], "euiStepStrings.errors": [Function], "euiStepStrings.incomplete": [Function], "euiStepStrings.loading": [Function], "euiStepStrings.simpleComplete": [Function], + "euiStepStrings.simpleCurrent": [Function], "euiStepStrings.simpleDisabled": [Function], "euiStepStrings.simpleErrors": [Function], "euiStepStrings.simpleIncomplete": [Function], diff --git a/src/core/public/i18n/i18n_eui_mapping.tsx b/src/core/public/i18n/i18n_eui_mapping.tsx index 3372146894edb..0a6c7a05e1c75 100644 --- a/src/core/public/i18n/i18n_eui_mapping.tsx +++ b/src/core/public/i18n/i18n_eui_mapping.tsx @@ -92,10 +92,14 @@ export const getEuiContextMapping = (): EuiTokensObject => { 'euiBreadcrumbs.collapsedBadge.ariaLabel': i18n.translate( 'core.euiBreadcrumbs.collapsedBadge.ariaLabel', { - defaultMessage: 'Show collapsed breadcrumbs', + defaultMessage: 'See collapsed breadcrumbs', description: 'Displayed when one or more breadcrumbs are hidden.', } ), + 'euiBreadcrumbs.nav.ariaLabel': i18n.translate('core.euiBreadcrumbs.nav.ariaLabel', { + defaultMessage: 'Breadcrumbs', + description: 'Label on the breadcrumbs nav element', + }), 'euiCardSelect.select': i18n.translate('core.euiCardSelect.select', { defaultMessage: 'Select', description: 'Displayed button text when a card option can be selected.', @@ -932,11 +936,21 @@ export const getEuiContextMapping = (): EuiTokensObject => { defaultMessage: 'Step {number}: {title} is complete', values: { number, title }, }), + 'euiStepStrings.current': ({ number, title }: EuiValues) => + i18n.translate('core.euiStepStrings.current', { + defaultMessage: 'Current step {number}: {title}', + values: { number, title }, + }), 'euiStepStrings.simpleComplete': ({ number }: EuiValues) => i18n.translate('core.euiStepStrings.simpleComplete', { defaultMessage: 'Step {number} is complete', values: { number }, }), + 'euiStepStrings.simpleCurrent': ({ number }: EuiValues) => + i18n.translate('core.euiStepStrings.simpleCurrent', { + defaultMessage: 'Current step is {number}', + values: { number }, + }), 'euiStepStrings.warning': ({ number, title }: EuiValues) => i18n.translate('core.euiStepStrings.warning', { defaultMessage: 'Step {number}: {title} has warnings', diff --git a/src/core/public/overlays/flyout/flyout_service.tsx b/src/core/public/overlays/flyout/flyout_service.tsx index 603736f08268f..b41b85e5f429f 100644 --- a/src/core/public/overlays/flyout/flyout_service.tsx +++ b/src/core/public/overlays/flyout/flyout_service.tsx @@ -84,6 +84,7 @@ export interface OverlayFlyoutOpenOptions { 'data-test-subj'?: string; size?: EuiFlyoutSize; maxWidth?: boolean | number | string; + hideCloseButton?: boolean; } interface StartDeps { diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 8e0087cbeeed0..80fd3927e05a3 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -1027,6 +1027,8 @@ export interface OverlayFlyoutOpenOptions { // (undocumented) closeButtonAriaLabel?: string; // (undocumented) + hideCloseButton?: boolean; + // (undocumented) maxWidth?: boolean | number | string; // (undocumented) ownFocus?: boolean; diff --git a/src/core/server/elasticsearch/client/configure_client.ts b/src/core/server/elasticsearch/client/configure_client.ts index 18c9724c2f8f4..f2953862c25e0 100644 --- a/src/core/server/elasticsearch/client/configure_client.ts +++ b/src/core/server/elasticsearch/client/configure_client.ts @@ -50,6 +50,12 @@ export const configureClient = ( const client = new Client({ ...clientOptions, Transport: KibanaTransport }); addLogging(client, logger.get('query', type)); + // ------------------------------------------------------------------------ // + // Hack to disable the "Product check" while the bugs in // + // https://github.com/elastic/kibana/issues/105557 are handled. // + skipProductCheck(client); + // ------------------------------------------------------------------------ // + return client; }; @@ -112,3 +118,21 @@ const addLogging = (client: Client, logger: Logger) => { } }); }; + +/** + * Hack to skip the Product Check performed by the Elasticsearch-js client. + * We noticed a couple of bugs that may need to be fixed before taking full + * advantage of this feature. + * + * The bugs are detailed in this issue: https://github.com/elastic/kibana/issues/105557 + * + * The hack is copied from the test/utils in the elasticsearch-js repo + * (https://github.com/elastic/elasticsearch-js/blob/master/test/utils/index.js#L45-L56) + */ +function skipProductCheck(client: Client) { + const tSymbol = Object.getOwnPropertySymbols(client.transport || client).filter( + (symbol) => symbol.description === 'product check' + )[0]; + // @ts-expect-error `tSymbol` is missing in the index signature of Transport + (client.transport || client)[tSymbol] = 2; +} diff --git a/src/core/server/plugins/plugin.test.ts b/src/core/server/plugins/plugin.test.ts index 610bc1cac5a3b..31706e01e4b84 100644 --- a/src/core/server/plugins/plugin.test.ts +++ b/src/core/server/plugins/plugin.test.ts @@ -39,6 +39,11 @@ jest.doMock(join('plugin-with-wrong-initializer-path', 'server'), () => ({ plugi virtual: true, }); +const OSS_PLUGIN_PATH_POSIX = '/kibana/src/plugins/ossPlugin'; +const OSS_PLUGIN_PATH_WINDOWS = 'C:\\kibana\\src\\plugins\\ossPlugin'; +const XPACK_PLUGIN_PATH_POSIX = '/kibana/x-pack/plugins/xPackPlugin'; +const XPACK_PLUGIN_PATH_WINDOWS = 'C:\\kibana\\x-pack\\plugins\\xPackPlugin'; + function createPluginManifest(manifestProps: Partial = {}): PluginManifest { return { id: 'some-plugin-id', @@ -97,10 +102,49 @@ test('`constructor` correctly initializes plugin instance', () => { expect(plugin.name).toBe('some-plugin-id'); expect(plugin.configPath).toBe('path'); expect(plugin.path).toBe('some-plugin-path'); + expect(plugin.source).toBe('external'); // see below for test cases for non-external sources (OSS and X-Pack) expect(plugin.requiredPlugins).toEqual(['some-required-dep']); expect(plugin.optionalPlugins).toEqual(['some-optional-dep']); }); +describe('`constructor` correctly sets non-external source', () => { + function createPlugin(path: string) { + const manifest = createPluginManifest(); + const opaqueId = Symbol(); + return new PluginWrapper({ + path, + manifest, + opaqueId, + initializerContext: createPluginInitializerContext( + coreContext, + opaqueId, + manifest, + instanceInfo + ), + }); + } + + test('for OSS plugin in POSIX', () => { + const plugin = createPlugin(OSS_PLUGIN_PATH_POSIX); + expect(plugin.source).toBe('oss'); + }); + + test('for OSS plugin in Windows', () => { + const plugin = createPlugin(OSS_PLUGIN_PATH_WINDOWS); + expect(plugin.source).toBe('oss'); + }); + + test('for X-Pack plugin in POSIX', () => { + const plugin = createPlugin(XPACK_PLUGIN_PATH_POSIX); + expect(plugin.source).toBe('x-pack'); + }); + + test('for X-Pack plugin in Windows', () => { + const plugin = createPlugin(XPACK_PLUGIN_PATH_WINDOWS); + expect(plugin.source).toBe('x-pack'); + }); +}); + test('`setup` fails if `plugin` initializer is not exported', () => { const manifest = createPluginManifest(); const opaqueId = Symbol(); diff --git a/src/core/server/plugins/plugin.ts b/src/core/server/plugins/plugin.ts index 76ed4f8f24b3d..dc861b4489dcf 100644 --- a/src/core/server/plugins/plugin.ts +++ b/src/core/server/plugins/plugin.ts @@ -27,6 +27,9 @@ import { } from './types'; import { CorePreboot, CoreSetup, CoreStart } from '..'; +const OSS_PATH_REGEX = /[\/|\\]src[\/|\\]plugins[\/|\\]/; // Matches src/plugins directory on POSIX and Windows +const XPACK_PATH_REGEX = /[\/|\\]x-pack[\/|\\]plugins[\/|\\]/; // Matches x-pack/plugins directory on POSIX and Windows + /** * Lightweight wrapper around discovered plugin that is responsible for instantiating * plugin and dispatching proper context and dependencies into plugin's lifecycle hooks. @@ -40,6 +43,7 @@ export class PluginWrapper< TPluginsStart extends object = object > { public readonly path: string; + public readonly source: 'oss' | 'x-pack' | 'external'; public readonly manifest: PluginManifest; public readonly opaqueId: PluginOpaqueId; public readonly name: PluginManifest['id']; @@ -70,6 +74,7 @@ export class PluginWrapper< } ) { this.path = params.path; + this.source = getPluginSource(params.path); this.manifest = params.manifest; this.opaqueId = params.opaqueId; this.initializerContext = params.initializerContext; @@ -204,3 +209,12 @@ export class PluginWrapper< return this.manifest.type === PluginType.preboot; } } + +function getPluginSource(path: string): 'oss' | 'x-pack' | 'external' { + if (OSS_PATH_REGEX.test(path)) { + return 'oss'; + } else if (XPACK_PATH_REGEX.test(path)) { + return 'x-pack'; + } + return 'external'; +} diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 3cd645cb74ac0..a9827dc60fb78 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -52,6 +52,15 @@ expect.addSnapshotSerializer(createAbsolutePathSerializer()); }); }); +const OSS_PLUGIN_PATH = '/kibana/src/plugins/ossPlugin'; +const XPACK_PLUGIN_PATH = '/kibana/x-pack/plugins/xPackPlugin'; +const EXTERNAL_PLUGIN_PATH = '/kibana/plugins/externalPlugin'; +[OSS_PLUGIN_PATH, XPACK_PLUGIN_PATH, EXTERNAL_PLUGIN_PATH].forEach((path) => { + jest.doMock(join(path, 'server'), () => ({}), { + virtual: true, + }); +}); + const createPlugin = ( id: string, { @@ -247,6 +256,75 @@ describe('PluginsService', () => { expect(standardMockPluginSystem.setupPlugins).not.toHaveBeenCalled(); }); + describe('X-Pack dependencies', () => { + function mockDiscoveryResults(params: { sourcePluginPath: string; dependencyType: string }) { + const { sourcePluginPath, dependencyType } = params; + // Each plugin's source is derived from its path; the PluginWrapper test suite contains more detailed test cases around the paths (for both POSIX and Windows) + mockDiscover.mockReturnValue({ + error$: from([]), + plugin$: from([ + createPlugin('sourcePlugin', { + path: sourcePluginPath, + version: 'some-version', + configPath: 'path', + requiredPlugins: dependencyType === 'requiredPlugin' ? ['xPackPlugin'] : [], + requiredBundles: dependencyType === 'requiredBundle' ? ['xPackPlugin'] : undefined, + optionalPlugins: dependencyType === 'optionalPlugin' ? ['xPackPlugin'] : [], + }), + createPlugin('xPackPlugin', { + path: XPACK_PLUGIN_PATH, + version: 'some-version', + configPath: 'path', + requiredPlugins: [], + optionalPlugins: [], + }), + ]), + }); + } + + async function expectError() { + await expect(pluginsService.discover({ environment: environmentPreboot })).rejects.toThrow( + `X-Pack plugin or bundle with id "xPackPlugin" is required by OSS plugin "sourcePlugin", which is prohibited. Consider making this an optional dependency instead.` + ); + expect(standardMockPluginSystem.addPlugin).not.toHaveBeenCalled(); + } + async function expectSuccess() { + await expect(pluginsService.discover({ environment: environmentPreboot })).resolves.toEqual( + expect.anything() + ); + expect(standardMockPluginSystem.addPlugin).toHaveBeenCalled(); + } + + it('throws if an OSS plugin requires an X-Pack plugin or bundle', async () => { + for (const dependencyType of ['requiredPlugin', 'requiredBundle']) { + mockDiscoveryResults({ sourcePluginPath: OSS_PLUGIN_PATH, dependencyType }); + await expectError(); + } + }); + + it('does not throw if an OSS plugin has an optional dependency on an X-Pack plugin', async () => { + mockDiscoveryResults({ + sourcePluginPath: OSS_PLUGIN_PATH, + dependencyType: 'optionalPlugin', + }); + await expectSuccess(); + }); + + it('does not throw if an X-Pack plugin depends on an X-Pack plugin or bundle', async () => { + for (const dependencyType of ['requiredPlugin', 'requiredBundle', 'optionalPlugin']) { + mockDiscoveryResults({ sourcePluginPath: XPACK_PLUGIN_PATH, dependencyType }); + await expectSuccess(); + } + }); + + it('does not throw if an external plugin depends on an X-Pack plugin or bundle', async () => { + for (const dependencyType of ['requiredPlugin', 'requiredBundle', 'optionalPlugin']) { + mockDiscoveryResults({ sourcePluginPath: EXTERNAL_PLUGIN_PATH, dependencyType }); + await expectSuccess(); + } + }); + }); + it('properly detects plugins that should be disabled.', async () => { jest .spyOn(configService, 'isEnabledAtPath') diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index 05bb60fb22c6d..9def7554ccd09 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -314,27 +314,7 @@ export class PluginsService implements CoreService + ) { + const { name, manifest, requiredBundles, requiredPlugins } = plugin; + + // validate that `requiredBundles` ids point to a discovered plugin which `includesUiPlugin` + for (const requiredBundleId of requiredBundles) { + if (!pluginEnableStatuses.has(requiredBundleId)) { + throw new Error( + `Plugin bundle with id "${requiredBundleId}" is required by plugin "${name}" but it is missing.` + ); + } + + const requiredPlugin = pluginEnableStatuses.get(requiredBundleId)!.plugin; + if (!requiredPlugin.includesUiPlugin) { + throw new Error( + `Plugin bundle with id "${requiredBundleId}" is required by plugin "${name}" but it doesn't have a UI bundle.` + ); + } + + if (requiredPlugin.manifest.type !== plugin.manifest.type) { + throw new Error( + `Plugin bundle with id "${requiredBundleId}" is required by plugin "${name}" and expected to have "${manifest.type}" type, but its type is "${requiredPlugin.manifest.type}".` + ); + } + } + + // validate that OSS plugins do not have required dependencies on X-Pack plugins + if (plugin.source === 'oss') { + for (const id of [...requiredPlugins, ...requiredBundles]) { + const requiredPlugin = pluginEnableStatuses.get(id); + if (requiredPlugin && requiredPlugin.plugin.source === 'x-pack') { + throw new Error( + `X-Pack plugin or bundle with id "${id}" is required by OSS plugin "${name}", which is prohibited. Consider making this an optional dependency instead.` + ); + } + } + } + } + private shouldEnablePlugin( pluginName: PluginName, pluginEnableStatuses: Map, diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.mock.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.mock.ts new file mode 100644 index 0000000000000..ee0b18af5ac0d --- /dev/null +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.mock.ts @@ -0,0 +1,21 @@ +/* + * 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 mockGetConvertedObjectId = jest.fn().mockReturnValue('uuidv5'); +jest.mock('../../service/lib/utils', () => { + const actual = jest.requireActual('../../service/lib/utils'); + return { + ...actual, + SavedObjectsUtils: { + ...actual.SavedObjectsUtils, + getConvertedObjectId: mockGetConvertedObjectId, + }, + }; +}); + +export { mockGetConvertedObjectId }; diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts index 87b8ee0809064..64c1c4ce2fa9f 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { mockUuidv5 } from './__mocks__'; +import { mockGetConvertedObjectId } from './document_migrator.test.mock'; import { set } from '@elastic/safer-lodash-set'; import _ from 'lodash'; import { SavedObjectUnsanitizedDoc } from '../../serialization'; @@ -37,7 +37,7 @@ const createRegistry = (...types: Array>) => { }; beforeEach(() => { - mockUuidv5.mockClear(); + mockGetConvertedObjectId.mockClear(); }); describe('DocumentMigrator', () => { @@ -866,7 +866,7 @@ describe('DocumentMigrator', () => { namespace: 'foo-namespace', }; const actual = migrator.migrate(obj); - expect(mockUuidv5).not.toHaveBeenCalled(); + expect(mockGetConvertedObjectId).not.toHaveBeenCalled(); expect(actual).toEqual({ id: 'cowardly', type: 'dog', @@ -898,7 +898,7 @@ describe('DocumentMigrator', () => { it('in the default space', () => { const actual = migrator.migrateAndConvert(obj); - expect(mockUuidv5).not.toHaveBeenCalled(); + expect(mockGetConvertedObjectId).not.toHaveBeenCalled(); expect(actual).toEqual([ { id: 'bad', @@ -912,8 +912,8 @@ describe('DocumentMigrator', () => { it('in a non-default space', () => { const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); - expect(mockUuidv5).toHaveBeenCalledTimes(1); - expect(mockUuidv5).toHaveBeenCalledWith('foo-namespace:toy:favorite', 'DNSUUID'); + expect(mockGetConvertedObjectId).toHaveBeenCalledTimes(1); + expect(mockGetConvertedObjectId).toHaveBeenCalledWith('foo-namespace', 'toy', 'favorite'); expect(actual).toEqual([ { id: 'bad', @@ -946,7 +946,7 @@ describe('DocumentMigrator', () => { it('in the default space', () => { const actual = migrator.migrateAndConvert(obj); - expect(mockUuidv5).not.toHaveBeenCalled(); + expect(mockGetConvertedObjectId).not.toHaveBeenCalled(); expect(actual).toEqual([ { id: 'loud', @@ -961,8 +961,8 @@ describe('DocumentMigrator', () => { it('in a non-default space', () => { const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); - expect(mockUuidv5).toHaveBeenCalledTimes(1); - expect(mockUuidv5).toHaveBeenCalledWith('foo-namespace:dog:loud', 'DNSUUID'); + expect(mockGetConvertedObjectId).toHaveBeenCalledTimes(1); + expect(mockGetConvertedObjectId).toHaveBeenCalledWith('foo-namespace', 'dog', 'loud'); expect(actual).toEqual([ { id: 'uuidv5', @@ -1008,7 +1008,7 @@ describe('DocumentMigrator', () => { it('in the default space', () => { const actual = migrator.migrateAndConvert(obj); - expect(mockUuidv5).not.toHaveBeenCalled(); + expect(mockGetConvertedObjectId).not.toHaveBeenCalled(); expect(actual).toEqual([ { id: 'cute', @@ -1024,9 +1024,19 @@ describe('DocumentMigrator', () => { it('in a non-default space', () => { const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); - expect(mockUuidv5).toHaveBeenCalledTimes(2); - expect(mockUuidv5).toHaveBeenNthCalledWith(1, 'foo-namespace:toy:favorite', 'DNSUUID'); - expect(mockUuidv5).toHaveBeenNthCalledWith(2, 'foo-namespace:dog:cute', 'DNSUUID'); + expect(mockGetConvertedObjectId).toHaveBeenCalledTimes(2); + expect(mockGetConvertedObjectId).toHaveBeenNthCalledWith( + 1, + 'foo-namespace', + 'toy', + 'favorite' + ); + expect(mockGetConvertedObjectId).toHaveBeenNthCalledWith( + 2, + 'foo-namespace', + 'dog', + 'cute' + ); expect(actual).toEqual([ { id: 'uuidv5', @@ -1080,7 +1090,7 @@ describe('DocumentMigrator', () => { it('in the default space', () => { const actual = migrator.migrateAndConvert(obj); - expect(mockUuidv5).not.toHaveBeenCalled(); + expect(mockGetConvertedObjectId).not.toHaveBeenCalled(); expect(actual).toEqual([ { id: 'sleepy', @@ -1095,8 +1105,8 @@ describe('DocumentMigrator', () => { it('in a non-default space', () => { const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); - expect(mockUuidv5).toHaveBeenCalledTimes(1); - expect(mockUuidv5).toHaveBeenCalledWith('foo-namespace:toy:favorite', 'DNSUUID'); + expect(mockGetConvertedObjectId).toHaveBeenCalledTimes(1); + expect(mockGetConvertedObjectId).toHaveBeenCalledWith('foo-namespace', 'toy', 'favorite'); expect(actual).toEqual([ { id: 'sleepy', @@ -1134,7 +1144,7 @@ describe('DocumentMigrator', () => { it('in the default space', () => { const actual = migrator.migrateAndConvert(obj); - expect(mockUuidv5).not.toHaveBeenCalled(); + expect(mockGetConvertedObjectId).not.toHaveBeenCalled(); expect(actual).toEqual([ { id: 'hungry', @@ -1149,8 +1159,8 @@ describe('DocumentMigrator', () => { it('in a non-default space', () => { const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); - expect(mockUuidv5).toHaveBeenCalledTimes(1); - expect(mockUuidv5).toHaveBeenCalledWith('foo-namespace:dog:hungry', 'DNSUUID'); + expect(mockGetConvertedObjectId).toHaveBeenCalledTimes(1); + expect(mockGetConvertedObjectId).toHaveBeenCalledWith('foo-namespace', 'dog', 'hungry'); expect(actual).toEqual([ { id: 'uuidv5', @@ -1204,7 +1214,7 @@ describe('DocumentMigrator', () => { it('in the default space', () => { const actual = migrator.migrateAndConvert(obj); - expect(mockUuidv5).not.toHaveBeenCalled(); + expect(mockGetConvertedObjectId).not.toHaveBeenCalled(); expect(actual).toEqual([ { id: 'pretty', @@ -1220,9 +1230,19 @@ describe('DocumentMigrator', () => { it('in a non-default space', () => { const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); - expect(mockUuidv5).toHaveBeenCalledTimes(2); - expect(mockUuidv5).toHaveBeenNthCalledWith(1, 'foo-namespace:toy:favorite', 'DNSUUID'); - expect(mockUuidv5).toHaveBeenNthCalledWith(2, 'foo-namespace:dog:pretty', 'DNSUUID'); + expect(mockGetConvertedObjectId).toHaveBeenCalledTimes(2); + expect(mockGetConvertedObjectId).toHaveBeenNthCalledWith( + 1, + 'foo-namespace', + 'toy', + 'favorite' + ); + expect(mockGetConvertedObjectId).toHaveBeenNthCalledWith( + 2, + 'foo-namespace', + 'dog', + 'pretty' + ); expect(actual).toEqual([ { id: 'uuidv5', diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts index 23f5d075d72e3..040aa81cdab3a 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts @@ -65,7 +65,7 @@ import { MigrationLogger } from './migration_logger'; import { TransformSavedObjectDocumentError } from '.'; import { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { SavedObjectMigrationFn, SavedObjectMigrationMap } from '../types'; -import { DEFAULT_NAMESPACE_STRING } from '../../service/lib/utils'; +import { DEFAULT_NAMESPACE_STRING, SavedObjectsUtils } from '../../service/lib/utils'; import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../object_types'; const DEFAULT_MINIMUM_CONVERT_VERSION = '8.0.0'; @@ -556,7 +556,7 @@ function convertNamespaceType(doc: SavedObjectUnsanitizedDoc) { } const { id: originId, type } = otherAttrs; - const id = deterministicallyRegenerateObjectId(namespace, type, originId!); + const id = SavedObjectsUtils.getConvertedObjectId(namespace, type, originId!); if (namespace !== undefined) { const legacyUrlAlias: SavedObjectUnsanitizedDoc = { id: `${namespace}:${type}:${originId}`, @@ -616,7 +616,9 @@ function getReferenceTransforms(typeRegistry: ISavedObjectTypeRegistry): Transfo references: references.map(({ type, id, ...attrs }) => ({ ...attrs, type, - id: types.has(type) ? deterministicallyRegenerateObjectId(namespace, type, id) : id, + id: types.has(type) + ? SavedObjectsUtils.getConvertedObjectId(namespace, type, id) + : id, })), }, additionalDocs: [], diff --git a/src/core/server/saved_objects/migrations/core/__mocks__/index.ts b/src/core/server/saved_objects/service/lib/utils.test.mock.ts similarity index 80% rename from src/core/server/saved_objects/migrations/core/__mocks__/index.ts rename to src/core/server/saved_objects/service/lib/utils.test.mock.ts index d290bc57e2669..9e2b5c66bc91a 100644 --- a/src/core/server/saved_objects/migrations/core/__mocks__/index.ts +++ b/src/core/server/saved_objects/service/lib/utils.test.mock.ts @@ -6,8 +6,11 @@ * Side Public License, v 1. */ +const mockUuidv1 = jest.fn().mockReturnValue('uuidv1'); +jest.mock('uuid/v1', () => mockUuidv1); + const mockUuidv5 = jest.fn().mockReturnValue('uuidv5'); Object.defineProperty(mockUuidv5, 'DNS', { value: 'DNSUUID', writable: false }); jest.mock('uuid/v5', () => mockUuidv5); -export { mockUuidv5 }; +export { mockUuidv1, mockUuidv5 }; diff --git a/src/core/server/saved_objects/service/lib/utils.test.ts b/src/core/server/saved_objects/service/lib/utils.test.ts index 7101eafc72a32..0f502d8bfb9f1 100644 --- a/src/core/server/saved_objects/service/lib/utils.test.ts +++ b/src/core/server/saved_objects/service/lib/utils.test.ts @@ -6,14 +6,11 @@ * Side Public License, v 1. */ -import uuid from 'uuid'; +import { mockUuidv1, mockUuidv5 } from './utils.test.mock'; + import { SavedObjectsFindOptions } from '../../types'; import { SavedObjectsUtils } from './utils'; -jest.mock('uuid', () => ({ - v1: jest.fn().mockReturnValue('mock-uuid'), -})); - describe('SavedObjectsUtils', () => { const { namespaceIdToString, @@ -21,6 +18,7 @@ describe('SavedObjectsUtils', () => { createEmptyFindResponse, generateId, isRandomId, + getConvertedObjectId, } = SavedObjectsUtils; describe('#namespaceIdToString', () => { @@ -80,8 +78,8 @@ describe('SavedObjectsUtils', () => { describe('#generateId', () => { it('returns a valid uuid', () => { - expect(generateId()).toBe('mock-uuid'); - expect(uuid.v1).toHaveBeenCalled(); + expect(generateId()).toBe('uuidv1'); // default return value for mockUuidv1 + expect(mockUuidv1).toHaveBeenCalled(); }); }); @@ -93,4 +91,20 @@ describe('SavedObjectsUtils', () => { expect(isRandomId(undefined)).toBe(false); }); }); + + describe('#getConvertedObjectId', () => { + it('does not change the object ID if namespace is undefined or "default"', () => { + for (const namespace of [undefined, 'default']) { + const result = getConvertedObjectId(namespace, 'type', 'oldId'); + expect(result).toBe('oldId'); + expect(mockUuidv5).not.toHaveBeenCalled(); + } + }); + + it('changes the object ID if namespace is defined and not "default"', () => { + const result = getConvertedObjectId('namespace', 'type', 'oldId'); + expect(result).toBe('uuidv5'); // default return value for mockUuidv5 + expect(mockUuidv5).toHaveBeenCalledWith('namespace:type:oldId', 'DNSUUID'); + }); + }); }); diff --git a/src/core/server/saved_objects/service/lib/utils.ts b/src/core/server/saved_objects/service/lib/utils.ts index 494ac6ce9fad5..6942b3b376232 100644 --- a/src/core/server/saved_objects/service/lib/utils.ts +++ b/src/core/server/saved_objects/service/lib/utils.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import uuid from 'uuid'; +import uuidv1 from 'uuid/v1'; +import uuidv5 from 'uuid/v5'; import { SavedObjectsFindOptions } from '../../types'; import { SavedObjectsFindResponse } from '..'; @@ -65,7 +66,7 @@ export class SavedObjectsUtils { * Generates a random ID for a saved objects. */ public static generateId() { - return uuid.v1(); + return uuidv1(); } /** @@ -77,4 +78,19 @@ export class SavedObjectsUtils { public static isRandomId(id: string | undefined) { return typeof id === 'string' && UUID_REGEX.test(id); } + + /** + * Uses a single-namespace object's "legacy ID" to determine what its new ID will be after it is converted to a multi-namespace type. + * + * @param {string} namespace The namespace of the saved object before it is converted. + * @param {string} type The type of the saved object before it is converted. + * @param {string} id The ID of the saved object before it is converted. + * @returns {string} The ID of the saved object after it is converted. + */ + public static getConvertedObjectId(namespace: string | undefined, type: string, id: string) { + if (SavedObjectsUtils.namespaceIdToString(namespace) === DEFAULT_NAMESPACE_STRING) { + return id; // Objects that exist in the Default space do not get new IDs when they are converted. + } + return uuidv5(`${namespace}:${type}:${id}`, uuidv5.DNS); // The uuidv5 namespace constant (uuidv5.DNS) is arbitrary. + } } diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index fbc51acfdc8ef..7f2ce38a5bdd4 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -3163,6 +3163,7 @@ export interface SavedObjectsUpdateResponse extends Omit({ page, perPage, }: SavedObjectsFindOptions) => SavedObjectsFindResponse; static generateId(): string; + static getConvertedObjectId(namespace: string | undefined, type: string, id: string): string; static isRandomId(id: string | undefined): boolean; static namespaceIdToString: (namespace?: string | undefined) => string; static namespaceStringToId: (namespace: string) => string | undefined; 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 644dc32dd8140..e2d81c5ae1752 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/dev/typescript/build_ts_refs_cli.ts b/src/dev/typescript/build_ts_refs_cli.ts index 00f298887ee17..ad7807eb87c66 100644 --- a/src/dev/typescript/build_ts_refs_cli.ts +++ b/src/dev/typescript/build_ts_refs_cli.ts @@ -41,7 +41,7 @@ export async function runBuildRefsCli() { const cacheEnabled = process.env.BUILD_TS_REFS_CACHE_ENABLE !== 'false' && !!flags.cache; const doCapture = process.env.BUILD_TS_REFS_CACHE_CAPTURE === 'true'; const doClean = !!flags.clean || doCapture; - const doInitCache = cacheEnabled && !doClean; + const doInitCache = cacheEnabled && !doCapture; if (doClean) { log.info('deleting', outDirs.length, 'ts output directories'); 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 dd6eeb92ef932..cf643fa7d271f 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/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap index afe339f3f43a2..70a21438754bd 100644 --- a/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap +++ b/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap @@ -308,6 +308,7 @@ exports[`DashboardEmptyScreen renders correctly with edit mode 1`] = ` >
    Create content that tells a story about your data. @@ -666,6 +667,7 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = ` >
    You need additional privileges to edit this dashboard.
    @@ -1023,6 +1025,7 @@ exports[`DashboardEmptyScreen renders correctly with view mode 1`] = ` >

    Click edit in the menu bar above to start adding panels. diff --git a/src/plugins/data/README.mdx b/src/plugins/data/README.mdx index fba5cbd6d48e1..c2da9a686cacb 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 89ccdfcc0479e..406da4e56d150 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 8bf44fe48a589..f108e69c68d56 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 d4477e72b64c4..68f817d1b1484 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 cd05c613f793f..2bc383db6f530 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 73f015ca5517c..f95de90955b65 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 9a33b0cfa6f1c..f4f94856c7226 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 732b9c7c8eab0..fe872ae929899 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 508bddb7a4096..7c111f7666544 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 b3f2605723c02..48bcdf6982b67 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 8715f8feb067a..a80e97b4e2cab 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 cecf3b8c07d1a..64628f7165f27 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({ @@ -528,7 +528,7 @@ export class IndexPatternsService { const indexPattern = await this.create(spec, skipFetchFields); const createdIndexPattern = await this.createSavedObject(indexPattern, override); await this.setDefault(createdIndexPattern.id!); - return createdIndexPattern; + return createdIndexPattern!; } /** diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index d05a7ea6e2d93..56a79dd9cfada 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 1db60db507f0f..375de79f7f081 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 c147708b5b2f9..0cefd6b73b336 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 1e4d97a007301..51ea6d081d139 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 7e4289e23cfae..c9ab1617929dc 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 60a58a68e18e0..202cc59c83c0f 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 51c71cd1a2994..9f7559c6fe34f 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 ff22af720bde6..50c8966d284a0 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 1cf80a6d0085c..76112980c55fb 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 175b6832b3c85..2aead866c6b60 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 b78980cb5136e..e200f9bf02536 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 c982ba1e7a293..c72976e3412a6 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 e425d0701155b..e6faa6bd0b1a7 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 ea7d7690fb876..5c3b016dc631e 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 14ae24a2a5626..563321cf56fef 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 566532085059d..0000000000000 --- 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 c0eebf3402771..467efa98934ec 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 e3ecacfbda5ad..fb35d00545040 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) */ @@ -152,7 +83,9 @@ export { IndexPatternSpec, IndexPatternLoadExpressionFunctionDefinition, fieldList, + GetFieldsOptions, INDEX_PATTERN_SAVED_OBJECT_TYPE, + AggregationRestrictions, IndexPatternType, } from '../common'; diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 9e01350add2e3..ba1cba987c0b9 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 7a071dbffe696..46b1d4a14be78 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 ce105b8cc94ac..64b73ded88eef 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); @@ -537,6 +538,22 @@ export class AggParamType extends Ba makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig; } +// Warning: (ae-missing-release-tag) "AggregationRestrictions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +type AggregationRestrictions = Record; + +export { AggregationRestrictions } + +export { AggregationRestrictions as IndexPatternAggRestrictions } + // Warning: (ae-forgotten-export) The symbol "AggsCommonStart" needs to be exported by the entry point index.d.ts // // @public @@ -573,13 +590,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 +654,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 +669,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 +683,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 +905,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) // @@ -1112,6 +1003,24 @@ export function getEsPreference(uiSettings: IUiSettingsClient_2, sessionId?: str // @public (undocumented) export function getEsQueryConfig(config: KibanaConfig): EsQueryConfig_2; +// Warning: (ae-missing-release-tag) "GetFieldsOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface GetFieldsOptions { + // (undocumented) + allowNoIndex?: boolean; + // (undocumented) + lookBack?: boolean; + // (undocumented) + metaFields?: string[]; + // (undocumented) + pattern: string; + // (undocumented) + rollupIndex?: string; + // (undocumented) + type?: string; +} + // Warning: (ae-missing-release-tag) "getKbnTypeNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @deprecated (undocumented) @@ -1191,16 +1100,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 +1153,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; @@ -1437,18 +1337,6 @@ export class IndexPattern implements IIndexPattern { version: string | undefined; } -// Warning: (ae-missing-release-tag) "AggregationRestrictions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type IndexPatternAggRestrictions = Record; - // Warning: (ae-missing-release-tag) "IndexPatternAttributes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -1640,7 +1528,6 @@ export class IndexPatternsService { getDefault: () => Promise; getDefaultId: () => Promise; getFieldsForIndexPattern: (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions | undefined) => Promise; - // Warning: (ae-forgotten-export) The symbol "GetFieldsOptions" needs to be exported by the entry point index.d.ts getFieldsForWildcard: (options: GetFieldsOptions) => Promise; getIds: (refresh?: boolean) => Promise; getIdsWithTitle: (refresh?: boolean) => Promise; + aggs?: Record; // (undocumented) params?: { rollup_index: string; @@ -2578,13 +2465,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 +2494,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:227:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:227:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:227:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:229:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:230:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:239:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:240:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:241:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:242:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:246:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:247:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:250:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:251:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:254: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 cd2ee69d33996..563fd5d044073 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 f603bd733f601..3f91ced6de950 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 b964ddbd2a379..613e3850c922e 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 5ba4ba2bc48af..d8bfcfdb6ddb1 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/__snapshots__/data_view.test.tsx.snap b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap index 1e7b59d8a9e76..612ffdcf5029e 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap +++ b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap @@ -196,6 +196,7 @@ exports[`Inspector Data View component should render empty state 1`] = ` > 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 e79a1c2b52e03..57e586eaf12f8 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 10c74bbc99dd0..498fe8d7fcca0 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 e5656ceabc151..d010819d14e94 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 4269f15127daf..c3cdc65d3fa04 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 786dd30dbabd0..af104dea72af7 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 7b73802f1a34d..e04095ed26a46 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 0a4f54ee339a1..fdff2a4a8118c 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 96927728f2f2f..197d8fd7caa99 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 314cb2c3acbf8..e6db3c76ef9ba 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 cc7981220fa6f..5b4ff121f3c77 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 9d5e598588f25..51436dabe05a0 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 0723c4b2d85c2..360529ad5a735 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 9c95878af631e..6e4e89f7538fd 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 04469e0ef4276..f612e0ec2cc8d 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 5e3a025d8c7ba..0f6c86df0db64 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 80fb8a570f78b..6b356446850e6 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/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap index 5f6dabc26659f..dfded530c6983 100644 --- a/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap +++ b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap @@ -167,6 +167,7 @@ exports[`Source Viewer component renders error state 1`] = ` >

    { 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 840a59f3d3092..829063aa07d02 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 f653c6a4f82a2..d3a6964619cae 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 d058d0c6a6574..1d226c936a977 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 926f907af8fe9..a92a6756b335c 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 0812dcc795dc9..586d42ffa7a31 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 76fe4404e2418..b981eca5db441 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 837031b59dc5c..da849fae1e6ab 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 c8c98d010dc61..36470cbd14874 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 9d8b7ba1c369d..7f11b6377836d 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 77f4274759ba3..d01acf571f9d9 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 a402975c672e2..3fd70c8ce74a8 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 c54e32452f267..6111cbf838b79 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 655cf315a05a4..726f2c31e7825 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 de77736503f86..b3ed90329d6d4 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 63d3506dda0bb..ba5b38ba8b24f 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 c6aba38cf376d..149da871761e2 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 4c68a4c056b82..fd8bbcbc46541 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 3e808d90f0409..07d9ed4cd535e 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 86e44b69c05bf..e94efc88be20f 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 9e96f5cc1a6bd..675ec897c2b70 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 a3d763196e11f..f2395eef29c54 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 c09106751815f..9c9112f45373b 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 5771c0166075d..3cfa0241c6f41 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 0000000000000..33a44925982ec --- /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 0000000000000..53f7471913dc3 --- /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/field_formats/jest.config.js b/src/plugins/field_formats/jest.config.js new file mode 100644 index 0000000000000..ea20fcfec6d09 --- /dev/null +++ b/src/plugins/field_formats/jest.config.js @@ -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 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. + */ + +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 0000000000000..ee5529697851d --- /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 0000000000000..7928d81b1f565 --- /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 0000000000000..f765513fb4c4c --- /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 1d74e6095427d..acc051afd6b1d 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 f94d0e47a8ed4..c2e0b3f929067 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 8b8b00b2b8746..83a2dc60d8d95 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 883af75939220..53f8cf3a17494 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 06d007b2aff00..808413667918d 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 041d0d7dd0e1c..0e90aa9b48c8e --- 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 0000000000000..44de8fde558ec --- /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 3353f4a959b7e..86f0b2a7dc875 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 655c76d328f47..d47475f6274fa 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 a86f3c894c543..bf2151db0072c 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 0bfa9f89117e8..266d724ce5e22 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 fa94e5dcef9cf..e2c64807433a9 --- 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 0000000000000..eaf9c9ffdc390 --- /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 0000000000000..f24ddc60e1f97 --- /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 0000000000000..4382ab1051c1d --- /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_editor/README.md b/src/plugins/index_pattern_editor/README.md new file mode 100644 index 0000000000000..3ffb589230732 --- /dev/null +++ b/src/plugins/index_pattern_editor/README.md @@ -0,0 +1,48 @@ +# Index pattern editor + +Create index patterns from within Kibana apps. + +## How to use + +You first need to add in your kibana.json the "`indexPatternEditor`" plugin as a required dependency of your plugin. + +You will then receive in the start contract of the indexPatternEditor plugin the following API: + +### `userPermissions.editIndexPattern(): boolean` + +Convenience method that uses the `core.application.capabilities` api to determine whether the user can create or edit the index pattern. + +### `openEditor(options: IndexPatternEditorProps): CloseEditor` + +Use this method to display the index pattern editor to create an index pattern. + +#### `options` + +`onSave: (indexPattern: IndexPattern) => void` (**required**) + +You must provide an `onSave` handler to be notified when an index pattern has been created/updated. This handler is called after the index pattern has been persisted as a saved object. + +`onCancel: () => void;` (optional) + +You can optionally pass an `onCancel` handler which is called when the index pattern creation flyout is closed wihtout creating an index pattern. + +`defaultTypeIsRollup: boolean` (optional, default false) + +The default index pattern type can be optionally specified as `rollup`. + +`requireTimestampField: boolean` (optional, default false) + +The editor can require a timestamp field on the index pattern. + +### IndexPatternEditorComponent + +This the React component interface equivalent to `openEditor`. It takes the same arguments - + +```tsx + +``` diff --git a/src/plugins/index_pattern_editor/jest.config.js b/src/plugins/index_pattern_editor/jest.config.js new file mode 100644 index 0000000000000..0a018a42d06e6 --- /dev/null +++ b/src/plugins/index_pattern_editor/jest.config.js @@ -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 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/index_pattern_editor'], +}; diff --git a/src/plugins/index_pattern_editor/kibana.json b/src/plugins/index_pattern_editor/kibana.json new file mode 100644 index 0000000000000..b4a1bea555616 --- /dev/null +++ b/src/plugins/index_pattern_editor/kibana.json @@ -0,0 +1,13 @@ +{ + "id": "indexPatternEditor", + "version": "kibana", + "server": false, + "ui": true, + "requiredPlugins": ["data"], + "requiredBundles": ["kibanaReact", "esUiShared"], + "owner": { + "name": "App Services", + "githubTeam": "kibana-app-services" + }, + "description": "This plugin provides the ability to create index patterns via a modal flyout from any kibana app" +} diff --git a/src/plugins/index_pattern_editor/public/components/_templates.scss b/src/plugins/index_pattern_editor/public/components/_templates.scss new file mode 100644 index 0000000000000..5303537bddabc --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/_templates.scss @@ -0,0 +1,11 @@ +%inp-empty-state-footer { + background: $euiColorLightestShade; + margin: 0 (-$euiSizeL) (-$euiSizeL); + padding: $euiSizeL; + border-radius: 0 0 $euiBorderRadius $euiBorderRadius; + + // sass-lint:disable-block mixins-before-declarations + @include euiBreakpoint('xs', 's') { + text-align: center; + } +} diff --git a/src/plugins/index_pattern_editor/public/components/_variables.scss b/src/plugins/index_pattern_editor/public/components/_variables.scss new file mode 100644 index 0000000000000..5da25a91bd77c --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/_variables.scss @@ -0,0 +1 @@ +$inpEmptyStateMaxWidth: $euiSizeXXL * 19; diff --git a/src/plugins/index_pattern_editor/public/components/advanced_params_content/advanced_params_content.tsx b/src/plugins/index_pattern_editor/public/components/advanced_params_content/advanced_params_content.tsx new file mode 100644 index 0000000000000..ad876fed51801 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/advanced_params_content/advanced_params_content.tsx @@ -0,0 +1,66 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +import { UseField, TextField, ToggleField } from '../../shared_imports'; +import { IndexPatternConfig } from '../../types'; + +import { AdvancedParamsSection } from './advanced_params_section'; + +const allowHiddenAriaLabel = i18n.translate('indexPatternEditor.form.allowHiddenAriaLabel', { + defaultMessage: 'Allow hidden and system indices', +}); + +const customIndexPatternIdLabel = i18n.translate( + 'indexPatternEditor.form.customIndexPatternIdLabel', + { + defaultMessage: 'Custom index pattern ID', + } +); + +interface AdvancedParamsContentProps { + disableAllowHidden: boolean; +} + +export const AdvancedParamsContent = ({ disableAllowHidden }: AdvancedParamsContentProps) => ( + + + + + path={'allowHidden'} + component={ToggleField} + data-test-subj="allowHiddenField" + componentProps={{ + euiFieldProps: { + 'aria-label': allowHiddenAriaLabel, + disabled: disableAllowHidden, + }, + }} + /> + + + + + + + path={'id'} + component={TextField} + data-test-subj="savedObjectIdField" + componentProps={{ + euiFieldProps: { + 'aria-label': customIndexPatternIdLabel, + }, + }} + /> + + + +); diff --git a/src/plugins/index_pattern_editor/public/components/advanced_params_content/advanced_params_section.tsx b/src/plugins/index_pattern_editor/public/components/advanced_params_content/advanced_params_section.tsx new file mode 100644 index 0000000000000..b313fddb8ee2d --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/advanced_params_content/advanced_params_section.tsx @@ -0,0 +1,44 @@ +/* + * 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 React, { useState, useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { EuiButtonEmpty, EuiSpacer } from '@elastic/eui'; + +interface Props { + children: React.ReactNode; +} + +export const AdvancedParamsSection = ({ children }: Props) => { + const [isVisible, setIsVisible] = useState(false); + + const toggleIsVisible = useCallback(() => { + setIsVisible(!isVisible); + }, [isVisible]); + + return ( + <> + + {isVisible + ? i18n.translate('indexPatternEditor.editor.form.advancedSettings.hideButtonLabel', { + defaultMessage: 'Hide advanced settings', + }) + : i18n.translate('indexPatternEditor.editor.form.advancedSettings.showButtonLabel', { + defaultMessage: 'Show advanced settings', + })} + + +
    + + {/* We ned to wrap the children inside a "div" to have our css :first-child rule */} +
    {children}
    +
    + + ); +}; diff --git a/src/plugins/index_pattern_editor/public/components/advanced_params_content/index.ts b/src/plugins/index_pattern_editor/public/components/advanced_params_content/index.ts new file mode 100644 index 0000000000000..a285004d1f45d --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/advanced_params_content/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { AdvancedParamsContent } from './advanced_params_content'; diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/__snapshots__/empty_state.test.tsx.snap b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap similarity index 71% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/__snapshots__/empty_state.test.tsx.snap rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap index 75b8177d9dac3..a104c36e3a8a0 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/__snapshots__/empty_state.test.tsx.snap +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`EmptyState should render normally 1`] = ` +exports[`EmptyIndexListPrompt should render normally 1`] = ` @@ -38,7 +38,7 @@ exports[`EmptyState should render normally 1`] = ` description={ } @@ -53,7 +53,7 @@ exports[`EmptyState should render normally 1`] = ` title={ } @@ -65,7 +65,7 @@ exports[`EmptyState should render normally 1`] = ` description={ } @@ -80,7 +80,7 @@ exports[`EmptyState should render normally 1`] = ` title={ } @@ -92,7 +92,7 @@ exports[`EmptyState should render normally 1`] = ` description={ } @@ -107,7 +107,7 @@ exports[`EmptyState should render normally 1`] = ` title={ } @@ -131,18 +131,18 @@ exports[`EmptyState should render normally 1`] = ` Object { "description": , "title": , }, @@ -164,7 +164,7 @@ exports[`EmptyState should render normally 1`] = ` > @@ -175,7 +175,7 @@ exports[`EmptyState should render normally 1`] = ` , "title": , }, @@ -184,33 +184,33 @@ exports[`EmptyState should render normally 1`] = ` /> + + + + + , + } + } + /> +
    - - - - - , - } - } - /> - `; diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/empty_state.scss b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.scss similarity index 86% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/empty_state.scss rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.scss index 37889b9d7c483..845ddfb3eb234 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/empty_state.scss +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.scss @@ -1,5 +1,5 @@ -@import '../../../variables'; -@import '../../../templates'; +@import '../../variables'; +@import '../../templates'; .inpEmptyState { // override EUI specificity diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/empty_state.test.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.test.tsx similarity index 69% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/empty_state.test.tsx rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.test.tsx index ed68d4d4ddd64..f5a996a441515 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/empty_state.test.tsx +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.test.tsx @@ -7,14 +7,11 @@ */ import React from 'react'; -import { EmptyState } from '../empty_state'; +import { EmptyIndexListPrompt } from './empty_index_list_prompt'; import { shallow } from 'enzyme'; import sinon from 'sinon'; import { findTestSubject } from '@elastic/eui/lib/test'; import { mountWithIntl } from '@kbn/test/jest'; -import { docLinksServiceMock } from '../../../../../../core/public/mocks'; - -const docLinks = docLinksServiceMock.createStartContract(); jest.mock('react-router-dom', () => ({ useHistory: () => ({ @@ -22,14 +19,16 @@ jest.mock('react-router-dom', () => ({ }), })); -describe('EmptyState', () => { +describe('EmptyIndexListPrompt', () => { it('should render normally', () => { const component = shallow( - {}} - navigateToApp={async () => {}} - canSave={true} + createAnyway={() => {}} + closeFlyout={() => {}} + addDataUrl={'http://elastic.co'} + navigateToApp={async (appId) => {}} + canSaveIndexPattern={true} /> ); @@ -42,11 +41,13 @@ describe('EmptyState', () => { const onRefreshHandler = sinon.stub(); const component = mountWithIntl( - {}} - canSave={true} + createAnyway={() => {}} + closeFlyout={() => {}} + addDataUrl={'http://elastic.co'} + navigateToApp={async (appId) => {}} + canSaveIndexPattern={true} /> ); diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/empty_state.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx similarity index 72% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/empty_state.tsx rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx index af49e8c36fe3b..1331eb9b7c4ac 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/empty_state.tsx +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx @@ -6,10 +6,9 @@ * Side Public License, v 1. */ -import './empty_state.scss'; +import './empty_index_list_prompt.scss'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { DocLinksStart, ApplicationStart } from 'kibana/public'; import { EuiPageContentHeader, EuiPageContentHeaderSection, @@ -26,30 +25,34 @@ import { EuiText, EuiFlexGroup, } from '@elastic/eui'; -import { useHistory } from 'react-router-dom'; -import { reactRouterNavigate } from '../../../../../../plugins/kibana_react/public'; -export const EmptyState = ({ +import { ApplicationStart } from 'src/core/public'; + +export const EmptyIndexListPrompt = ({ onRefresh, + closeFlyout, + createAnyway, + canSaveIndexPattern, + addDataUrl, navigateToApp, - docLinks, - canSave, }: { onRefresh: () => void; + closeFlyout: () => void; + createAnyway: () => void; + canSaveIndexPattern: boolean; + addDataUrl: string; navigateToApp: ApplicationStart['navigateToApp']; - docLinks: DocLinksStart; - canSave: boolean; }) => { - const createAnyway = ( + const createAnywayLink = ( + createAnyway()} data-test-subj="createAnyway"> @@ -74,7 +77,7 @@ export const EmptyState = ({

    @@ -87,17 +90,20 @@ export const EmptyState = ({ navigateToApp('home', { path: '#/tutorial_directory' })} + onClick={() => { + navigateToApp('home', { path: '#/tutorial_directory' }); + closeFlyout(); + }} icon={} title={ } description={ } @@ -110,13 +116,13 @@ export const EmptyState = ({ icon={} title={ } description={ } @@ -125,17 +131,20 @@ export const EmptyState = ({ navigateToApp('home', { path: '#/tutorial_directory/sampleData' })} + onClick={() => { + navigateToApp('home', { path: '#/tutorial_directory/sampleData' }); + closeFlyout(); + }} icon={} title={ } description={ } @@ -151,14 +160,14 @@ export const EmptyState = ({ { title: ( ), description: ( - + @@ -173,14 +182,14 @@ export const EmptyState = ({ { title: ( ), description: ( {' '} @@ -191,11 +200,11 @@ export const EmptyState = ({ /> + + {canSaveIndexPattern && createAnywayLink}
    - - {canSave && createAnyway} ); }; diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/index.ts b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/index.ts new file mode 100644 index 0000000000000..ae5dcecf069c7 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { EmptyIndexListPrompt } from './empty_index_list_prompt'; diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/__snapshots__/empty_index_pattern_prompt.test.tsx.snap b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/__snapshots__/empty_index_pattern_prompt.test.tsx.snap similarity index 66% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/__snapshots__/empty_index_pattern_prompt.test.tsx.snap rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/__snapshots__/empty_index_pattern_prompt.test.tsx.snap index bc69fa29e6904..6216f04c0a1a9 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/__snapshots__/empty_index_pattern_prompt.test.tsx.snap +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/__snapshots__/empty_index_pattern_prompt.test.tsx.snap @@ -19,7 +19,15 @@ exports[`EmptyIndexPatternPrompt should render normally 1`] = ` className="inpEmptyIndexPatternPrompt__illustration" grow={1} > - + + } + > + +

    - - +
    @@ -79,19 +83,19 @@ exports[`EmptyIndexPatternPrompt should render normally 1`] = ` > diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/assets/index_pattern_illustration.scss b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/assets/index_pattern_illustration.scss similarity index 100% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/assets/index_pattern_illustration.scss rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/assets/index_pattern_illustration.scss diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/assets/index_pattern_illustration.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/assets/index_pattern_illustration.tsx similarity index 99% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/assets/index_pattern_illustration.tsx rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/assets/index_pattern_illustration.tsx index 3666c1be7b5d2..09b18e25e9d00 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/assets/index_pattern_illustration.tsx +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/assets/index_pattern_illustration.tsx @@ -537,4 +537,5 @@ const IndexPatternIllustration = () => ( ); -export const Illustration = IndexPatternIllustration; +/* eslint-disable import/no-default-export */ +export default IndexPatternIllustration; diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.scss b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/empty_index_pattern_prompt.scss similarity index 92% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.scss rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/empty_index_pattern_prompt.scss index 11ac55b098a57..f6db2fc89f353 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.scss +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/empty_index_pattern_prompt.scss @@ -1,5 +1,5 @@ -@import '../../../variables'; -@import '../../../templates'; +@import '../../variables'; +@import '../../templates'; .inpEmptyIndexPatternPrompt { // override EUI specificity diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.test.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/empty_index_pattern_prompt.test.tsx similarity index 82% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.test.tsx rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/empty_index_pattern_prompt.test.tsx index 1cd0574a35d15..4cf3c4b55acde 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.test.tsx +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/empty_index_pattern_prompt.test.tsx @@ -14,10 +14,9 @@ describe('EmptyIndexPatternPrompt', () => { it('should render normally', () => { const component = shallowWithI18nProvider( {} }]} - docLinksIndexPatternIntro={'testUrl'} - setBreadcrumbs={() => {}} + goToCreate={() => {}} + canSaveIndexPattern={true} + indexPatternsIntroUrl={'http://elastic.co/'} /> ); diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx similarity index 65% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx index f36f3a38c7380..054ff9d13b043 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx @@ -8,34 +8,26 @@ import './empty_index_pattern_prompt.scss'; -import React from 'react'; +import React, { lazy, Suspense } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPageContent, EuiSpacer, EuiText, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { EuiDescriptionListTitle } from '@elastic/eui'; import { EuiDescriptionListDescription, EuiDescriptionList } from '@elastic/eui'; -import { EuiLink } from '@elastic/eui'; -import { getListBreadcrumbs } from '../../breadcrumbs'; -import { IndexPatternCreationOption } from '../../types'; -import { CreateButton } from '../../create_button'; -import { Illustration } from './assets/index_pattern_illustration'; -import { ManagementAppMountParams } from '../../../../../management/public'; - +import { EuiLink, EuiButton, EuiLoadingSpinner } from '@elastic/eui'; interface Props { - canSave: boolean; - creationOptions: IndexPatternCreationOption[]; - docLinksIndexPatternIntro: string; - setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs']; + goToCreate: () => void; + canSaveIndexPattern: boolean; + indexPatternsIntroUrl: string; } +const Illustration = lazy(() => import('./assets/index_pattern_illustration')); + export const EmptyIndexPatternPrompt = ({ - canSave, - creationOptions, - docLinksIndexPatternIntro, - setBreadcrumbs, + goToCreate, + canSaveIndexPattern, + indexPatternsIntroUrl, }: Props) => { - setBreadcrumbs(getListBreadcrumbs()); - return ( - + }> + +


    - {canSave && ( - + {canSaveIndexPattern && ( + - + )}
    @@ -85,14 +84,14 @@ export const EmptyIndexPatternPrompt = ({ - + diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/index.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/index.tsx similarity index 100% rename from src/plugins/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/index.tsx rename to src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_pattern_prompt/index.tsx diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.tsx new file mode 100644 index 0000000000000..3b06fa1cff298 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_prompts.tsx @@ -0,0 +1,92 @@ +/* + * 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 React, { useState, useCallback, FC } from 'react'; + +import { useKibana } from '../../shared_imports'; + +import { MatchedItem, ResolveIndexResponseItemAlias, IndexPatternEditorContext } from '../../types'; + +import { getIndices } from '../../lib'; + +import { EmptyIndexListPrompt } from './empty_index_list_prompt'; +import { EmptyIndexPatternPrompt } from './empty_index_pattern_prompt'; +import { PromptFooter } from './prompt_footer'; + +const removeAliases = (item: MatchedItem) => + !((item as unknown) as ResolveIndexResponseItemAlias).indices; + +interface Props { + onCancel: () => void; + allSources: MatchedItem[]; + hasExistingIndexPatterns: boolean; + loadSources: () => void; +} + +export const EmptyPrompts: FC = ({ + hasExistingIndexPatterns, + allSources, + onCancel, + children, + loadSources, +}) => { + const { + services: { docLinks, application, http }, + } = useKibana(); + + const [remoteClustersExist, setRemoteClustersExist] = useState(false); + const [goToForm, setGoToForm] = useState(false); + + const hasDataIndices = allSources.some(({ name }: MatchedItem) => !name.startsWith('.')); + + useCallback(() => { + let isMounted = true; + if (!hasDataIndices) + getIndices(http, () => false, '*:*', false).then((dataSources) => { + if (isMounted) { + setRemoteClustersExist(!!dataSources.filter(removeAliases).length); + } + }); + return () => { + isMounted = false; + }; + }, [http, hasDataIndices]); + + if (!hasExistingIndexPatterns && !goToForm) { + if (!hasDataIndices && !remoteClustersExist) { + // load data + return ( + <> + setGoToForm(true)} + canSaveIndexPattern={application.capabilities.indexPatterns.save as boolean} + navigateToApp={application.navigateToApp} + addDataUrl={docLinks.links.indexPatterns.introduction} + /> + + + ); + } else { + // first time + return ( + <> + setGoToForm(true)} + indexPatternsIntroUrl={docLinks.links.indexPatterns.introduction} + canSaveIndexPattern={application.capabilities.indexPatterns.save as boolean} + /> + + + ); + } + } + + return <>{children}; +}; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/index.ts b/src/plugins/index_pattern_editor/public/components/empty_prompts/index.tsx similarity index 88% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/index.ts rename to src/plugins/index_pattern_editor/public/components/empty_prompts/index.tsx index 8f737b3a42613..20bf754e3771a 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/index.ts +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/index.tsx @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { Header } from './header'; +export { EmptyPrompts } from './empty_prompts'; diff --git a/src/plugins/index_pattern_management/public/components/create_button/index.ts b/src/plugins/index_pattern_editor/public/components/empty_prompts/prompt_footer/index.ts similarity index 88% rename from src/plugins/index_pattern_management/public/components/create_button/index.ts rename to src/plugins/index_pattern_editor/public/components/empty_prompts/prompt_footer/index.ts index 497096d2fb7bc..52b2a82651b28 100644 --- a/src/plugins/index_pattern_management/public/components/create_button/index.ts +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/prompt_footer/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { CreateButton } from './create_button'; +export { PromptFooter } from './prompt_footer'; diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/prompt_footer/prompt_footer.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/prompt_footer/prompt_footer.tsx new file mode 100644 index 0000000000000..d1ab3febc5ed3 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/prompt_footer/prompt_footer.tsx @@ -0,0 +1,42 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; + +import { EuiFlyoutFooter, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; + +const closeButtonLabel = i18n.translate( + 'indexPatternEditor.editor.emptyPrompt.flyoutCloseButtonLabel', + { + defaultMessage: 'Close', + } +); + +interface PromptFooterProps { + onCancel: () => void; +} + +export const PromptFooter = ({ onCancel }: PromptFooterProps) => { + return ( + + + + + {closeButtonLabel} + + + + + ); +}; diff --git a/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panel.tsx b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panel.tsx new file mode 100644 index 0000000000000..8f9193a47327f --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panel.tsx @@ -0,0 +1,120 @@ +/* + * 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 React, { + CSSProperties, + useState, + useLayoutEffect, + useCallback, + createContext, + useContext, +} from 'react'; +import classnames from 'classnames'; +import { EuiFlexItem } from '@elastic/eui'; + +import { useFlyoutPanelsContext } from './flyout_panels'; + +interface Context { + registerFooter: () => void; + registerContent: () => void; +} + +const flyoutPanelContext = createContext({ + registerFooter: () => {}, + registerContent: () => {}, +}); + +export interface Props { + /** Width of the panel (in percent %) */ + width?: number; + /** EUI sass background */ + backgroundColor?: 'euiPageBackground' | 'euiEmptyShade'; + /** Add a border to the panel */ + border?: 'left' | 'right'; +} + +export const Panel: React.FC> = ({ + children, + width, + className = '', + backgroundColor, + border, + ...rest +}) => { + const [config, setConfig] = useState<{ hasFooter: boolean; hasContent: boolean }>({ + hasContent: false, + hasFooter: false, + }); + + /* eslint-disable @typescript-eslint/naming-convention */ + const classes = classnames('fieldEditor__flyoutPanel', className, { + 'fieldEditor__flyoutPanel--pageBackground': backgroundColor === 'euiPageBackground', + 'fieldEditor__flyoutPanel--emptyShade': backgroundColor === 'euiEmptyShade', + 'fieldEditor__flyoutPanel--leftBorder': border === 'left', + 'fieldEditor__flyoutPanel--rightBorder': border === 'right', + 'fieldEditor__flyoutPanel--withContent': config.hasContent, + }); + /* eslint-enable @typescript-eslint/naming-convention */ + + const { addPanel } = useFlyoutPanelsContext(); + + const registerContent = useCallback(() => { + setConfig((prev) => { + return { + ...prev, + hasContent: true, + }; + }); + }, []); + + const registerFooter = useCallback(() => { + setConfig((prev) => { + if (!prev.hasContent) { + throw new Error( + 'You need to add a when you add a ' + ); + } + return { + ...prev, + hasFooter: true, + }; + }); + }, []); + + useLayoutEffect(() => { + const removePanel = addPanel({ width }); + + return removePanel; + }, [width, addPanel]); + + const styles: CSSProperties = {}; + + if (width) { + styles.flexBasis = `${width}%`; + } + + return ( + + +
    + {children} +
    +
    +
    + ); +}; + +export const useFlyoutPanelContext = (): Context => { + const ctx = useContext(flyoutPanelContext); + + if (ctx === undefined) { + throw new Error('useFlyoutPanel() must be used within a '); + } + + return ctx; +}; diff --git a/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels.scss b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels.scss new file mode 100644 index 0000000000000..8ba2770003954 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels.scss @@ -0,0 +1,42 @@ +.fieldEditor__flyoutPanels { + height: 100%; +} + +.fieldEditor__flyoutPanel { + height: 100%; + overflow-y: auto; + padding: $euiSizeL $euiSizeL 0 $euiSizeL; + + &--pageBackground { + background-color: $euiPageBackgroundColor; + } + &--emptyShade { + background-color: $euiColorEmptyShade; + } + &--leftBorder { + border-left: $euiBorderThin; + } + &--rightBorder { + border-right: $euiBorderThin; + } + &--withContent { + padding: 0; + overflow-y: hidden; + display: flex; + flex-direction: column; + } + + &__header { + padding: 0 !important; + } + + &__content { + flex: 1; + overflow-y: auto; + padding: $euiSizeL; + } + + &__footer { + flex: 0; + } +} diff --git a/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels.tsx b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels.tsx new file mode 100644 index 0000000000000..c878090ec7c1b --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels.tsx @@ -0,0 +1,119 @@ +/* + * 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 React, { + useState, + createContext, + useContext, + useCallback, + useMemo, + useLayoutEffect, +} from 'react'; +import { EuiFlexGroup, EuiFlexGroupProps } from '@elastic/eui'; + +import './flyout_panels.scss'; + +interface Panel { + width?: number; +} + +interface Context { + addPanel: (panel: Panel) => () => void; +} + +let idx = 0; + +const panelId = () => idx++; + +const flyoutPanelsContext = createContext({ + addPanel() { + return () => {}; + }, +}); + +export interface Props { + /** + * The total max width with all the panels in the DOM + * Corresponds to the "maxWidth" prop passed to the EuiFlyout + */ + maxWidth: number; + /** The className selector of the flyout */ + flyoutClassName: string; + /** The size between the panels. Corresponds to EuiFlexGroup gutterSize */ + gutterSize?: EuiFlexGroupProps['gutterSize']; +} + +export const Panels: React.FC = ({ maxWidth, flyoutClassName, ...props }) => { + const flyoutDOMelement = useMemo(() => { + const el = document.getElementsByClassName(flyoutClassName); + + if (el.length === 0) { + // throw new Error(`Flyout with className "${flyoutClassName}" not found.`); + return null; + } + + return el.item(0) as HTMLDivElement; + }, [flyoutClassName]); + + const [panels, setPanels] = useState<{ [id: number]: Panel }>({}); + + const removePanel = useCallback((id: number) => { + setPanels((prev) => { + const { [id]: panelToRemove, ...rest } = prev; + return rest; + }); + }, []); + + const addPanel = useCallback( + (panel: Panel) => { + const nextId = panelId(); + setPanels((prev) => { + return { ...prev, [nextId]: panel }; + }); + return removePanel.bind(null, nextId); + }, + [removePanel] + ); + + const ctx: Context = useMemo( + () => ({ + addPanel, + }), + [addPanel] + ); + + useLayoutEffect(() => { + if (!flyoutDOMelement) { + return; + } + + const totalPercentWidth = Math.min( + 100, + Object.values(panels).reduce((acc, { width = 0 }) => acc + width, 0) + ); + const currentWidth = (maxWidth * totalPercentWidth) / 100; + + flyoutDOMelement.style.maxWidth = `${currentWidth}px`; + }, [panels, maxWidth, flyoutClassName, flyoutDOMelement]); + + return ( + + + + ); +}; + +export const useFlyoutPanelsContext = (): Context => { + const ctx = useContext(flyoutPanelsContext); + + if (ctx === undefined) { + throw new Error(' must be used within a wrapper'); + } + + return ctx; +}; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/header.test.tsx b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels_content.tsx similarity index 50% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/header.test.tsx rename to src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels_content.tsx index 444e5c49cb497..a25d4a03a83c8 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/header.test.tsx +++ b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels_content.tsx @@ -5,15 +5,16 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import React, { useEffect } from 'react'; -import React from 'react'; -import { Header } from '../header'; -import { shallow } from 'enzyme'; +import { useFlyoutPanelContext } from './flyout_panel'; -describe('Header', () => { - it('should render normally', () => { - const component = shallow(
    ); +export const PanelContent: React.FC = (props) => { + const { registerContent } = useFlyoutPanelContext(); - expect(component).toMatchSnapshot(); - }); -}); + useEffect(() => { + registerContent(); + }, [registerContent]); + + return
    ; +}; diff --git a/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels_footer.tsx b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels_footer.tsx new file mode 100644 index 0000000000000..8a987420dd84b --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels_footer.tsx @@ -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 React, { useEffect } from 'react'; +import { EuiFlyoutFooter, EuiFlyoutFooterProps } from '@elastic/eui'; + +import { useFlyoutPanelContext } from './flyout_panel'; + +export const PanelFooter: React.FC< + { children: React.ReactNode } & Omit +> = (props) => { + const { registerFooter } = useFlyoutPanelContext(); + + useEffect(() => { + registerFooter(); + }, [registerFooter]); + + return ; +}; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/loading_state.test.tsx b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels_header.tsx similarity index 53% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/loading_state.test.tsx rename to src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels_header.tsx index ac10848500f63..00edf1c637fc1 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/loading_state.test.tsx +++ b/src/plugins/index_pattern_editor/public/components/flyout_panels/flyout_panels_header.tsx @@ -7,13 +7,13 @@ */ import React from 'react'; -import { LoadingState } from '../loading_state'; -import { shallow } from 'enzyme'; +import { EuiSpacer, EuiFlyoutHeader, EuiFlyoutHeaderProps } from '@elastic/eui'; -describe('LoadingState', () => { - it('should render normally', () => { - const component = shallow(); - - expect(component).toMatchSnapshot(); - }); -}); +export const PanelHeader: React.FunctionComponent< + { children: React.ReactNode } & Omit +> = (props) => ( + <> + + + +); diff --git a/src/plugins/index_pattern_editor/public/components/flyout_panels/index.ts b/src/plugins/index_pattern_editor/public/components/flyout_panels/index.ts new file mode 100644 index 0000000000000..9fc9a7a916f56 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/flyout_panels/index.ts @@ -0,0 +1,21 @@ +/* + * 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 { PanelFooter } from './flyout_panels_footer'; +import { PanelHeader } from './flyout_panels_header'; +import { PanelContent } from './flyout_panels_content'; +import { Panel } from './flyout_panel'; +import { Panels } from './flyout_panels'; + +export const FlyoutPanels = { + Group: Panels, + Item: Panel, + Content: PanelContent, + Header: PanelHeader, + Footer: PanelFooter, +}; diff --git a/src/plugins/index_pattern_editor/public/components/footer/footer.tsx b/src/plugins/index_pattern_editor/public/components/footer/footer.tsx new file mode 100644 index 0000000000000..0c80d0e77534c --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/footer/footer.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; + +import { + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiButton, +} from '@elastic/eui'; + +interface FooterProps { + onCancel: () => void; + onSubmit: () => void; + submitDisabled: boolean; +} + +const closeButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutCloseButtonLabel', { + defaultMessage: 'Close', +}); + +const saveButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutSaveButtonLabel', { + defaultMessage: 'Create index pattern', +}); + +export const Footer = ({ onCancel, onSubmit, submitDisabled }: FooterProps) => { + return ( + + + + + {closeButtonLabel} + + + + + + {saveButtonLabel} + + + + + ); +}; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/index.ts b/src/plugins/index_pattern_editor/public/components/footer/index.ts similarity index 91% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/index.ts rename to src/plugins/index_pattern_editor/public/components/footer/index.ts index 8f737b3a42613..e3103aab170e0 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/index.ts +++ b/src/plugins/index_pattern_editor/public/components/footer/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { Header } from './header'; +export { Footer } from './footer'; diff --git a/src/plugins/index_pattern_editor/public/components/form_fields/index.ts b/src/plugins/index_pattern_editor/public/components/form_fields/index.ts new file mode 100644 index 0000000000000..0d2091c065771 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/form_fields/index.ts @@ -0,0 +1,11 @@ +/* + * 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 { TimestampField } from './timestamp_field'; +export { TypeField } from './type_field'; +export { TitleField } from './title_field'; diff --git a/src/plugins/index_pattern_editor/public/components/form_fields/timestamp_field.tsx b/src/plugins/index_pattern_editor/public/components/form_fields/timestamp_field.tsx new file mode 100644 index 0000000000000..93cb39ea1ba33 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/form_fields/timestamp_field.tsx @@ -0,0 +1,157 @@ +/* + * 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 React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { EuiFormRow, EuiComboBox, EuiFormHelpText, EuiComboBoxOptionOption } from '@elastic/eui'; + +import { + UseField, + FieldConfig, + ValidationConfig, + getFieldValidityAndErrorMessage, +} from '../../shared_imports'; + +import { TimestampOption } from '../../types'; +import { schema } from '../form_schema'; + +interface Props { + options: TimestampOption[]; + isLoadingOptions: boolean; + isExistingIndexPattern: boolean; + isLoadingMatchedIndices: boolean; + hasMatchedIndices: boolean; +} + +const requireTimestampOptionValidator = (options: Props['options']): ValidationConfig => ({ + validator: async ({ value }) => { + const isValueRequired = !!options.length; + if (isValueRequired && !value) { + return { + message: i18n.translate( + 'indexPatternEditor.requireTimestampOption.ValidationErrorMessage', + { + defaultMessage: 'Select a timestamp field.', + } + ), + }; + } + }, +}); + +const getTimestampConfig = ( + options: Props['options'] +): FieldConfig> => { + const timestampFieldConfig = schema.timestampField; + + const validations = [ + ...timestampFieldConfig.validations, + // note this is responsible for triggering the state update for the selected source list. + requireTimestampOptionValidator(options), + ]; + + return { + ...timestampFieldConfig!, + validations, + }; +}; + +const noTimestampOptionText = i18n.translate('indexPatternEditor.editor.form.noTimeFieldsLabel', { + defaultMessage: 'No matching data stream, index, or alias has a timestamp field.', +}); + +const timestampFieldHelp = i18n.translate('indexPatternEditor.editor.form.timeFieldHelp', { + defaultMessage: 'Select a timestamp field for use with the global time filter.', +}); + +export const TimestampField = ({ + options = [], + isLoadingOptions = false, + isExistingIndexPattern, + isLoadingMatchedIndices, + hasMatchedIndices, +}: Props) => { + const optionsAsComboBoxOptions = options.map(({ display, fieldName }) => ({ + label: display, + value: fieldName, + })); + const timestampConfig = useMemo(() => getTimestampConfig(options), [options]); + const selectTimestampHelp = options.length ? timestampFieldHelp : ''; + + const timestampNoFieldsHelp = + options.length === 0 && + !isExistingIndexPattern && + !isLoadingMatchedIndices && + !isLoadingOptions && + hasMatchedIndices + ? noTimestampOptionText + : ''; + + return ( + > config={timestampConfig} path="timestampField"> + {(field) => { + const { label, value, setValue } = field; + + if (value === undefined) { + return null; + } + + const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); + const isDisabled = !optionsAsComboBoxOptions.length; + + return ( + <> + + <> + + placeholder={i18n.translate( + 'indexPatternEditor.editor.form.runtimeType.placeholderLabel', + { + defaultMessage: 'Select a timestamp field', + } + )} + singleSelection={{ asPlainText: true }} + options={optionsAsComboBoxOptions} + selectedOptions={value ? [value] : undefined} + onChange={(newValue) => { + if (newValue.length === 0) { + // Don't allow clearing the type. One must always be selected + return; + } + // + setValue(newValue[0]); + }} + isClearable={false} + isDisabled={isDisabled} + data-test-subj="timestampField" + aria-label={i18n.translate( + 'indexPatternEditor.editor.form.timestampSelectAriaLabel', + { + defaultMessage: 'Timestamp field', + } + )} + isLoading={isLoadingOptions} + fullWidth + /> + + {timestampNoFieldsHelp || selectTimestampHelp || <> } + + + + + ); + }} + + ); +}; diff --git a/src/plugins/index_pattern_editor/public/components/form_fields/title_field.tsx b/src/plugins/index_pattern_editor/public/components/form_fields/title_field.tsx new file mode 100644 index 0000000000000..aa16351e0922f --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/form_fields/title_field.tsx @@ -0,0 +1,232 @@ +/* + * 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 React, { ChangeEvent, useState, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFormRow, EuiFieldText } from '@elastic/eui'; +import { + UseField, + getFieldValidityAndErrorMessage, + ValidationConfig, + FieldConfig, +} from '../../shared_imports'; +import { canAppendWildcard } from '../../lib'; +import { schema } from '../form_schema'; +import { + MatchedItem, + RollupIndicesCapsResponse, + IndexPatternConfig, + MatchedIndicesSet, +} from '../../types'; + +interface RefreshMatchedIndicesResult { + matchedIndicesResult: MatchedIndicesSet; + newRollupIndexName?: string; +} + +interface TitleFieldProps { + existingIndexPatterns: string[]; + isRollup: boolean; + matchedIndices: MatchedItem[]; + rollupIndicesCapabilities: RollupIndicesCapsResponse; + refreshMatchedIndices: (title: string) => Promise; +} + +const rollupIndexPatternNoMatchError = { + message: i18n.translate('indexPatternEditor.rollupIndexPattern.createIndex.noMatchError', { + defaultMessage: 'Rollup index pattern error: must match one rollup index', + }), +}; + +const rollupIndexPatternTooManyMatchesError = { + message: i18n.translate('indexPatternEditor.rollupIndexPattern.createIndex.tooManyMatchesError', { + defaultMessage: 'Rollup index pattern error: can only match one rollup index', + }), +}; + +const mustMatchError = { + message: i18n.translate('indexPatternEditor.createIndex.noMatch', { + defaultMessage: 'Name must match one or more data streams, indices, or aliases.', + }), +}; + +const createTitlesNoDupesValidator = ( + namesNotAllowed: string[] +): ValidationConfig<{}, string, string> => ({ + validator: ({ value }) => { + if (namesNotAllowed.includes(value)) { + return { + message: i18n.translate('indexPatternEditor.indexPatternExists.ValidationErrorMessage', { + defaultMessage: 'An index pattern with this title already exists.', + }), + }; + } + }, +}); + +interface MatchesValidatorArgs { + rollupIndicesCapabilities: Record; + refreshMatchedIndices: (title: string) => Promise; + isRollup: boolean; +} + +const createMatchesIndicesValidator = ({ + rollupIndicesCapabilities, + refreshMatchedIndices, + isRollup, +}: MatchesValidatorArgs): ValidationConfig<{}, string, string> => ({ + validator: async ({ value }) => { + const { matchedIndicesResult, newRollupIndexName } = await refreshMatchedIndices(value); + const rollupIndices = Object.keys(rollupIndicesCapabilities); + + if (matchedIndicesResult.exactMatchedIndices.length === 0) { + return mustMatchError; + } + + if (!isRollup || !rollupIndices || !rollupIndices.length) { + return; + } + + // A rollup index pattern needs to match one and only one rollup index. + const rollupIndexMatches = matchedIndicesResult.exactMatchedIndices.filter((matchedIndex) => + rollupIndices.includes(matchedIndex.name) + ); + + if (!rollupIndexMatches.length) { + return rollupIndexPatternNoMatchError; + } else if (rollupIndexMatches.length > 1) { + return rollupIndexPatternTooManyMatchesError; + } + + // Error info is potentially provided via the rollup indices caps request + const error = newRollupIndexName && rollupIndicesCapabilities[newRollupIndexName].error; + + if (error) { + return { + message: i18n.translate('indexPatternEditor.rollup.uncaughtError', { + defaultMessage: 'Rollup index pattern error: {error}', + values: { + error, + }, + }), + }; + } + }, +}); + +interface GetTitleConfigArgs { + namesNotAllowed: string[]; + isRollup: boolean; + matchedIndices: MatchedItem[]; + rollupIndicesCapabilities: RollupIndicesCapsResponse; + refreshMatchedIndices: (title: string) => Promise; +} + +const getTitleConfig = ({ + namesNotAllowed, + isRollup, + rollupIndicesCapabilities, + refreshMatchedIndices, +}: GetTitleConfigArgs): FieldConfig => { + const titleFieldConfig = schema.title; + + const validations = [ + ...titleFieldConfig.validations, + // note this is responsible for triggering the state update for the selected source list. + createMatchesIndicesValidator({ + rollupIndicesCapabilities, + refreshMatchedIndices, + isRollup, + }), + createTitlesNoDupesValidator(namesNotAllowed), + ]; + + return { + ...titleFieldConfig!, + validations, + }; +}; + +export const TitleField = ({ + existingIndexPatterns, + isRollup, + matchedIndices, + rollupIndicesCapabilities, + refreshMatchedIndices, +}: TitleFieldProps) => { + const [appendedWildcard, setAppendedWildcard] = useState(false); + + const fieldConfig = useMemo( + () => + getTitleConfig({ + namesNotAllowed: existingIndexPatterns, + isRollup, + matchedIndices, + rollupIndicesCapabilities, + refreshMatchedIndices, + }), + [ + existingIndexPatterns, + isRollup, + matchedIndices, + rollupIndicesCapabilities, + refreshMatchedIndices, + ] + ); + + return ( + + path="title" + config={fieldConfig} + componentProps={{ + euiFieldProps: { + 'aria-label': i18n.translate('indexPatternEditor.form.titleAriaLabel', { + defaultMessage: 'Title field', + }), + }, + }} + > + {(field) => { + const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); + return ( + + ) => { + e.persist(); + let query = e.target.value; + if (query.length === 1 && !appendedWildcard && canAppendWildcard(query)) { + query += '*'; + setAppendedWildcard(true); + setTimeout(() => e.target.setSelectionRange(1, 1)); + } else { + if (['', '*'].includes(query) && appendedWildcard) { + query = ''; + setAppendedWildcard(false); + } + } + field.setValue(query); + }} + isLoading={field.isValidating} + fullWidth + data-test-subj="createIndexPatternNameInput" + /> + + ); + }} + + ); +}; diff --git a/src/plugins/index_pattern_editor/public/components/form_fields/type_field.tsx b/src/plugins/index_pattern_editor/public/components/form_fields/type_field.tsx new file mode 100644 index 0000000000000..606197159e53c --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/form_fields/type_field.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +// @ts-ignore +import { euiColorAccent } from '@elastic/eui/dist/eui_theme_light.json'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { + EuiFormRow, + EuiSuperSelect, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, + EuiBadge, +} from '@elastic/eui'; + +import { UseField } from '../../shared_imports'; + +import { INDEX_PATTERN_TYPE, IndexPatternConfig } from '../../types'; + +interface TypeFieldProps { + onChange: (type: INDEX_PATTERN_TYPE) => void; +} + +const standardSelectItem = ( + + + + + + + + +); + +const rollupSelectItem = ( + + + +   + + + + + + + + +); + +export const TypeField = ({ onChange }: TypeFieldProps) => { + return ( + path="type"> + {({ label, value, setValue }) => { + if (value === undefined) { + return null; + } + return ( + <> + + { + setValue(newValue); + onChange(newValue); + }} + aria-label={i18n.translate('indexPatternEditor.editor.form.typeSelectAriaLabel', { + defaultMessage: 'Type field', + })} + fullWidth + /> + + + ); + }} + + ); +}; diff --git a/src/plugins/index_pattern_editor/public/components/form_schema.ts b/src/plugins/index_pattern_editor/public/components/form_schema.ts new file mode 100644 index 0000000000000..4e852285031ca --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/form_schema.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 { fieldValidators } from '../shared_imports'; +import { INDEX_PATTERN_TYPE } from '../types'; + +export const schema = { + title: { + label: i18n.translate('indexPatternEditor.editor.form.titleLabel', { + defaultMessage: 'Name', + }), + defaultValue: '', + helpText: i18n.translate('indexPatternEditor.validations.titleHelpText', { + defaultMessage: + 'Use an asterisk (*) to match multiple indices. Spaces and the characters , /, ?, ", <, >, | are not allowed.', + }), + validations: [ + { + validator: fieldValidators.emptyField( + i18n.translate('indexPatternEditor.validations.titleIsRequiredErrorMessage', { + defaultMessage: 'A name is required.', + }) + ), + }, + ], + }, + timestampField: { + label: i18n.translate('indexPatternEditor.editor.form.timeFieldLabel', { + defaultMessage: 'Timestamp field', + }), + helpText: i18n.translate('indexPatternEditor.editor.form.timestampFieldHelp', { + defaultMessage: 'Select a timestamp field for use with the global time filter.', + }), + validations: [], + }, + allowHidden: { + label: i18n.translate('indexPatternEditor.editor.form.allowHiddenLabel', { + defaultMessage: 'Allow hidden and system indices', + }), + defaultValue: false, + }, + id: { + label: i18n.translate('indexPatternEditor.editor.form.customIdLabel', { + defaultMessage: 'Custom index pattern ID', + }), + helpText: i18n.translate('indexPatternEditor.editor.form.customIdHelp', { + defaultMessage: + 'Kibana provides a unique identifier for each index pattern, or you can create your own.', + }), + }, + type: { + label: i18n.translate('indexPatternEditor.editor.form.TypeLabel', { + defaultMessage: 'Index pattern type', + }), + defaultValue: INDEX_PATTERN_TYPE.DEFAULT, + }, +}; diff --git a/src/plugins/index_pattern_editor/public/components/i18n_texts.ts b/src/plugins/index_pattern_editor/public/components/i18n_texts.ts new file mode 100644 index 0000000000000..5745714ae7bd8 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/i18n_texts.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 { i18n } from '@kbn/i18n'; + +export const geti18nTexts = () => { + return { + noTimestampOptionText: i18n.translate( + 'indexPatternEditor.createIndexPattern.stepTime.noTimeFieldsLabel', + { + defaultMessage: 'No matching data stream, index, or alias has a timestamp field.', + } + ), + timestampFieldHelp: i18n.translate('indexPatternEditor.editor.form.timeFieldHelp', { + defaultMessage: 'Select a timestamp field for use with the global time filter.', + }), + rollupLabel: i18n.translate('indexPatternEditor.rollupIndexPattern.createIndex.indexLabel', { + defaultMessage: 'Rollup', + }), + }; +}; diff --git a/src/plugins/index_pattern_editor/public/components/index.ts b/src/plugins/index_pattern_editor/public/components/index.ts new file mode 100644 index 0000000000000..82da708ac859d --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { + IndexPatternEditorFlyoutContent, + Props as IndexPatternEditorFlyoutContentProps, +} from './index_pattern_editor_flyout_content'; + +export { IndexPatternEditor } from './index_pattern_editor'; + +export { schema } from './form_schema'; +export { TimestampField, TypeField, TitleField } from './form_fields'; +export { EmptyPrompts } from './empty_prompts'; +export { PreviewPanel } from './preview_panel'; +export { LoadingIndices } from './loading_indices'; +export { geti18nTexts } from './i18n_texts'; +export { Footer } from './footer'; +export { AdvancedParamsContent } from './advanced_params_content'; +export { RollupBetaWarning } from './rollup_beta_warning'; diff --git a/src/plugins/index_pattern_editor/public/components/index_pattern_editor.scss b/src/plugins/index_pattern_editor/public/components/index_pattern_editor.scss new file mode 100644 index 0000000000000..5b3a30effc767 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/index_pattern_editor.scss @@ -0,0 +1,13 @@ +.indexPatternEditor__form { + flex-grow: 1; +} + +.fieldEditor__mainFlyoutPanel { + display: flex; + flex-direction: column; +} + +.indexPatternEditor__footer { + margin-left: -$euiSizeL; + margin-right: -$euiSizeL; +} diff --git a/src/plugins/index_pattern_editor/public/components/index_pattern_editor.tsx b/src/plugins/index_pattern_editor/public/components/index_pattern_editor.tsx new file mode 100644 index 0000000000000..bc0b6156a7844 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/index_pattern_editor.tsx @@ -0,0 +1,43 @@ +/* + * 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 React from 'react'; +import { EuiFlyout } from '@elastic/eui'; +import { IndexPatternEditorLazy } from './index_pattern_editor_lazy'; +import { IndexPatternEditorContext, IndexPatternEditorProps } from '../types'; +import { createKibanaReactContext } from '../shared_imports'; +import './index_pattern_editor.scss'; + +export interface IndexPatternEditorPropsWithServices extends IndexPatternEditorProps { + services: IndexPatternEditorContext; +} + +export const IndexPatternEditor = ({ + onSave, + onCancel = () => {}, + services, + defaultTypeIsRollup = false, + requireTimestampField = false, +}: IndexPatternEditorPropsWithServices) => { + const { + Provider: KibanaReactContextProvider, + } = createKibanaReactContext(services); + + return ( + + {}} hideCloseButton={true} size="l"> + + + + ); +}; diff --git a/src/plugins/index_pattern_editor/public/components/index_pattern_editor_flyout_content.tsx b/src/plugins/index_pattern_editor/public/components/index_pattern_editor_flyout_content.tsx new file mode 100644 index 0000000000000..cabff9bfb009b --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/index_pattern_editor_flyout_content.tsx @@ -0,0 +1,379 @@ +/* + * 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 React, { useState, useEffect, useCallback, useRef } from 'react'; +import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { + IndexPatternSpec, + Form, + useForm, + useFormData, + useKibana, + GetFieldsOptions, +} from '../shared_imports'; + +import { ensureMinimumTime, getIndices, extractTimeFields, getMatchedIndices } from '../lib'; +import { FlyoutPanels } from './flyout_panels'; + +import { + MatchedItem, + IndexPatternEditorContext, + RollupIndicesCapsResponse, + INDEX_PATTERN_TYPE, + IndexPatternConfig, + MatchedIndicesSet, + FormInternal, + TimestampOption, +} from '../types'; + +import { + TimestampField, + TypeField, + TitleField, + schema, + Footer, + AdvancedParamsContent, + EmptyPrompts, + PreviewPanel, + RollupBetaWarning, +} from '.'; + +export interface Props { + /** + * Handler for the "save" footer button + */ + onSave: (indexPatternSpec: IndexPatternSpec) => void; + /** + * Handler for the "cancel" footer button + */ + onCancel: () => void; + defaultTypeIsRollup?: boolean; + requireTimestampField?: boolean; +} + +const editorTitle = i18n.translate('indexPatternEditor.title', { + defaultMessage: 'Create index pattern', +}); + +const IndexPatternEditorFlyoutContentComponent = ({ + onSave, + onCancel, + defaultTypeIsRollup, + requireTimestampField = false, +}: Props) => { + const isMounted = useRef(false); + const { + services: { http, indexPatternService, uiSettings }, + } = useKibana(); + + const { form } = useForm({ + defaultValue: { + type: defaultTypeIsRollup ? INDEX_PATTERN_TYPE.ROLLUP : INDEX_PATTERN_TYPE.DEFAULT, + }, + schema, + onSubmit: async (formData, isValid) => { + if (!isValid) { + return; + } + + const indexPatternStub: IndexPatternSpec = { + title: formData.title, + timeFieldName: formData.timestampField?.value, + id: formData.id, + }; + + if (type === INDEX_PATTERN_TYPE.ROLLUP && rollupIndex) { + indexPatternStub.type = INDEX_PATTERN_TYPE.ROLLUP; + indexPatternStub.typeMeta = { + params: { + rollup_index: rollupIndex, + }, + aggs: rollupIndicesCapabilities[rollupIndex].aggs, + }; + } + + await onSave(indexPatternStub); + }, + }); + + const { getFields } = form; + + const [{ title, allowHidden, type }] = useFormData({ form }); + const [isLoadingSources, setIsLoadingSources] = useState(true); + + const [timestampFieldOptions, setTimestampFieldOptions] = useState([]); + const [isLoadingTimestampFields, setIsLoadingTimestampFields] = useState(false); + const [isLoadingMatchedIndices, setIsLoadingMatchedIndices] = useState(false); + const [allSources, setAllSources] = useState([]); + const [isLoadingIndexPatterns, setIsLoadingIndexPatterns] = useState(true); + const [existingIndexPatterns, setExistingIndexPatterns] = useState([]); + const [rollupIndex, setRollupIndex] = useState(); + const [ + rollupIndicesCapabilities, + setRollupIndicesCapabilities, + ] = useState({}); + const [matchedIndices, setMatchedIndices] = useState({ + allIndices: [], + exactMatchedIndices: [], + partialMatchedIndices: [], + visibleIndices: [], + }); + + // load all data sources and set initial matchedIndices + const loadSources = useCallback(() => { + getIndices(http, () => false, '*', allowHidden).then((dataSources) => { + setAllSources(dataSources); + const matchedSet = getMatchedIndices(dataSources, [], [], allowHidden); + setMatchedIndices(matchedSet); + setIsLoadingSources(false); + }); + }, [http, allowHidden]); + + // loading list of index patterns + useEffect(() => { + isMounted.current = true; + loadSources(); + const getTitles = async () => { + const indexPatternTitles = await indexPatternService.getTitles(); + if (isMounted.current) { + setExistingIndexPatterns(indexPatternTitles); + setIsLoadingIndexPatterns(false); + } + }; + getTitles(); + return () => { + isMounted.current = false; + }; + }, [http, indexPatternService, loadSources]); + + // loading rollup info + useEffect(() => { + const getRollups = async () => { + try { + const response = await http.get('/api/rollup/indices'); + if (isMounted.current) { + setRollupIndicesCapabilities(response || {}); + } + } catch (e) { + // Silently swallow failure responses such as expired trials + } + }; + + getRollups(); + }, [http, type]); + + const getRollupIndices = (rollupCaps: RollupIndicesCapsResponse) => Object.keys(rollupCaps); + + const loadTimestampFieldOptions = useCallback( + async (query: string) => { + let timestampOptions: TimestampOption[] = []; + const isValidResult = + !existingIndexPatterns.includes(query) && matchedIndices.exactMatchedIndices.length > 0; + if (isValidResult) { + setIsLoadingTimestampFields(true); + const getFieldsOptions: GetFieldsOptions = { + pattern: query, + }; + if (type === INDEX_PATTERN_TYPE.ROLLUP) { + getFieldsOptions.type = INDEX_PATTERN_TYPE.ROLLUP; + getFieldsOptions.rollupIndex = rollupIndex; + } + + const fields = await ensureMinimumTime( + indexPatternService.getFieldsForWildcard(getFieldsOptions) + ); + timestampOptions = extractTimeFields(fields, requireTimestampField); + } + if (isMounted.current) { + setIsLoadingTimestampFields(false); + setTimestampFieldOptions(timestampOptions); + } + return timestampOptions; + }, + [ + existingIndexPatterns, + indexPatternService, + requireTimestampField, + rollupIndex, + type, + matchedIndices.exactMatchedIndices, + ] + ); + + useEffect(() => { + loadTimestampFieldOptions(title); + getFields().timestampField?.setValue(''); + }, [matchedIndices, loadTimestampFieldOptions, title, getFields]); + + const reloadMatchedIndices = useCallback( + async (newTitle: string) => { + const isRollupIndex = (indexName: string) => + getRollupIndices(rollupIndicesCapabilities).includes(indexName); + let newRollupIndexName: string | undefined; + + const fetchIndices = async (query: string = '') => { + setIsLoadingMatchedIndices(true); + const indexRequests = []; + + if (query?.endsWith('*')) { + const exactMatchedQuery = getIndices(http, isRollupIndex, query, allowHidden); + indexRequests.push(exactMatchedQuery); + // provide default value when not making a request for the partialMatchQuery + indexRequests.push(Promise.resolve([])); + } else { + const exactMatchQuery = getIndices(http, isRollupIndex, query, allowHidden); + const partialMatchQuery = getIndices(http, isRollupIndex, `${query}*`, allowHidden); + + indexRequests.push(exactMatchQuery); + indexRequests.push(partialMatchQuery); + } + + const [exactMatched, partialMatched] = (await ensureMinimumTime( + indexRequests + )) as MatchedItem[][]; + + const matchedIndicesResult = getMatchedIndices( + allSources, + partialMatched, + exactMatched, + allowHidden + ); + + if (isMounted.current) { + if (type === INDEX_PATTERN_TYPE.ROLLUP) { + const rollupIndices = exactMatched.filter((index) => isRollupIndex(index.name)); + newRollupIndexName = rollupIndices.length === 1 ? rollupIndices[0].name : undefined; + setRollupIndex(newRollupIndexName); + } else { + setRollupIndex(undefined); + } + + setMatchedIndices(matchedIndicesResult); + setIsLoadingMatchedIndices(false); + } + + return { matchedIndicesResult, newRollupIndexName }; + }; + + return fetchIndices(newTitle); + }, + [http, allowHidden, allSources, type, rollupIndicesCapabilities] + ); + + useEffect(() => { + reloadMatchedIndices(title); + }, [allowHidden, reloadMatchedIndices, title]); + + const onTypeChange = useCallback( + (newType) => { + form.setFieldValue('title', ''); + form.setFieldValue('timestampField', ''); + if (newType === INDEX_PATTERN_TYPE.ROLLUP) { + form.setFieldValue('allowHidden', false); + } + }, + [form] + ); + + if (isLoadingSources || isLoadingIndexPatterns) { + return ; + } + + const showIndexPatternTypeSelect = () => + uiSettings.isDeclared('rollups:enableIndexPatterns') && + uiSettings.get('rollups:enableIndexPatterns') && + getRollupIndices(rollupIndicesCapabilities).length; + + const indexPatternTypeSelect = showIndexPatternTypeSelect() ? ( + <> + + + + + + + {type === INDEX_PATTERN_TYPE.ROLLUP ? ( + + + + + + ) : ( + <> + )} + + ) : ( + <> + ); + + return ( + + + + +

    {editorTitle}

    +
    +
    + {indexPatternTypeSelect} + + + + + + + + + + + + + + +
    form.submit()} + submitDisabled={form.isSubmitted && !form.isValid} + /> + + + {isLoadingSources ? ( + <> + ) : ( + + )} + + + + ); +}; + +export const IndexPatternEditorFlyoutContent = React.memo(IndexPatternEditorFlyoutContentComponent); diff --git a/src/plugins/index_pattern_editor/public/components/index_pattern_editor_lazy.tsx b/src/plugins/index_pattern_editor/public/components/index_pattern_editor_lazy.tsx new file mode 100644 index 0000000000000..4241f042af0d5 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/index_pattern_editor_lazy.tsx @@ -0,0 +1,22 @@ +/* + * 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 React, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; + +import { IndexPatternEditorProps } from '../types'; + +const IndexPatternFlyoutContentContainer = lazy( + () => import('./index_pattern_flyout_content_container') +); + +export const IndexPatternEditorLazy = (props: IndexPatternEditorProps) => ( + }> + + +); diff --git a/src/plugins/index_pattern_editor/public/components/index_pattern_flyout_content_container.tsx b/src/plugins/index_pattern_editor/public/components/index_pattern_flyout_content_container.tsx new file mode 100644 index 0000000000000..2f60d85677207 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/index_pattern_flyout_content_container.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 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 React from 'react'; +import { i18n } from '@kbn/i18n'; + +import { IndexPatternSpec, useKibana } from '../shared_imports'; +import { IndexPatternEditorFlyoutContent } from './index_pattern_editor_flyout_content'; +import { IndexPatternEditorContext, IndexPatternEditorProps } from '../types'; + +const IndexPatternFlyoutContentContainer = ({ + onSave, + onCancel = () => {}, + defaultTypeIsRollup, + requireTimestampField = false, +}: IndexPatternEditorProps) => { + const { + services: { indexPatternService, notifications }, + } = useKibana(); + + const onSaveClick = async (indexPatternSpec: IndexPatternSpec) => { + try { + const indexPattern = await indexPatternService.createAndSave(indexPatternSpec); + + const message = i18n.translate('indexPatternEditor.saved', { + defaultMessage: "Saved '{indexPatternTitle}'", + values: { indexPatternTitle: indexPattern.title }, + }); + notifications.toasts.addSuccess(message); + await onSave(indexPattern); + } catch (e) { + const title = i18n.translate('indexPatternEditor.indexPatterns.unableSaveLabel', { + defaultMessage: 'Failed to save index pattern.', + }); + + notifications.toasts.addDanger({ title }); + } + }; + + return ( + + ); +}; + +/* eslint-disable import/no-default-export */ +export default IndexPatternFlyoutContentContainer; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/__snapshots__/loading_indices.test.tsx.snap b/src/plugins/index_pattern_editor/public/components/loading_indices/__snapshots__/loading_indices.test.tsx.snap similarity index 88% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/__snapshots__/loading_indices.test.tsx.snap rename to src/plugins/index_pattern_editor/public/components/loading_indices/__snapshots__/loading_indices.test.tsx.snap index a5517f6d4b616..8c62657c7859e 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/__snapshots__/loading_indices.test.tsx.snap +++ b/src/plugins/index_pattern_editor/public/components/loading_indices/__snapshots__/loading_indices.test.tsx.snap @@ -18,7 +18,7 @@ exports[`LoadingIndices should render normally 1`] = ` > diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/index.ts b/src/plugins/index_pattern_editor/public/components/loading_indices/index.ts similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/index.ts rename to src/plugins/index_pattern_editor/public/components/loading_indices/index.ts diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.test.tsx b/src/plugins/index_pattern_editor/public/components/loading_indices/loading_indices.test.tsx similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.test.tsx rename to src/plugins/index_pattern_editor/public/components/loading_indices/loading_indices.test.tsx diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.tsx b/src/plugins/index_pattern_editor/public/components/loading_indices/loading_indices.tsx similarity index 93% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.tsx rename to src/plugins/index_pattern_editor/public/components/loading_indices/loading_indices.tsx index eb17027c63a3d..14ae51567938d 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/loading_indices/loading_indices.tsx +++ b/src/plugins/index_pattern_editor/public/components/loading_indices/loading_indices.tsx @@ -24,7 +24,7 @@ export const LoadingIndices = ({ ...rest }) => (

    diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/index.ts b/src/plugins/index_pattern_editor/public/components/preview_panel/index.ts similarity index 88% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/index.ts rename to src/plugins/index_pattern_editor/public/components/preview_panel/index.ts index 6d39f5bacbcaa..c677d9878f183 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/index.ts +++ b/src/plugins/index_pattern_editor/public/components/preview_panel/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { LoadingState } from './loading_state'; +export { PreviewPanel } from './preview_panel'; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__snapshots__/indices_list.test.tsx.snap b/src/plugins/index_pattern_editor/public/components/preview_panel/indices_list/__snapshots__/indices_list.test.tsx.snap similarity index 97% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__snapshots__/indices_list.test.tsx.snap rename to src/plugins/index_pattern_editor/public/components/preview_panel/indices_list/__snapshots__/indices_list.test.tsx.snap index ca41dddf6197e..74d803a6ff176 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/indices_list/__snapshots__/indices_list.test.tsx.snap +++ b/src/plugins/index_pattern_editor/public/components/preview_panel/indices_list/__snapshots__/indices_list.test.tsx.snap @@ -47,7 +47,7 @@ exports[`IndicesList should change pages 1`] = ` > { pager: Pager; constructor(props: IndicesListProps) { @@ -96,7 +97,7 @@ export class IndicesList extends React.Component diff --git a/src/plugins/index_pattern_editor/public/components/preview_panel/preview_panel.tsx b/src/plugins/index_pattern_editor/public/components/preview_panel/preview_panel.tsx new file mode 100644 index 0000000000000..28413debdb2a9 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/preview_panel/preview_panel.tsx @@ -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 React from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { StatusMessage } from './status_message'; +import { IndicesList } from './indices_list'; + +import { INDEX_PATTERN_TYPE, MatchedIndicesSet } from '../../types'; + +interface Props { + type: INDEX_PATTERN_TYPE; + allowHidden: boolean; + title: string; + matched: MatchedIndicesSet; +} + +export const PreviewPanel = ({ type, allowHidden, title = '', matched }: Props) => { + const indicesListContent = + matched.visibleIndices.length || matched.allIndices.length ? ( + <> + + + + ) : ( + <> + ); + + return ( + <> + + {indicesListContent} + + ); +}; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__snapshots__/status_message.test.tsx.snap b/src/plugins/index_pattern_editor/public/components/preview_panel/status_message/__snapshots__/status_message.test.tsx.snap similarity index 66% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__snapshots__/status_message.test.tsx.snap rename to src/plugins/index_pattern_editor/public/components/preview_panel/status_message/__snapshots__/status_message.test.tsx.snap index 44b753c473803..8b492be417bff 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/__snapshots__/status_message.test.tsx.snap +++ b/src/plugins/index_pattern_editor/public/components/preview_panel/status_message/__snapshots__/status_message.test.tsx.snap @@ -11,7 +11,7 @@ exports[`StatusMessage should render with exact matches 1`] = `   @@ -134,7 +133,7 @@ exports[`StatusMessage should show that system indices exist 1`] = ` diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/index.ts b/src/plugins/index_pattern_editor/public/components/preview_panel/status_message/index.ts similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/index.ts rename to src/plugins/index_pattern_editor/public/components/preview_panel/status_message/index.ts diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.test.tsx b/src/plugins/index_pattern_editor/public/components/preview_panel/status_message/status_message.test.tsx similarity index 96% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.test.tsx rename to src/plugins/index_pattern_editor/public/components/preview_panel/status_message/status_message.test.tsx index 6201440d5e7ff..1bae9bdf3c666 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.test.tsx +++ b/src/plugins/index_pattern_editor/public/components/preview_panel/status_message/status_message.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { StatusMessage } from '../status_message'; import { shallow } from 'enzyme'; -import { MatchedItem } from '../../../../types'; +import { MatchedItem } from '../../../types'; const tagsPartial = { tags: [], @@ -22,6 +22,7 @@ const matchedIndices = { ] as unknown) as MatchedItem[], exactMatchedIndices: [] as MatchedItem[], partialMatchedIndices: ([{ name: 'kibana', ...tagsPartial }] as unknown) as MatchedItem[], + visibleIndices: [], }; describe('StatusMessage', () => { @@ -94,6 +95,7 @@ describe('StatusMessage', () => { allIndices: [], exactMatchedIndices: [], partialMatchedIndices: [], + visibleIndices: [], }} isIncludingSystemIndices={false} query={''} @@ -111,6 +113,7 @@ describe('StatusMessage', () => { allIndices: [], exactMatchedIndices: [], partialMatchedIndices: [], + visibleIndices: [], }} isIncludingSystemIndices={true} query={''} diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.tsx b/src/plugins/index_pattern_editor/public/components/preview_panel/status_message/status_message.tsx similarity index 58% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.tsx rename to src/plugins/index_pattern_editor/public/components/preview_panel/status_message/status_message.tsx index 0ab41ed1a5be4..871ed79c27de5 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/status_message/status_message.tsx +++ b/src/plugins/index_pattern_editor/public/components/preview_panel/status_message/status_message.tsx @@ -12,19 +12,48 @@ import { EuiCallOut } from '@elastic/eui'; import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import { FormattedMessage } from '@kbn/i18n/react'; -import { MatchedItem } from '../../../../types'; +import { MatchedIndicesSet } from '../../../types'; interface StatusMessageProps { - matchedIndices: { - allIndices: MatchedItem[]; - exactMatchedIndices: MatchedItem[]; - partialMatchedIndices: MatchedItem[]; - }; + matchedIndices: MatchedIndicesSet; isIncludingSystemIndices: boolean; query: string; showSystemIndices: boolean; } +const NoMatchStatusMessage = (allIndicesLength: number) => ( + + + + + ), + }} + /> + +); + +const NoMatchNoIndicesStatusMessage = () => ( + + + +); + export const StatusMessage: React.FC = ({ matchedIndices: { allIndices = [], exactMatchedIndices = [], partialMatchedIndices = [] }, isIncludingSystemIndices, @@ -45,10 +74,10 @@ export const StatusMessage: React.FC = ({ statusMessage = ( @@ -58,8 +87,8 @@ export const StatusMessage: React.FC = ({ statusMessage = ( ); @@ -67,7 +96,7 @@ export const StatusMessage: React.FC = ({ statusMessage = ( @@ -80,7 +109,7 @@ export const StatusMessage: React.FC = ({   @@ -124,33 +153,9 @@ export const StatusMessage: React.FC = ({ } else { statusIcon = undefined; statusColor = 'warning'; - statusMessage = ( - - - - - ), - indicesLength: allIndicesLength, - }} - /> - - ); + statusMessage = allIndicesLength + ? NoMatchStatusMessage(allIndicesLength) + : NoMatchNoIndicesStatusMessage(); } return ( diff --git a/src/plugins/index_pattern_editor/public/components/rollup_beta_warning/index.ts b/src/plugins/index_pattern_editor/public/components/rollup_beta_warning/index.ts new file mode 100644 index 0000000000000..4c72c12312855 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/rollup_beta_warning/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { RollupBetaWarning } from './rollup_beta_warning'; diff --git a/src/plugins/index_pattern_editor/public/components/rollup_beta_warning/rollup_beta_warning.tsx b/src/plugins/index_pattern_editor/public/components/rollup_beta_warning/rollup_beta_warning.tsx new file mode 100644 index 0000000000000..fcd61bab07d4c --- /dev/null +++ b/src/plugins/index_pattern_editor/public/components/rollup_beta_warning/rollup_beta_warning.tsx @@ -0,0 +1,42 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { EuiCallOut } from '@elastic/eui'; + +const rollupBetaWarningTitle = i18n.translate( + 'indexPatternEditor.rollupIndexPattern.warning.title', + { + defaultMessage: 'Beta feature', + } +); + +export const RollupBetaWarning = () => ( + +

    + +

    +

    + +

    +
    +); diff --git a/src/plugins/index_pattern_editor/public/constants.ts b/src/plugins/index_pattern_editor/public/constants.ts new file mode 100644 index 0000000000000..ff74e0827fa50 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/constants.ts @@ -0,0 +1,11 @@ +/* + * 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 pluginName = 'index_pattern_editor'; +export const MAX_NUMBER_OF_MATCHING_INDICES = 100; +export const CONFIG_ROLLUPS = 'rollups:enableIndexPatterns'; diff --git a/src/plugins/index_pattern_editor/public/index.ts b/src/plugins/index_pattern_editor/public/index.ts new file mode 100644 index 0000000000000..76c184f9399f1 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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. + */ + +/** + * Management Plugin - public + * + * This is the entry point for the entire client-side public contract of the plugin. + * If something is not explicitly exported here, you can safely assume it is private + * to the plugin and not considered stable. + * + * All stateful contracts will be injected by the platform at runtime, and are defined + * in the setup/start interfaces in `plugin.ts`. The remaining items exported here are + * either types, or static code. + */ + +import { IndexPatternEditorPlugin } from './plugin'; + +export type { PluginStart as IndexPatternEditorStart, IndexPatternEditorProps } from './types'; + +export function plugin() { + return new IndexPatternEditorPlugin(); +} diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/__snapshots__/get_indices.test.ts.snap b/src/plugins/index_pattern_editor/public/lib/__snapshots__/get_indices.test.ts.snap similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/__snapshots__/get_indices.test.ts.snap rename to src/plugins/index_pattern_editor/public/lib/__snapshots__/get_indices.test.ts.snap diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/can_append_wildcard.test.ts b/src/plugins/index_pattern_editor/public/lib/can_append_wildcard.test.ts similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/can_append_wildcard.test.ts rename to src/plugins/index_pattern_editor/public/lib/can_append_wildcard.test.ts diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/can_append_wildcard.ts b/src/plugins/index_pattern_editor/public/lib/can_append_wildcard.ts similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/can_append_wildcard.ts rename to src/plugins/index_pattern_editor/public/lib/can_append_wildcard.ts diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/contains_illegal_characters.ts b/src/plugins/index_pattern_editor/public/lib/contains_illegal_characters.ts similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/contains_illegal_characters.ts rename to src/plugins/index_pattern_editor/public/lib/contains_illegal_characters.ts diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/contains_invalid_characters.test.ts b/src/plugins/index_pattern_editor/public/lib/contains_invalid_characters.test.ts similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/contains_invalid_characters.test.ts rename to src/plugins/index_pattern_editor/public/lib/contains_invalid_characters.test.ts diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/ensure_minimum_time.test.ts b/src/plugins/index_pattern_editor/public/lib/ensure_minimum_time.test.ts similarity index 79% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/ensure_minimum_time.test.ts rename to src/plugins/index_pattern_editor/public/lib/ensure_minimum_time.test.ts index 1bda1b9b5394c..6fb80df3f8932 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/ensure_minimum_time.test.ts +++ b/src/plugins/index_pattern_editor/public/lib/ensure_minimum_time.test.ts @@ -9,28 +9,25 @@ import { ensureMinimumTime } from './ensure_minimum_time'; describe('ensureMinimumTime', () => { - it('resolves single promise', async (done) => { + it('resolves single promise', async () => { const promiseA = new Promise((resolve) => resolve('a')); const a = await ensureMinimumTime(promiseA, 0); expect(a).toBe('a'); - done(); }); - it('resolves multiple promises', async (done) => { - const promiseA = new Promise((resolve) => resolve('a')); - const promiseB = new Promise((resolve) => resolve('b')); + it('resolves multiple promises', async () => { + const promiseA = new Promise((resolve) => resolve('a')); + const promiseB = new Promise((resolve) => resolve('b')); const [a, b] = await ensureMinimumTime([promiseA, promiseB], 0); expect(a).toBe('a'); expect(b).toBe('b'); - done(); }); - it('resolves in the amount of time provided, at minimum', async (done) => { + it('resolves in the amount of time provided, at minimum', async () => { const startTime = new Date().getTime(); const promise = new Promise((resolve) => resolve()); await ensureMinimumTime(promise, 100); const endTime = new Date().getTime(); expect(endTime - startTime).toBeGreaterThanOrEqual(100); - done(); }); }); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/ensure_minimum_time.ts b/src/plugins/index_pattern_editor/public/lib/ensure_minimum_time.ts similarity index 95% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/ensure_minimum_time.ts rename to src/plugins/index_pattern_editor/public/lib/ensure_minimum_time.ts index cab11ff3b5a37..5c52446efbe34 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/ensure_minimum_time.ts +++ b/src/plugins/index_pattern_editor/public/lib/ensure_minimum_time.ts @@ -15,8 +15,8 @@ export const DEFAULT_MINIMUM_TIME_MS = 300; -export async function ensureMinimumTime( - promiseOrPromises: Promise | Array>, +export async function ensureMinimumTime( + promiseOrPromises: Promise | Array>, minimumTimeMs = DEFAULT_MINIMUM_TIME_MS ) { let returnValue; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/extract_time_fields.test.ts b/src/plugins/index_pattern_editor/public/lib/extract_time_fields.test.ts similarity index 76% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/extract_time_fields.test.ts rename to src/plugins/index_pattern_editor/public/lib/extract_time_fields.test.ts index 071c21d229524..bc3e4209f5db7 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/extract_time_fields.test.ts +++ b/src/plugins/index_pattern_editor/public/lib/extract_time_fields.test.ts @@ -16,18 +16,16 @@ describe('extractTimeFields', () => { { type: 'text', name: 'name' }, ] as IndexPatternField[]; - expect(extractTimeFields(fields)).toEqual([ - { display: `The indices which match this index pattern don't contain any time fields.` }, - ]); + expect(extractTimeFields(fields)).toEqual([]); }); it('should add extra options', () => { const fields = [{ type: 'date', name: '@timestamp' }] as IndexPatternField[]; + // const extractedFields = extractTimeFields(fields); expect(extractTimeFields(fields)).toEqual([ { display: '@timestamp', fieldName: '@timestamp' }, - { isDisabled: true, display: '───', fieldName: '' }, - { display: `I don't want to use the time filter`, fieldName: undefined }, + { display: `--- I don't want to use the time filter ---`, fieldName: '' }, ]); }); }); diff --git a/src/plugins/index_pattern_editor/public/lib/extract_time_fields.ts b/src/plugins/index_pattern_editor/public/lib/extract_time_fields.ts new file mode 100644 index 0000000000000..82b94b5c6744a --- /dev/null +++ b/src/plugins/index_pattern_editor/public/lib/extract_time_fields.ts @@ -0,0 +1,44 @@ +/* + * 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 { IndexPatternField } from '../../../../plugins/data/public'; +import { TimestampOption } from '../types'; + +export function extractTimeFields( + fields: IndexPatternField[], + requireTimestampField: boolean = false +): TimestampOption[] { + const dateFields = fields.filter((field) => field.type === 'date'); + + if (dateFields.length === 0) { + return []; + } + + const noTimeFieldLabel = i18n.translate( + 'indexPatternEditor.createIndexPattern.stepTime.noTimeFieldOptionLabel', + { + defaultMessage: "--- I don't want to use the time filter ---", + } + ); + const noTimeFieldOption = { + display: noTimeFieldLabel, + fieldName: '', + }; + + const timeFields = dateFields.map((field) => ({ + display: field.name, + fieldName: field.name, + })); + + if (!requireTimestampField) { + timeFields.push(noTimeFieldOption); + } + + return timeFields; +} diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.test.ts b/src/plugins/index_pattern_editor/public/lib/get_indices.test.ts similarity index 79% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.test.ts rename to src/plugins/index_pattern_editor/public/lib/get_indices.test.ts index 29330cefc4806..fc96482f0379f 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.test.ts +++ b/src/plugins/index_pattern_editor/public/lib/get_indices.test.ts @@ -7,7 +7,7 @@ */ import { getIndices, responseToItemArray } from './get_indices'; -import { httpServiceMock } from '../../../../../../core/public/mocks'; +import { httpServiceMock } from '../../../../core/public/mocks'; import { ResolveIndexResponseItemIndexAttrs } from '../types'; export const successfulResponse = { @@ -33,26 +33,27 @@ export const successfulResponse = { }; const mockGetTags = () => []; +const mockIsRollupIndex = () => false; const http = httpServiceMock.createStartContract(); http.get.mockResolvedValue(successfulResponse); describe('getIndices', () => { it('should work in a basic case', async () => { - const result = await getIndices(http, mockGetTags, 'kibana', false); + const result = await getIndices(http, mockIsRollupIndex, 'kibana', false); expect(result.length).toBe(3); expect(result[0].name).toBe('f-alias'); expect(result[1].name).toBe('foo'); }); it('should ignore ccs query-all', async () => { - expect((await getIndices(http, mockGetTags, '*:', false)).length).toBe(0); + expect((await getIndices(http, mockIsRollupIndex, '*:', false)).length).toBe(0); }); it('should ignore a single comma', async () => { - expect((await getIndices(http, mockGetTags, ',', false)).length).toBe(0); - expect((await getIndices(http, mockGetTags, ',*', false)).length).toBe(0); - expect((await getIndices(http, mockGetTags, ',foobar', false)).length).toBe(0); + expect((await getIndices(http, mockIsRollupIndex, ',', false)).length).toBe(0); + expect((await getIndices(http, mockIsRollupIndex, ',*', false)).length).toBe(0); + expect((await getIndices(http, mockIsRollupIndex, ',foobar', false)).length).toBe(0); }); it('response object to item array', () => { @@ -89,7 +90,7 @@ describe('getIndices', () => { http.get.mockImplementationOnce(() => { throw new Error('Test error'); }); - const result = await getIndices(http, mockGetTags, 'kibana', false); + const result = await getIndices(http, mockIsRollupIndex, 'kibana', false); expect(result.length).toBe(0); }); }); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts b/src/plugins/index_pattern_editor/public/lib/get_indices.ts similarity index 73% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts rename to src/plugins/index_pattern_editor/public/lib/get_indices.ts index e7edd177c2bda..625e99ecbcdc5 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_indices.ts +++ b/src/plugins/index_pattern_editor/public/lib/get_indices.ts @@ -9,25 +9,41 @@ import { sortBy } from 'lodash'; import { HttpStart } from 'kibana/public'; import { i18n } from '@kbn/i18n'; -import { IndexPatternCreationConfig } from '../../../../../index_pattern_management/public'; +import { Tag, INDEX_PATTERN_TYPE } from '../types'; +// todo move into this plugin, consider removing all ipm references import { MatchedItem, ResolveIndexResponse, ResolveIndexResponseItemIndexAttrs } from '../types'; -const aliasLabel = i18n.translate('indexPatternManagement.aliasLabel', { defaultMessage: 'Alias' }); -const dataStreamLabel = i18n.translate('indexPatternManagement.dataStreamLabel', { +const aliasLabel = i18n.translate('indexPatternEditor.aliasLabel', { defaultMessage: 'Alias' }); +const dataStreamLabel = i18n.translate('indexPatternEditor.dataStreamLabel', { defaultMessage: 'Data stream', }); -const indexLabel = i18n.translate('indexPatternManagement.indexLabel', { +const indexLabel = i18n.translate('indexPatternEditor.indexLabel', { defaultMessage: 'Index', }); -const frozenLabel = i18n.translate('indexPatternManagement.frozenLabel', { +const frozenLabel = i18n.translate('indexPatternEditor.frozenLabel', { defaultMessage: 'Frozen', }); +const rollupLabel = i18n.translate('indexPatternEditor.rollupLabel', { + defaultMessage: 'Rollup', +}); + +const getIndexTags = (isRollupIndex: (indexName: string) => boolean) => (indexName: string) => + isRollupIndex(indexName) + ? [ + { + key: INDEX_PATTERN_TYPE.ROLLUP, + name: rollupLabel, + color: 'primary', + }, + ] + : []; + export async function getIndices( http: HttpStart, - getIndexTags: IndexPatternCreationConfig['getIndexTags'], + isRollupIndex: (indexName: string) => boolean, rawPattern: string, showAllIndices: boolean ): Promise { @@ -62,7 +78,7 @@ export async function getIndices( return []; } - return responseToItemArray(response, getIndexTags); + return responseToItemArray(response, getIndexTags(isRollupIndex)); } catch { return []; } @@ -70,7 +86,7 @@ export async function getIndices( export const responseToItemArray = ( response: ResolveIndexResponse, - getIndexTags: IndexPatternCreationConfig['getIndexTags'] + getTags: (indexName: string) => Tag[] ): MatchedItem[] => { const source: MatchedItem[] = []; @@ -78,7 +94,7 @@ export const responseToItemArray = ( const tags: MatchedItem['tags'] = [{ key: 'index', name: indexLabel, color: 'default' }]; const isFrozen = (index.attributes || []).includes(ResolveIndexResponseItemIndexAttrs.FROZEN); - tags.push(...getIndexTags(index.name)); + tags.push(...getTags(index.name)); if (isFrozen) { tags.push({ name: frozenLabel, key: 'frozen', color: 'danger' }); } diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.test.ts b/src/plugins/index_pattern_editor/public/lib/get_matched_indices.test.ts similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.test.ts rename to src/plugins/index_pattern_editor/public/lib/get_matched_indices.test.ts diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.ts b/src/plugins/index_pattern_editor/public/lib/get_matched_indices.ts similarity index 97% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.ts rename to src/plugins/index_pattern_editor/public/lib/get_matched_indices.ts index e9b365474425b..0b659aa5fbc76 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/get_matched_indices.ts +++ b/src/plugins/index_pattern_editor/public/lib/get_matched_indices.ts @@ -23,10 +23,6 @@ function isSystemIndex(index: string): boolean { } function filterSystemIndices(indices: MatchedItem[], isIncludingSystemIndices: boolean) { - if (!indices) { - return indices; - } - const acceptableIndices = isIncludingSystemIndices ? indices : // All system indices begin with a period. @@ -54,14 +50,14 @@ function filterSystemIndices(indices: MatchedItem[], isIncludingSystemIndices: b We call this `exact` matches because ES is telling us exactly what it matches */ -import { MatchedItem } from '../types'; +import { MatchedItem, MatchedIndicesSet } from '../types'; export function getMatchedIndices( unfilteredAllIndices: MatchedItem[], unfilteredPartialMatchedIndices: MatchedItem[], unfilteredExactMatchedIndices: MatchedItem[], isIncludingSystemIndices: boolean = false -) { +): MatchedIndicesSet { const allIndices = filterSystemIndices(unfilteredAllIndices, isIncludingSystemIndices); const partialMatchedIndices = filterSystemIndices( unfilteredPartialMatchedIndices, diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/index.ts b/src/plugins/index_pattern_editor/public/lib/index.ts similarity index 100% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/index.ts rename to src/plugins/index_pattern_editor/public/lib/index.ts diff --git a/src/plugins/index_pattern_editor/public/mocks.ts b/src/plugins/index_pattern_editor/public/mocks.ts new file mode 100644 index 0000000000000..c7ac1d61e3c10 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/mocks.ts @@ -0,0 +1,32 @@ +/* + * 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 { IndexPatternEditorPlugin } from './plugin'; + +export type Start = jest.Mocked>; + +export type Setup = jest.Mocked>; + +const createSetupContract = (): Setup => { + return {}; +}; + +const createStartContract = (): Start => { + return { + openEditor: jest.fn(), + IndexPatternEditorComponent: jest.fn(), + userPermissions: { + editIndexPattern: jest.fn(), + }, + }; +}; + +export const indexPatternEditorPluginMock = { + createSetupContract, + createStartContract, +}; diff --git a/src/plugins/index_pattern_editor/public/open_editor.tsx b/src/plugins/index_pattern_editor/public/open_editor.tsx new file mode 100644 index 0000000000000..ec62a1d6ec7c6 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/open_editor.tsx @@ -0,0 +1,92 @@ +/* + * 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 React from 'react'; +import { CoreStart, OverlayRef } from 'src/core/public'; +import { I18nProvider } from '@kbn/i18n/react'; + +import { + createKibanaReactContext, + toMountPoint, + IndexPattern, + DataPublicPluginStart, +} from './shared_imports'; + +import { CloseEditor, IndexPatternEditorContext, IndexPatternEditorProps } from './types'; +import { IndexPatternEditorLazy } from './components/index_pattern_editor_lazy'; + +interface Dependencies { + core: CoreStart; + indexPatternService: DataPublicPluginStart['indexPatterns']; +} + +export const getEditorOpener = ({ core, indexPatternService }: Dependencies) => ( + options: IndexPatternEditorProps +): CloseEditor => { + const { uiSettings, overlays, docLinks, notifications, http, application } = core; + const { + Provider: KibanaReactContextProvider, + } = createKibanaReactContext({ + uiSettings, + docLinks, + http, + notifications, + application, + indexPatternService, + }); + + let overlayRef: OverlayRef | null = null; + + const openEditor = ({ + onSave, + onCancel = () => {}, + defaultTypeIsRollup = false, + requireTimestampField = false, + }: IndexPatternEditorProps): CloseEditor => { + const closeEditor = () => { + if (overlayRef) { + overlayRef.close(); + overlayRef = null; + } + }; + + const onSaveIndexPattern = (indexPattern: IndexPattern) => { + closeEditor(); + + if (onSave) { + onSave(indexPattern); + } + }; + + overlayRef = overlays.openFlyout( + toMountPoint( + + + { + closeEditor(); + onCancel(); + }} + defaultTypeIsRollup={defaultTypeIsRollup} + requireTimestampField={requireTimestampField} + /> + + + ), + { + hideCloseButton: true, + size: 'l', + } + ); + + return closeEditor; + }; + + return openEditor(options); +}; diff --git a/src/plugins/index_pattern_editor/public/plugin.test.tsx b/src/plugins/index_pattern_editor/public/plugin.test.tsx new file mode 100644 index 0000000000000..b11d6b0c176b6 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/plugin.test.tsx @@ -0,0 +1,83 @@ +/* + * 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 React from 'react'; + +jest.mock('../../kibana_react/public', () => { + const original = jest.requireActual('../../kibana_react/public'); + + return { + ...original, + toMountPoint: (node: React.ReactNode) => node, + }; +}); + +import { CoreStart } from 'src/core/public'; +import { coreMock } from 'src/core/public/mocks'; +import { dataPluginMock } from '../../data/public/mocks'; +import { usageCollectionPluginMock } from '../../usage_collection/public/mocks'; + +import { IndexPatternEditorLazy } from './components/index_pattern_editor_lazy'; +import { IndexPatternEditorPlugin } from './plugin'; + +const noop = () => {}; + +describe('IndexPatternEditorPlugin', () => { + const coreStart: CoreStart = coreMock.createStart(); + const pluginStart = { + data: dataPluginMock.createStartContract(), + usageCollection: usageCollectionPluginMock.createSetupContract(), + }; + + let plugin: IndexPatternEditorPlugin; + + beforeEach(() => { + plugin = new IndexPatternEditorPlugin(); + }); + + test('should expose a handler to open the indexpattern field editor', async () => { + const startApi = await plugin.start(coreStart, pluginStart); + expect(startApi.openEditor).toBeDefined(); + }); + + test('should call core.overlays.openFlyout when opening the editor', async () => { + const openFlyout = jest.fn(); + const onSaveSpy = jest.fn(); + + const coreStartMocked = { + ...coreStart, + overlays: { + ...coreStart.overlays, + openFlyout, + }, + }; + const { openEditor } = await plugin.start(coreStartMocked, pluginStart); + + openEditor({ onSave: onSaveSpy }); + + expect(openFlyout).toHaveBeenCalled(); + + const [[arg]] = openFlyout.mock.calls; + const i18nProvider = arg.props.children; + expect(i18nProvider.props.children.type).toBe(IndexPatternEditorLazy); + + // We force call the "onSave" prop from the component + // and make sure that the the spy is being called. + // Note: we are testing implementation details, if we change or rename the "onSave" prop on + // the component, we will need to update this test accordingly. + expect(i18nProvider.props.children.props.onSave).toBeDefined(); + i18nProvider.props.children.props.onSave(); + expect(onSaveSpy).toHaveBeenCalled(); + }); + + test('should return a handler to close the flyout', async () => { + const { openEditor } = await plugin.start(coreStart, pluginStart); + + const closeEditorHandler = openEditor({ onSave: noop }); + expect(typeof closeEditorHandler).toBe('function'); + }); +}); diff --git a/src/plugins/index_pattern_editor/public/plugin.tsx b/src/plugins/index_pattern_editor/public/plugin.tsx new file mode 100644 index 0000000000000..ca72249496e77 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/plugin.tsx @@ -0,0 +1,76 @@ +/* + * 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 React from 'react'; +import { Plugin, CoreSetup, CoreStart } from 'src/core/public'; + +import { + PluginSetup, + PluginStart, + SetupPlugins, + StartPlugins, + IndexPatternEditorProps, +} from './types'; +import { getEditorOpener } from './open_editor'; +import { IndexPatternEditor } from './components/index_pattern_editor'; + +export class IndexPatternEditorPlugin + implements Plugin { + public setup(core: CoreSetup, plugins: SetupPlugins): PluginSetup { + return {}; + } + + public start(core: CoreStart, plugins: StartPlugins) { + const { application, uiSettings, docLinks, http, notifications } = core; + const { data } = plugins; + + return { + /** + * Index pattern editor flyout via function interface + * @param IndexPatternEditorProps - index pattern editor config + * @returns method to close editor + */ + openEditor: getEditorOpener({ + core, + indexPatternService: data.indexPatterns, + }), + /** + * Index pattern editor flyout via react component + * @param IndexPatternEditorProps - index pattern editor config + * @returns JSX.Element + */ + IndexPatternEditorComponent: (props: IndexPatternEditorProps) => ( + + ), + /** + * Convenience method to determine whether the user can create or edit edit the index patterns. + * + * @returns boolean + */ + userPermissions: { + editIndexPattern: () => { + return application.capabilities.management.kibana.indexPatterns; + }, + }, + }; + } + + public stop() { + return {}; + } +} diff --git a/src/plugins/index_pattern_editor/public/shared_imports.ts b/src/plugins/index_pattern_editor/public/shared_imports.ts new file mode 100644 index 0000000000000..c99d32e0c8a28 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/shared_imports.ts @@ -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 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 { + IndexPattern, + IndexPatternField, + DataPublicPluginStart, + IndexPatternSpec, + GetFieldsOptions, + IndexPatternAggRestrictions, +} from '../../data/public'; + +export { + createKibanaReactContext, + toMountPoint, + CodeEditor, + useKibana, +} from '../../kibana_react/public'; + +export { + useForm, + useFormData, + useFormContext, + Form, + FormSchema, + UseField, + FormHook, + ValidationFunc, + FieldConfig, + getFieldValidityAndErrorMessage, + ValidationConfig, +} from '../../es_ui_shared/static/forms/hook_form_lib'; + +export { fieldValidators } from '../../es_ui_shared/static/forms/helpers'; + +export { + TextField, + ToggleField, + NumericField, + SelectField, + FormRow, + SuperSelectField, +} from '../../es_ui_shared/static/forms/components'; + +export { HttpStart } from '../../../core/public'; diff --git a/src/plugins/index_pattern_editor/public/test_utils/helpers.ts b/src/plugins/index_pattern_editor/public/test_utils/helpers.ts new file mode 100644 index 0000000000000..b55a59df34545 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/test_utils/helpers.ts @@ -0,0 +1,41 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { TestBed } from './test_utils'; + +export const getCommonActions = (testBed: TestBed) => { + const toggleFormRow = (row: 'customLabel' | 'value' | 'format', value: 'on' | 'off' = 'on') => { + const testSubj = `${row}Row.toggle`; + const toggle = testBed.find(testSubj); + const isOn = toggle.props()['aria-checked']; + + if ((value === 'on' && isOn) || (value === 'off' && isOn === false)) { + return; + } + + testBed.form.toggleEuiSwitch(testSubj); + }; + + const changeFieldType = async (value: string, label?: string) => { + await act(async () => { + testBed.find('typeField').simulate('change', [ + { + value, + label: label ?? value, + }, + ]); + }); + testBed.component.update(); + }; + + return { + toggleFormRow, + changeFieldType, + }; +}; diff --git a/src/plugins/index_pattern_editor/public/test_utils/index.ts b/src/plugins/index_pattern_editor/public/test_utils/index.ts new file mode 100644 index 0000000000000..b5d943281cd79 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/test_utils/index.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 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 * from './test_utils'; + +export * from './mocks'; + +export * from './helpers'; diff --git a/src/plugins/index_pattern_editor/public/test_utils/mocks.ts b/src/plugins/index_pattern_editor/public/test_utils/mocks.ts new file mode 100644 index 0000000000000..5d0296a04ea51 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/test_utils/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 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 { DocLinksStart } from 'src/core/public'; + +export const noop = () => {}; + +export const docLinks: DocLinksStart = { + ELASTIC_WEBSITE_URL: 'htts://jestTest.elastic.co', + DOC_LINK_VERSION: 'jest', + links: {} as any, +}; diff --git a/src/plugins/data/server/field_formats/index.ts b/src/plugins/index_pattern_editor/public/test_utils/test_utils.ts similarity index 76% rename from src/plugins/data/server/field_formats/index.ts rename to src/plugins/index_pattern_editor/public/test_utils/test_utils.ts index a8e6d28f2c642..c8e4aedc26471 100644 --- a/src/plugins/data/server/field_formats/index.ts +++ b/src/plugins/index_pattern_editor/public/test_utils/test_utils.ts @@ -6,4 +6,6 @@ * Side Public License, v 1. */ -export { FieldFormatsService, FieldFormatsSetup, FieldFormatsStart } from './field_formats_service'; +export { getRandomString } from '@kbn/test/jest'; + +export { registerTestBed, TestBed } from '@kbn/test/jest'; diff --git a/src/plugins/index_pattern_editor/public/types.ts b/src/plugins/index_pattern_editor/public/types.ts new file mode 100644 index 0000000000000..2a2abe249b330 --- /dev/null +++ b/src/plugins/index_pattern_editor/public/types.ts @@ -0,0 +1,184 @@ +/* + * 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 { FC } from 'react'; +import { + ApplicationStart, + IUiSettingsClient, + NotificationsStart, + DocLinksStart, + HttpSetup, +} from 'src/core/public'; + +import { EuiComboBoxOptionOption } from '@elastic/eui'; + +import type { IndexPattern } from 'src/plugins/data/public'; +import { DataPublicPluginStart, IndexPatternAggRestrictions } from './shared_imports'; + +export interface IndexPatternEditorContext { + uiSettings: IUiSettingsClient; + docLinks: DocLinksStart; + http: HttpSetup; + notifications: NotificationsStart; + application: ApplicationStart; + indexPatternService: DataPublicPluginStart['indexPatterns']; +} + +/** @public */ +export interface IndexPatternEditorProps { + /** + * Handler for the "save" footer button + * @param indexPattern - newly created index pattern + */ + onSave: (indexPattern: IndexPattern) => void; + /** + * Handler for the "cancel" footer button + */ + onCancel?: () => void; + /** + * Sets the default index pattern type to rollup. Defaults to false. + */ + defaultTypeIsRollup?: boolean; + /** + * Sets whether a timestamp field is required to create an index pattern. Defaults to false. + */ + requireTimestampField?: boolean; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PluginSetup {} + +export interface PluginStart { + openEditor(options: IndexPatternEditorProps): () => void; + IndexPatternEditorComponent: FC; + userPermissions: { + editIndexPattern: () => boolean; + }; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SetupPlugins {} + +export interface StartPlugins { + data: DataPublicPluginStart; +} + +export type CloseEditor = () => void; + +export interface MatchedItem { + name: string; + tags: Tag[]; + item: { + name: string; + backing_indices?: string[]; + timestamp_field?: string; + indices?: string[]; + aliases?: string[]; + attributes?: ResolveIndexResponseItemIndexAttrs[]; + data_stream?: string; + }; +} + +// for showing index matches +export interface ResolveIndexResponse { + indices?: ResolveIndexResponseItemIndex[]; + aliases?: ResolveIndexResponseItemAlias[]; + data_streams?: ResolveIndexResponseItemDataStream[]; +} + +export interface ResolveIndexResponseItem { + name: string; +} + +export interface ResolveIndexResponseItemDataStream extends ResolveIndexResponseItem { + backing_indices: string[]; + timestamp_field: string; +} + +export interface ResolveIndexResponseItemAlias extends ResolveIndexResponseItem { + indices: string[]; +} + +export interface ResolveIndexResponseItemIndex extends ResolveIndexResponseItem { + aliases?: string[]; + attributes?: ResolveIndexResponseItemIndexAttrs[]; + data_stream?: string; +} + +export interface Tag { + name: string; + key: string; + color: string; +} +// end for index matches + +export interface IndexPatternTableItem { + id: string; + title: string; + default: boolean; + tag?: string[]; + sort: string; +} + +// copied from index pattern management, needs review +export interface MatchedItem { + name: string; + tags: Tag[]; + item: { + name: string; + backing_indices?: string[]; + timestamp_field?: string; + indices?: string[]; + aliases?: string[]; + attributes?: ResolveIndexResponseItemIndexAttrs[]; + data_stream?: string; + }; +} + +export enum ResolveIndexResponseItemIndexAttrs { + OPEN = 'open', + CLOSED = 'closed', + HIDDEN = 'hidden', + FROZEN = 'frozen', +} + +export interface RollupIndiciesCapability { + aggs: Record; + error: string; +} + +export type RollupIndicesCapsResponse = Record; + +export enum INDEX_PATTERN_TYPE { + ROLLUP = 'rollup', + DEFAULT = 'default', +} + +export interface IndexPatternConfig { + title: string; + timestampField?: EuiComboBoxOptionOption; + allowHidden: boolean; + id?: string; + type: INDEX_PATTERN_TYPE; +} + +export interface FormInternal extends Omit { + timestampField?: TimestampOption; +} + +export interface TimestampOption { + display: string; + fieldName?: string; +} + +export interface MatchedIndicesSet { + allIndices: MatchedItem[]; + exactMatchedIndices: MatchedItem[]; + partialMatchedIndices: MatchedItem[]; + visibleIndices: MatchedItem[]; +} diff --git a/src/plugins/index_pattern_editor/tsconfig.json b/src/plugins/index_pattern_editor/tsconfig.json new file mode 100644 index 0000000000000..559b1aaf0fc26 --- /dev/null +++ b/src/plugins/index_pattern_editor/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "public/**/*", + ], + "references": [ + { "path": "../../core/tsconfig.json" }, + { "path": "../data/tsconfig.json" }, + { "path": "../kibana_react/tsconfig.json" }, + { "path": "../kibana_utils/tsconfig.json" }, + { "path": "../es_ui_shared/tsconfig.json" }, + ] +} diff --git a/src/plugins/index_pattern_field_editor/kibana.json b/src/plugins/index_pattern_field_editor/kibana.json index a3a7e61e9806e..02308b349d4ca 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 04be866a33ca7..219a51e421b44 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 1026012f3b887..4b0df63261496 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 3570ef4c9d33a..67290212c04dd 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 f3f46cbbdda31..348ac86e209ea 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 4e8d56f91c6eb..8fc4f4dbdfdb6 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 90f1d43fa357a..c56d1c0261ed1 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 7228a02afa2a5..129049e1b0565 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 80a57b9419763..dbc369126d64e 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 2bfb0182cbb88..89bac89a95b76 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 053dff1213908..4c34af8748f59 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 ad71263978d9c..1554da93dac53 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 6f6a88cfb7c95..ef71055deca7b 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 8d9cb17b33a40..68a65e5a0fa6c 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 5b573d938ebb2..61076353dbf5e 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 6a4ae0fde0c59..acba1fbedb206 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 556078286e524..cbdd8efb2a4c8 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 1000d9d2b8650..79dc63230dc33 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 ccb6cf7794f87..1c0c7ecba3b2b 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 dbbaf51f114b1..c6f5fc9899ac7 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 9caa5e093a96f..cfc36543780c1 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 559b1aaf0fc26..5450ae74a91a0 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/kibana.json b/src/plugins/index_pattern_management/kibana.json index 7d9d885bbf9d6..3ba56eda3620e 100644 --- a/src/plugins/index_pattern_management/kibana.json +++ b/src/plugins/index_pattern_management/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["management", "data", "urlForwarding", "indexPatternFieldEditor"], + "requiredPlugins": ["management", "data", "urlForwarding", "indexPatternFieldEditor", "indexPatternEditor"], "requiredBundles": ["kibanaReact", "kibanaUtils"], "owner": { "name": "App Services", diff --git a/src/plugins/index_pattern_management/public/components/__snapshots__/utils.test.ts.snap b/src/plugins/index_pattern_management/public/components/__snapshots__/utils.test.ts.snap index 224a5c992d58b..3a25a78472b50 100644 --- a/src/plugins/index_pattern_management/public/components/__snapshots__/utils.test.ts.snap +++ b/src/plugins/index_pattern_management/public/components/__snapshots__/utils.test.ts.snap @@ -6,14 +6,19 @@ Array [ "default": true, "id": "test", "sort": "0test name", - "tags": undefined, + "tags": Array [ + Object { + "key": "default", + "name": "Default", + }, + ], "title": "test name", }, Object { "default": false, "id": "test1", "sort": "1test name 1", - "tags": undefined, + "tags": Array [], "title": "test name 1", }, ] diff --git a/src/plugins/index_pattern_management/public/components/create_button/create_button.tsx b/src/plugins/index_pattern_management/public/components/create_button/create_button.tsx deleted file mode 100644 index 0da250f5cba3b..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_button/create_button.tsx +++ /dev/null @@ -1,137 +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. - */ - -// @ts-ignore -import { euiColorAccent } from '@elastic/eui/dist/eui_theme_light.json'; -import React, { Component, Fragment } from 'react'; - -import { - EuiBadge, - EuiButton, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiDescriptionList, - EuiDescriptionListDescription, - EuiDescriptionListTitle, - EuiPopover, -} from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -interface State { - isPopoverOpen: boolean; -} - -interface Props { - options: Array<{ - text: string; - description?: string; - testSubj?: string; - isBeta?: boolean; - onClick: () => void; - }>; -} - -export class CreateButton extends Component { - public state = { - isPopoverOpen: false, - }; - - public render() { - const { options, children } = this.props; - const { isPopoverOpen } = this.state; - - if (!options || !options.length) { - return null; - } - - if (options.length === 1) { - return ( - - {children} - - ); - } - - const button = ( - - {children} - - ); - - if (options.length > 1) { - return ( - - { - return ( - - - - {option.text} - {option.isBeta ? {this.renderBetaBadge()} : null} - - - {option.description} - - - - ); - })} - /> - - ); - } - } - - private togglePopover = () => { - this.setState({ - isPopoverOpen: !this.state.isPopoverOpen, - }); - }; - - private closePopover = () => { - this.setState({ - isPopoverOpen: false, - }); - }; - - private renderBetaBadge = () => { - return ( - - - - ); - }; -} diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/CREATE_INDEX_PATTERN.md b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/CREATE_INDEX_PATTERN.md deleted file mode 100644 index 6e01bf001691e..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/CREATE_INDEX_PATTERN.md +++ /dev/null @@ -1,15 +0,0 @@ -# Create Index Pattern - -This is meant to serve as a guide to this area of code. - -## Bye bye regressions -In order to prevent future regressions, there are a few scenarios -that need to be tested with each change to this area of the code. - -- Cross-cluster search - - Ensure changes work properly in a CCS environment - - A solid CCS environment involves various indices on all nodes including the controlling node. -- Alias support - - Indices are the most common use case, but we also support aliases. -- Time field name - - This needs to be `undefined` if the user does not select one (other areas of Kibana depend on this) \ No newline at end of file diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap deleted file mode 100644 index 38a9e47014416..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/__snapshots__/create_index_pattern_wizard.test.tsx.snap +++ /dev/null @@ -1,250 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CreateIndexPatternWizard defaults to the loading state 1`] = ` - - - - -`; - -exports[`CreateIndexPatternWizard renders index pattern step when there are indices 1`] = ` - -
    - - - - -`; - -exports[`CreateIndexPatternWizard renders the empty state when there are no indices 1`] = ` - -
    - - - - -`; - -exports[`CreateIndexPatternWizard renders time field step when step is set to 2 1`] = ` - -
    - - - - -`; - -exports[`CreateIndexPatternWizard renders when there are no indices but there are remote clusters 1`] = ` - -
    - - - - -`; - -exports[`CreateIndexPatternWizard shows system indices even if there are no other indices if the include system indices is toggled 1`] = ` - -
    - - - - -`; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/__snapshots__/header.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/__snapshots__/header.test.tsx.snap deleted file mode 100644 index 5e5fbb7c5e99d..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/__snapshots__/header.test.tsx.snap +++ /dev/null @@ -1,499 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Header should render a different name, prompt, and beta tag if provided 1`] = ` -
    - Test prompt -
    - } -> - - Create test index pattern - - - - - - } - > -
    - - Create test index pattern - - - - - - } - responsive={true} - > -
    - -
    - -
    - -

    - Create test index pattern - - - - Beta - - -

    -
    -
    -
    -
    -
    -
    - -
    - - -
    -

    - - multiple - , - "single": - filebeat-4-3-22 - , - "star": - filebeat-* - , - } - } - > - - An index pattern can match a single source, for example, - - - - - filebeat-4-3-22 - - - - - , or - - multiple - - data sources, - - - - - filebeat-* - - - - - . - - -
    - - - -

    -
    -
    - -
    - -
    - Test prompt -
    -
    -
    - -
    -
    -
    -`; - -exports[`Header should render normally 1`] = ` -
    - - Create test index pattern - - } - > -
    - - Create test index pattern - - } - responsive={true} - > -
    - -
    - -
    - -

    - Create test index pattern -

    -
    -
    -
    -
    -
    -
    - -
    - - -
    -

    - - multiple - , - "single": - filebeat-4-3-22 - , - "star": - filebeat-* - , - } - } - > - - An index pattern can match a single source, for example, - - - - - filebeat-4-3-22 - - - - - , or - - multiple - - data sources, - - - - - filebeat-* - - - - - . - - -
    - - - -

    -
    -
    -
    -
    - -
    -
    -
    -`; - -exports[`Header should render without including system indices 1`] = ` -
    - - Create test index pattern - - } - > -
    - - Create test index pattern - - } - responsive={true} - > -
    - -
    - -
    - -

    - Create test index pattern -

    -
    -
    -
    -
    -
    -
    - -
    - - -
    -

    - - multiple - , - "single": - filebeat-4-3-22 - , - "star": - filebeat-* - , - } - } - > - - An index pattern can match a single source, for example, - - - - - filebeat-4-3-22 - - - - - , or - - multiple - - data sources, - - - - - filebeat-* - - - - - . - - -
    - - - -

    -
    -
    -
    -
    - -
    -
    -
    -`; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/header.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/header.test.tsx deleted file mode 100644 index 188fe2fbc02af..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/header.test.tsx +++ /dev/null @@ -1,71 +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 React from 'react'; -import { Header } from '../header'; -import { mount } from 'enzyme'; -import { KibanaContextProvider } from 'src/plugins/kibana_react/public'; -import { mockManagementPlugin } from '../../../../mocks'; -import { DocLinksStart } from 'kibana/public'; - -describe('Header', () => { - const indexPatternName = 'test index pattern'; - const mockedContext = mockManagementPlugin.createIndexPatternManagmentContext(); - const mockedDocLinks = { - links: { - indexPatterns: {}, - }, - } as DocLinksStart; - - it('should render normally', () => { - const component = mount( -
    , - { - wrappingComponent: KibanaContextProvider, - wrappingComponentProps: { - services: mockedContext, - }, - } - ); - - expect(component).toMatchSnapshot(); - }); - - it('should render without including system indices', () => { - const component = mount( -
    , - { - wrappingComponent: KibanaContextProvider, - wrappingComponentProps: { - services: mockedContext, - }, - } - ); - - expect(component).toMatchSnapshot(); - }); - - it('should render a different name, prompt, and beta tag if provided', () => { - const component = mount( -
    Test prompt
    } - indexPatternName={indexPatternName} - isBeta={true} - docLinks={mockedDocLinks} - />, - { - wrappingComponent: KibanaContextProvider, - wrappingComponentProps: { - services: mockedContext, - }, - } - ); - - expect(component).toMatchSnapshot(); - }); -}); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/header.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/header.tsx deleted file mode 100644 index c708bd3cac33e..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/header.tsx +++ /dev/null @@ -1,88 +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 React from 'react'; - -import { EuiBetaBadge, EuiCode, EuiLink, EuiPageHeader, EuiSpacer, EuiText } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { DocLinksStart } from 'kibana/public'; -import { useKibana } from '../../../../../../../plugins/kibana_react/public'; -import { IndexPatternManagmentContext } from '../../../../types'; - -export const Header = ({ - prompt, - indexPatternName, - isBeta = false, - docLinks, -}: { - prompt?: React.ReactNode; - indexPatternName: string; - isBeta?: boolean; - docLinks: DocLinksStart; -}) => { - const changeTitle = useKibana().services.chrome.docTitle.change; - const createIndexPatternHeader = i18n.translate( - 'indexPatternManagement.createIndexPatternHeader', - { - defaultMessage: 'Create {indexPatternName}', - values: { indexPatternName }, - } - ); - - changeTitle(createIndexPatternHeader); - - return ( - - {createIndexPatternHeader} - {isBeta ? ( - <> - {' '} - - - ) : null} - - } - bottomBorder - > - -

    - multiple, - single: filebeat-4-3-22, - star: filebeat-*, - }} - /> -
    - - - -

    -
    - {prompt ? ( - <> - - {prompt} - - ) : null} -
    - ); -}; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/__snapshots__/loading_state.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/__snapshots__/loading_state.test.tsx.snap deleted file mode 100644 index 1e6ac56d437e1..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/__snapshots__/loading_state.test.tsx.snap +++ /dev/null @@ -1,39 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LoadingState should render normally 1`] = ` - - - -

    - -

    -
    -
    - - - -
    -`; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/loading_state.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/loading_state.tsx deleted file mode 100644 index 9cfb9321b3f00..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/loading_state/loading_state.tsx +++ /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 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 React from 'react'; - -import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiTitle } from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -export const LoadingState = () => ( - - - -

    - -

    -
    -
    - - - - -
    -); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/__snapshots__/step_index_pattern.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/__snapshots__/step_index_pattern.test.tsx.snap deleted file mode 100644 index 813a0c61c0829..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/__snapshots__/step_index_pattern.test.tsx.snap +++ /dev/null @@ -1,58 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`StepIndexPattern renders errors when input is invalid 1`] = ` -Object { - "component":
    , |" - data-test-subj="createIndexPatternStep1Header" - errors={ - Array [ - "A name cannot contain spaces or the characters: \\\\, /, ?, \\", <, >, |", - ] - } - goToNextStep={[Function]} - isIncludingSystemIndices={false} - isInputInvalid={true} - isNextStepDisabled={true} - onChangeIncludingSystemIndices={[Function]} - onQueryChanged={[Function]} - query="?" - />, -} -`; - -exports[`StepIndexPattern renders indices which match the initial query 1`] = ` - -`; - -exports[`StepIndexPattern renders matching indices when input is valid 1`] = ` - -`; - -exports[`StepIndexPattern renders the loading state 1`] = ` - -`; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/__snapshots__/header.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/__snapshots__/header.test.tsx.snap deleted file mode 100644 index 851e5cc4c2a76..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/__snapshots__/header.test.tsx.snap +++ /dev/null @@ -1,221 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Header should mark the input as invalid 1`] = ` -
    - -

    - -

    -
    - - - - - - - * - , - } - } - /> - - - % - , - } - } - /> - - } - isInvalid={true} - label={ - - } - labelType="label" - > - - - - - - - - - - - - -
    -`; - -exports[`Header should render normally 1`] = ` -
    - -

    - -

    -
    - - - - - - - * - , - } - } - /> - - - % - , - } - } - /> - - } - isInvalid={false} - label={ - - } - labelType="label" - > - - - - - - - - - - - - -
    -`; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.test.tsx deleted file mode 100644 index 8c7e43ed57c77..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.test.tsx +++ /dev/null @@ -1,49 +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 React from 'react'; -import { Header } from '../header'; -import { shallowWithI18nProvider } from '@kbn/test/jest'; - -describe('Header', () => { - it('should render normally', () => { - const component = shallowWithI18nProvider( -
    {}} - goToNextStep={() => {}} - isNextStepDisabled={false} - onChangeIncludingSystemIndices={() => {}} - isIncludingSystemIndices={false} - /> - ); - - expect(component).toMatchSnapshot(); - }); - - it('should mark the input as invalid', () => { - const component = shallowWithI18nProvider( -
    {}} - goToNextStep={() => {}} - isNextStepDisabled={true} - onChangeIncludingSystemIndices={() => {}} - isIncludingSystemIndices={false} - /> - ); - - expect(component).toMatchSnapshot(); - }); -}); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx deleted file mode 100644 index 342665b185e79..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/components/header/header.tsx +++ /dev/null @@ -1,144 +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 React from 'react'; - -import { - EuiTitle, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiButton, - EuiForm, - EuiFormRow, - EuiFieldText, - EuiSwitchEvent, - EuiSwitch, -} from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -interface HeaderProps { - isInputInvalid: boolean; - errors: any; - characterList: string; - query: string; - onQueryChanged: (e: React.ChangeEvent) => void; - goToNextStep: (query: string) => void; - isNextStepDisabled: boolean; - showSystemIndices?: boolean; - onChangeIncludingSystemIndices: (event: EuiSwitchEvent) => void; - isIncludingSystemIndices: boolean; -} - -export const Header: React.FC = ({ - isInputInvalid, - errors, - characterList, - query, - onQueryChanged, - goToNextStep, - isNextStepDisabled, - showSystemIndices = false, - onChangeIncludingSystemIndices, - isIncludingSystemIndices, - ...rest -}) => ( -
    - -

    - -

    -
    - - - - - - } - isInvalid={isInputInvalid} - error={errors} - helpText={ - <> - * }} - />{' '} - {characterList} }} - /> - - } - > - - - - {showSystemIndices ? ( - - - } - id="checkboxShowSystemIndices" - checked={isIncludingSystemIndices} - onChange={onChangeIncludingSystemIndices} - data-test-subj="showSystemAndHiddenIndices" - /> - - ) : null} - - - - - goToNextStep(query)} - isDisabled={isNextStepDisabled} - data-test-subj="createIndexPatternGoToStep2Button" - > - - - - - -
    -); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/index.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/index.ts deleted file mode 100644 index 3d87a6d7c39dc..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { StepIndexPattern } from './step_index_pattern'; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx deleted file mode 100644 index 8b4f751a4e3a3..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx +++ /dev/null @@ -1,244 +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 React from 'react'; -import { StepIndexPattern, canPreselectTimeField } from './step_index_pattern'; -import { Header } from './components/header'; -import { IndexPatternCreationConfig } from '../../../../../../../plugins/index_pattern_management/public'; -import { mockManagementPlugin } from '../../../../mocks'; -import { createComponentWithContext } from '../../../test_utils'; - -jest.mock('../../lib/ensure_minimum_time', () => ({ - ensureMinimumTime: async (promises: Array>) => - Array.isArray(promises) ? await Promise.all(promises) : await promises, -})); - -const mockIndexPatternCreationType = new IndexPatternCreationConfig({ - type: 'default', - name: 'name', -}); - -jest.mock('../../lib/get_indices', () => ({ - getIndices: ({}, {}, query: string) => { - if (query.startsWith('e')) { - return [{ name: 'es', item: {} }]; - } - - return [{ name: 'kibana', item: {} }]; - }, -})); - -const allIndices = [ - { name: 'kibana', tags: [], item: {} }, - { name: 'es', tags: [], item: {} }, -]; - -const goToNextStep = () => {}; - -const mockContext = mockManagementPlugin.createIndexPatternManagmentContext(); - -mockContext.data.indexPatterns.getTitles = async () => Promise.resolve([]); -mockContext.uiSettings.get.mockReturnValue(''); - -describe('StepIndexPattern', () => { - it('renders the loading state', () => { - const component = createComponentWithContext( - StepIndexPattern, - { - allIndices, - isIncludingSystemIndices: false, - goToNextStep, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - component.setState({ isLoadingIndices: true }); - expect(component.find('[data-test-subj="createIndexPatternStep1Loading"]')).toMatchSnapshot(); - }); - - it('renders indices which match the initial query', async () => { - const component = createComponentWithContext( - StepIndexPattern, - { - allIndices, - isIncludingSystemIndices: false, - goToNextStep, - indexPatternCreationType: mockIndexPatternCreationType, - initialQuery: 'kibana', - }, - mockContext - ); - - // Ensure all promises resolve - await new Promise((resolve) => process.nextTick(resolve)); - // Ensure the state changes are reflected - await component.update(); - - expect( - component.find('[data-test-subj="createIndexPatternStep1IndicesList"]') - ).toMatchSnapshot(); - }); - - it('renders errors when input is invalid', async () => { - const component = createComponentWithContext( - StepIndexPattern, - { - allIndices, - isIncludingSystemIndices: false, - goToNextStep, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - const instance = component.instance() as StepIndexPattern; - instance.onQueryChanged({ target: { value: '?' } } as React.ChangeEvent); - - // Ensure all promises resolve - await new Promise((resolve) => process.nextTick(resolve)); - // Ensure the state changes are reflected - component.update(); - expect({ - component: component.find('[data-test-subj="createIndexPatternStep1Header"]'), - }).toMatchSnapshot(); - }); - - it('renders matching indices when input is valid', async () => { - const component = createComponentWithContext( - StepIndexPattern, - { - allIndices, - isIncludingSystemIndices: false, - goToNextStep, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - const instance = component.instance() as StepIndexPattern; - instance.onQueryChanged({ target: { value: 'k' } } as React.ChangeEvent); - - // Ensure all promises resolve - await new Promise((resolve) => process.nextTick(resolve)); - // Ensure the state changes are reflected - component.update(); - - expect( - component.find('[data-test-subj="createIndexPatternStep1IndicesList"]') - ).toMatchSnapshot(); - }); - - it('appends a wildcard automatically to queries', async () => { - const component = createComponentWithContext( - StepIndexPattern, - { - allIndices, - isIncludingSystemIndices: false, - goToNextStep, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - const instance = component.instance() as StepIndexPattern; - instance.onQueryChanged({ target: { value: 'k' } } as React.ChangeEvent); - expect(component.state('query')).toBe('k*'); - }); - - it('disables the next step if the index pattern exists', async () => { - const component = createComponentWithContext( - StepIndexPattern, - { - allIndices, - isIncludingSystemIndices: false, - goToNextStep, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - component.setState({ indexPatternExists: true }); - expect(component.find(Header).prop('isNextStepDisabled')).toBe(true); - }); - - it('ensures the response of the latest request is persisted', async () => { - const component = createComponentWithContext( - StepIndexPattern, - { - allIndices, - isIncludingSystemIndices: false, - goToNextStep, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - const instance = component.instance() as StepIndexPattern; - instance.onQueryChanged({ target: { value: 'e' } } as React.ChangeEvent); - instance.lastQuery = 'k'; - await new Promise((resolve) => process.nextTick(resolve)); - - // Honesty, the state would match the result of the `k` query but - // it's hard to mock this in tests but if remove our fix - // (the early return if the queries do not match) then this - // equals [{name: 'es'}] - expect(component.state('exactMatchedIndices')).toEqual([]); - - // Ensure it works in the other code flow too (the other early return) - - // Provide `es` so we do not auto append * and enter our other code flow - instance.onQueryChanged({ target: { value: 'es' } } as React.ChangeEvent); - instance.lastQuery = 'k'; - await new Promise((resolve) => process.nextTick(resolve)); - expect(component.state('exactMatchedIndices')).toEqual([]); - }); - - it('it can preselect time field', async () => { - const dataStream1 = { - name: 'data stream 1', - tags: [], - item: { name: 'data stream 1', backing_indices: [], timestamp_field: 'timestamp_field' }, - }; - - const dataStream2 = { - name: 'data stream 2', - tags: [], - item: { name: 'data stream 2', backing_indices: [], timestamp_field: 'timestamp_field' }, - }; - - const differentDataStream = { - name: 'different data stream', - tags: [], - item: { name: 'different data stream 2', backing_indices: [], timestamp_field: 'x' }, - }; - - const index = { - name: 'index', - tags: [], - item: { - name: 'index', - }, - }; - - const alias = { - name: 'alias', - tags: [], - item: { - name: 'alias', - indices: [], - }, - }; - - expect(canPreselectTimeField([index])).toEqual(undefined); - expect(canPreselectTimeField([alias])).toEqual(undefined); - expect(canPreselectTimeField([index, alias, dataStream1])).toEqual(undefined); - - expect(canPreselectTimeField([dataStream1])).toEqual('timestamp_field'); - - expect(canPreselectTimeField([dataStream1, dataStream2])).toEqual('timestamp_field'); - - expect(canPreselectTimeField([dataStream1, dataStream2, differentDataStream])).toEqual( - undefined - ); - }); -}); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx deleted file mode 100644 index 052e454041181..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx +++ /dev/null @@ -1,367 +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 React, { Component } from 'react'; -import { EuiSpacer, EuiCallOut, EuiSwitchEvent } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { indexPatterns, UI_SETTINGS } from '../../../../../../../plugins/data/public'; -import { - getIndices, - containsIllegalCharacters, - getMatchedIndices, - canAppendWildcard, - ensureMinimumTime, -} from '../../lib'; -import { LoadingIndices } from './components/loading_indices'; -import { StatusMessage } from './components/status_message'; -import { IndicesList } from './components/indices_list'; -import { Header } from './components/header'; -import { context as contextType } from '../../../../../../kibana_react/public'; -import { IndexPatternCreationConfig } from '../../../../../../../plugins/index_pattern_management/public'; -import { MatchedItem } from '../../types'; -import { IndexPatternManagmentContextValue } from '../../../../types'; - -interface StepIndexPatternProps { - allIndices: MatchedItem[]; - indexPatternCreationType: IndexPatternCreationConfig; - goToNextStep: (query: string, timestampField?: string) => void; - initialQuery?: string; - showSystemIndices: boolean; -} - -interface StepIndexPatternState { - partialMatchedIndices: MatchedItem[]; - exactMatchedIndices: MatchedItem[]; - isLoadingIndices: boolean; - existingIndexPatterns: string[]; - indexPatternExists: boolean; - query: string; - appendedWildcard: boolean; - showingIndexPatternQueryErrors: boolean; - indexPatternName: string; - isIncludingSystemIndices: boolean; -} - -export const canPreselectTimeField = (indices: MatchedItem[]) => { - const preselectStatus = indices.reduce( - ( - { canPreselect, timeFieldName }: { canPreselect: boolean; timeFieldName?: string }, - matchedItem - ) => { - const dataStreamItem = matchedItem.item; - const dataStreamTimestampField = dataStreamItem.timestamp_field; - const isDataStream = !!dataStreamItem.timestamp_field; - const timestampFieldMatches = - timeFieldName === undefined || timeFieldName === dataStreamTimestampField; - - return { - canPreselect: canPreselect && isDataStream && timestampFieldMatches, - timeFieldName: dataStreamTimestampField || timeFieldName, - }; - }, - { - canPreselect: true, - timeFieldName: undefined, - } - ); - - return preselectStatus.canPreselect ? preselectStatus.timeFieldName : undefined; -}; - -export class StepIndexPattern extends Component { - static contextType = contextType; - - public readonly context!: IndexPatternManagmentContextValue; - - state = { - partialMatchedIndices: [], - exactMatchedIndices: [], - isLoadingIndices: false, - existingIndexPatterns: [], - indexPatternExists: false, - query: '', - appendedWildcard: false, - showingIndexPatternQueryErrors: false, - indexPatternName: '', - isIncludingSystemIndices: false, - }; - - ILLEGAL_CHARACTERS = [...indexPatterns.ILLEGAL_CHARACTERS]; - - constructor(props: StepIndexPatternProps, context: IndexPatternManagmentContextValue) { - super(props, context); - const { indexPatternCreationType, initialQuery } = this.props; - - this.state.query = - initialQuery || context.services.uiSettings.get(UI_SETTINGS.INDEXPATTERN_PLACEHOLDER); - this.state.indexPatternName = indexPatternCreationType.getIndexPatternName(); - } - - lastQuery = ''; - - async UNSAFE_componentWillMount() { - this.fetchExistingIndexPatterns(); - if (this.state.query) { - this.lastQuery = this.state.query; - this.fetchIndices(this.state.query); - } - } - - fetchExistingIndexPatterns = async () => { - const existingIndexPatterns = await this.context.services.data.indexPatterns.getTitles(); - this.setState({ existingIndexPatterns }); - }; - - fetchIndices = async (query: string) => { - const { indexPatternCreationType } = this.props; - const { existingIndexPatterns } = this.state; - - if ((existingIndexPatterns as string[]).includes(query)) { - this.setState({ indexPatternExists: true }); - return; - } - - this.setState({ isLoadingIndices: true, indexPatternExists: false }); - - if (query.endsWith('*')) { - const exactMatchedIndices = await ensureMinimumTime( - getIndices( - this.context.services.http, - (indexName: string) => indexPatternCreationType.getIndexTags(indexName), - query, - this.state.isIncludingSystemIndices - ) - ); - // If the search changed, discard this state - if (query !== this.lastQuery) { - return; - } - this.setState({ exactMatchedIndices, isLoadingIndices: false }); - return; - } - - const [partialMatchedIndices, exactMatchedIndices] = await ensureMinimumTime([ - getIndices( - this.context.services.http, - (indexName: string) => indexPatternCreationType.getIndexTags(indexName), - `${query}*`, - this.state.isIncludingSystemIndices - ), - getIndices( - this.context.services.http, - (indexName: string) => indexPatternCreationType.getIndexTags(indexName), - query, - this.state.isIncludingSystemIndices - ), - ]); - - // If the search changed, discard this state - if (query !== this.lastQuery) { - return; - } - - this.setState({ - partialMatchedIndices, - exactMatchedIndices, - isLoadingIndices: false, - }); - }; - - onQueryChanged = (e: React.ChangeEvent) => { - const { appendedWildcard } = this.state; - const { target } = e; - - let query = target.value; - if (query.length === 1 && !appendedWildcard && canAppendWildcard(query)) { - query += '*'; - this.setState({ appendedWildcard: true }); - setTimeout(() => target.setSelectionRange(1, 1)); - } else { - if (['', '*'].includes(query) && appendedWildcard) { - query = ''; - this.setState({ appendedWildcard: false }); - } - } - - this.lastQuery = query; - this.setState({ query, showingIndexPatternQueryErrors: !!query.length }); - this.fetchIndices(query); - }; - - renderLoadingState() { - const { isLoadingIndices } = this.state; - - if (!isLoadingIndices) { - return null; - } - - return ( - <> - - - - - ); - } - - renderStatusMessage(matchedIndices: { - allIndices: MatchedItem[]; - exactMatchedIndices: MatchedItem[]; - partialMatchedIndices: MatchedItem[]; - }) { - const { indexPatternCreationType } = this.props; - const { query, isLoadingIndices, indexPatternExists, isIncludingSystemIndices } = this.state; - - if (isLoadingIndices || indexPatternExists) { - return null; - } - - return ( - - ); - } - - renderList({ - visibleIndices, - allIndices, - }: { - visibleIndices: MatchedItem[]; - allIndices: MatchedItem[]; - }) { - const { query, isLoadingIndices, indexPatternExists } = this.state; - - if (isLoadingIndices || indexPatternExists) { - return null; - } - - const indicesToList = query.length ? visibleIndices : allIndices; - return ( - - ); - } - - renderIndexPatternExists() { - const { indexPatternExists, query } = this.state; - - if (!indexPatternExists) { - return null; - } - - return ( - - } - iconType="help" - color="warning" - /> - ); - } - - renderHeader({ exactMatchedIndices: indices }: { exactMatchedIndices: MatchedItem[] }) { - const { goToNextStep, indexPatternCreationType } = this.props; - const { - query, - showingIndexPatternQueryErrors, - indexPatternExists, - indexPatternName, - isIncludingSystemIndices, - } = this.state; - - let containsErrors = false; - const errors = []; - const characterList = this.ILLEGAL_CHARACTERS.slice(0, this.ILLEGAL_CHARACTERS.length - 1).join( - ', ' - ); - - const checkIndices = indexPatternCreationType.checkIndicesForErrors(indices); - - if (!query || !query.length || query === '.' || query === '..') { - // This is an error scenario but do not report an error - containsErrors = true; - } else if (containsIllegalCharacters(query, indexPatterns.ILLEGAL_CHARACTERS)) { - const errorMessage = i18n.translate( - 'indexPatternManagement.createIndexPattern.step.invalidCharactersErrorMessage', - { - defaultMessage: - 'A {indexPatternName} cannot contain spaces or the characters: {characterList}', - values: { characterList, indexPatternName }, - } - ); - - errors.push(errorMessage); - containsErrors = true; - } else if (checkIndices) { - errors.push(...(checkIndices as string[])); - containsErrors = true; - } - - const isInputInvalid = showingIndexPatternQueryErrors && containsErrors && errors.length > 0; - const isNextStepDisabled = containsErrors || indices.length === 0 || indexPatternExists; - - return ( -
    goToNextStep(query, canPreselectTimeField(indices))} - isNextStepDisabled={isNextStepDisabled} - onChangeIncludingSystemIndices={this.onChangeIncludingSystemIndices} - isIncludingSystemIndices={isIncludingSystemIndices} - showSystemIndices={this.props.showSystemIndices} - /> - ); - } - - onChangeIncludingSystemIndices = (event: EuiSwitchEvent) => { - this.setState({ isIncludingSystemIndices: event.target.checked }, () => - this.fetchIndices(this.state.query) - ); - }; - - render() { - const { allIndices } = this.props; - const { partialMatchedIndices, exactMatchedIndices, isIncludingSystemIndices } = this.state; - - const matchedIndices = getMatchedIndices( - allIndices, - partialMatchedIndices, - exactMatchedIndices, - isIncludingSystemIndices - ); - - return ( - <> - {this.renderHeader(matchedIndices)} - - {this.renderLoadingState()} - {this.renderIndexPatternExists()} - {this.renderStatusMessage(matchedIndices)} - - {this.renderList(matchedIndices)} - - ); - } -} diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap deleted file mode 100644 index 544e3ba983122..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap +++ /dev/null @@ -1,364 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`StepTimeField should disable the action button if an invalid time field is selected 1`] = ` - -`; - -exports[`StepTimeField should enable the action button if the user decides to not select a time field 1`] = ` - -`; - -exports[`StepTimeField should render "Custom index pattern ID already exists" when error is "Conflict" 1`] = ` - -
    - - - - - - - } - > -

    - -

    -
    - - - -`; - -exports[`StepTimeField should render a loading state when creating the index pattern 1`] = ` - - - -

    - -

    -
    -
    - - - -
    -`; - -exports[`StepTimeField should render a selected timeField 1`] = ` - -
    - - - - - - - -`; - -exports[`StepTimeField should render advanced options 1`] = ` - -
    - - - - - - - -`; - -exports[`StepTimeField should render advanced options with an index pattern id 1`] = ` - -
    - - - - - - - -`; - -exports[`StepTimeField should render any error message 1`] = ` - -
    - - - - - - - } - > -

    - foobar -

    -
    - - - -`; - -exports[`StepTimeField should render normally 1`] = ` - -
    - - - - - - - -`; - -exports[`StepTimeField should render timeFields 1`] = ` - -
    - - - - - - - -`; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/action_buttons/action_buttons.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/action_buttons/action_buttons.tsx deleted file mode 100644 index 1f5b194fc2549..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/action_buttons/action_buttons.tsx +++ /dev/null @@ -1,47 +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 React from 'react'; - -import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiButtonEmpty } from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -export const ActionButtons = ({ - goToPreviousStep, - submittable, - createIndexPattern, -}: { - goToPreviousStep: () => void; - submittable: boolean; - createIndexPattern: () => void; -}) => ( - - - - - - - - - - - - -); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/action_buttons/index.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/action_buttons/index.ts deleted file mode 100644 index 56f13b55bc6be..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/action_buttons/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { ActionButtons } from './action_buttons'; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__snapshots__/advanced_options.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__snapshots__/advanced_options.test.tsx.snap deleted file mode 100644 index a2d2023ea0601..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/__snapshots__/advanced_options.test.tsx.snap +++ /dev/null @@ -1,69 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AdvancedOptions should hide if not showing 1`] = ` -
    - - - - -
    -`; - -exports[`AdvancedOptions should render normally 1`] = ` -
    - - - - - - - } - label={ - - } - labelType="label" - > - - - -
    -`; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.test.tsx deleted file mode 100644 index 0341a760c535f..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.test.tsx +++ /dev/null @@ -1,39 +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 React from 'react'; -import { AdvancedOptions } from '../advanced_options'; -import { shallowWithI18nProvider } from '@kbn/test/jest'; - -describe('AdvancedOptions', () => { - it('should render normally', () => { - const component = shallowWithI18nProvider( - {}} - onChangeIndexPatternId={() => {}} - /> - ); - - expect(component).toMatchSnapshot(); - }); - - it('should hide if not showing', () => { - const component = shallowWithI18nProvider( - {}} - onChangeIndexPatternId={() => {}} - /> - ); - - expect(component).toMatchSnapshot(); - }); -}); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.tsx deleted file mode 100644 index 665972c546158..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/advanced_options.tsx +++ /dev/null @@ -1,80 +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 React from 'react'; - -import { EuiForm, EuiFormRow, EuiFieldText, EuiButtonEmpty, EuiSpacer } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -interface AdvancedOptionsProps { - isVisible: boolean; - indexPatternId: string; - toggleAdvancedOptions: (e: React.FormEvent) => void; - onChangeIndexPatternId: (e: React.ChangeEvent) => void; -} - -export const AdvancedOptions: React.FC = ({ - isVisible, - indexPatternId, - toggleAdvancedOptions, - onChangeIndexPatternId, -}) => ( -
    - - {isVisible ? ( - - ) : ( - - )} - - - {isVisible ? ( - - - } - helpText={ - - } - > - - - - ) : null} -
    -); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/index.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/index.ts deleted file mode 100644 index 23ebc3a8c3f66..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/advanced_options/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { AdvancedOptions } from './advanced_options'; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/__snapshots__/header.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/__snapshots__/header.test.tsx.snap deleted file mode 100644 index 9efda4fdac7f9..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/__snapshots__/header.test.tsx.snap +++ /dev/null @@ -1,34 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Header should render normally 1`] = ` -
    - -

    - -

    -
    - - - - ki* - , - "indexPatternName": "ki*", - } - } - /> - -
    -`; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/header.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/header.tsx deleted file mode 100644 index 41cae957def2b..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/header/header.tsx +++ /dev/null @@ -1,42 +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 React from 'react'; - -import { EuiTitle, EuiSpacer, EuiText } from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -interface HeaderProps { - indexPattern: string; - indexPatternName: string; -} - -export const Header: React.FC = ({ indexPattern, indexPatternName }) => ( -
    - -

    - -

    -
    - - - {indexPattern}, - indexPatternName, - }} - /> - -
    -); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/__snapshots__/time_field.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/__snapshots__/time_field.test.tsx.snap deleted file mode 100644 index 73277b1963626..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/__snapshots__/time_field.test.tsx.snap +++ /dev/null @@ -1,188 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TimeField should render a loading state 1`] = ` - - -

    - -

    -
    - - - } - labelAppend={ - - } - labelType="label" - > - - -
    -`; - -exports[`TimeField should render a selected time field 1`] = ` - - -

    - -

    -
    - - - } - labelAppend={ - - - - - - } - labelType="label" - > - - -
    -`; - -exports[`TimeField should render normally 1`] = ` - - -

    - -

    -
    - - - } - labelAppend={ - - - - - - } - labelType="label" - > - - -
    -`; - -exports[`TimeField should render something if hiding time field 1`] = ` - - -

    - -

    -
    -
    -`; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/index.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/index.ts deleted file mode 100644 index 7dba3ecc1f75f..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { TimeField } from './time_field'; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.test.tsx deleted file mode 100644 index 1f16842f3c89d..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.test.tsx +++ /dev/null @@ -1,73 +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 React from 'react'; -import { TimeField } from '../time_field'; -import { shallowWithI18nProvider } from '@kbn/test/jest'; - -describe('TimeField', () => { - it('should render normally', () => { - const component = shallowWithI18nProvider( - {}} - timeFieldOptions={[{ text: '@timestamp', value: '@timestamp' }]} - isLoading={false} - selectedTimeField={''} - onTimeFieldChanged={() => {}} - /> - ); - - expect(component).toMatchSnapshot(); - }); - - it('should render something if hiding time field', () => { - const component = shallowWithI18nProvider( - {}} - timeFieldOptions={[{ text: '@timestamp', value: '@timestamp' }]} - isLoading={false} - selectedTimeField={''} - onTimeFieldChanged={() => {}} - /> - ); - - expect(component).toMatchSnapshot(); - }); - - it('should render a selected time field', () => { - const component = shallowWithI18nProvider( - {}} - timeFieldOptions={[{ text: '@timestamp', value: '@timestamp' }]} - isLoading={false} - selectedTimeField={'@timestamp'} - onTimeFieldChanged={() => {}} - /> - ); - - expect(component).toMatchSnapshot(); - }); - - it('should render a loading state', () => { - const component = shallowWithI18nProvider( - {}} - timeFieldOptions={[{ text: '@timestamp', value: '@timestamp' }]} - isLoading={true} - selectedTimeField={'@timestamp'} - onTimeFieldChanged={() => {}} - /> - ); - - expect(component).toMatchSnapshot(); - }); -}); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.tsx deleted file mode 100644 index 7fac76eb847cd..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/components/time_field/time_field.tsx +++ /dev/null @@ -1,116 +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 React from 'react'; - -import { - EuiForm, - EuiFormRow, - EuiSpacer, - EuiLink, - EuiSelect, - EuiText, - EuiLoadingSpinner, -} from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -interface TimeFieldProps { - isVisible: boolean; - fetchTimeFields: () => void; - timeFieldOptions: Array<{ text: string; value?: string }>; - isLoading: boolean; - selectedTimeField?: string; - onTimeFieldChanged: (e: React.ChangeEvent) => void; -} - -export const TimeField: React.FC = ({ - isVisible, - fetchTimeFields, - timeFieldOptions, - isLoading, - selectedTimeField, - onTimeFieldChanged, -}) => ( - - {isVisible ? ( - <> - -

    - -

    -
    - - - } - labelAppend={ - isLoading ? ( - - ) : ( - - - - - - ) - } - > - {isLoading ? ( - - ) : ( - - )} - - - ) : ( - -

    - -

    -
    - )} -
    -); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/index.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/index.ts deleted file mode 100644 index 4591dc5ae83dc..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { StepTimeField } from './step_time_field'; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx deleted file mode 100644 index fabc6ec69b0ee..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx +++ /dev/null @@ -1,331 +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 React from 'react'; -import { IndexPatternCreationConfig } from '../../../../../../../plugins/index_pattern_management/public'; -import { IndexPatternField } from '../../../../../../../plugins/data/public'; -import { mockManagementPlugin } from '../../../../mocks'; -import { createComponentWithContext } from '../../../test_utils'; - -import { StepTimeField } from '../step_time_field'; - -jest.mock('./components/header', () => ({ Header: 'Header' })); -jest.mock('./components/time_field', () => ({ TimeField: 'TimeField' })); -jest.mock('./components/advanced_options', () => ({ AdvancedOptions: 'AdvancedOptions' })); -jest.mock('./components/action_buttons', () => ({ ActionButtons: 'ActionButtons' })); -jest.mock('./../../lib', () => ({ - extractTimeFields: jest.requireActual('./../../lib').extractTimeFields, - ensureMinimumTime: async (fields: IndexPatternField) => Promise.resolve(fields), -})); - -const mockIndexPatternCreationType = new IndexPatternCreationConfig({ - type: 'default', - name: 'name', -}); - -const noop = () => {}; -const mockContext = mockManagementPlugin.createIndexPatternManagmentContext(); -const fields = [ - { - name: '@timestamp', - type: 'date', - }, -]; -mockContext.data.indexPatterns = { - create: () => ({}), - getFieldsForWildcard: jest.fn().mockReturnValue(Promise.resolve(fields)), -} as any; - -describe('StepTimeField', () => { - it('should render normally', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - expect(component).toMatchSnapshot(); - }); - - it('should render timeFields', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ - timeFields: [ - { display: '@timestamp', fieldName: '@timestamp' }, - { display: 'name', fieldName: 'name' }, - ], - }); - - expect(component).toMatchSnapshot(); - }); - - it('should render a selected timeField', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ - timeFields: [ - { display: '@timestamp', fieldName: '@timestamp' }, - { display: 'name', fieldName: 'name' }, - ], - selectedTimeField: '@timestamp', - timeFieldSet: true, - }); - - expect(component).toMatchSnapshot(); - }); - - it('should ensure disabled time field options work properly', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ - timeFields: [ - { display: '@timestamp', fieldName: '@timestamp' }, - { display: 'name', fieldName: 'name' }, - ], - }); - - // If the value is undefined, that means the user selected the - // `I don't want to use a Time filter` option - (component.instance() as StepTimeField).onTimeFieldChanged(({ - target: { value: undefined }, - } as unknown) as React.ChangeEvent); - expect(component.state('timeFieldSet')).toBe(true); - - // If the value is an empty string, that means the user selected - // an invalid selection (like the empty selection or the `-----`) - (component.instance() as StepTimeField).onTimeFieldChanged(({ - target: { value: '' }, - } as unknown) as React.ChangeEvent); - expect(component.state('timeFieldSet')).toBe(false); - }); - - it('should disable the action button if an invalid time field is selected', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ - timeFields: [ - { display: '@timestamp', fieldName: '@timestamp' }, - { display: 'name', fieldName: 'name' }, - ], - }); - - (component.instance() as StepTimeField).onTimeFieldChanged(({ - target: { value: '' }, - } as unknown) as React.ChangeEvent); - component.update(); - - expect(component.find('ActionButtons')).toMatchSnapshot(); - }); - - it('should enable the action button if the user decides to not select a time field', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ - timeFields: [ - { display: '@timestamp', fieldName: '@timestamp' }, - { display: 'name', fieldName: 'name' }, - ], - }); - - (component.instance() as StepTimeField).onTimeFieldChanged(({ - target: { value: undefined }, - } as unknown) as React.ChangeEvent); - component.update(); - - expect(component.find('ActionButtons')).toMatchSnapshot(); - }); - - it('should render advanced options', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ showingAdvancedOptions: true }); - - expect(component).toMatchSnapshot(); - }); - - it('should render advanced options with an index pattern id', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ - showingAdvancedOptions: true, - indexPatternId: 'foobar', - }); - - expect(component).toMatchSnapshot(); - }); - - it('should render a loading state when creating the index pattern', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ isCreating: true }); - - expect(component).toMatchSnapshot(); - }); - - it('should render any error message', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ error: 'foobar' }); - - expect(component).toMatchSnapshot(); - }); - - it('should render "Custom index pattern ID already exists" when error is "Conflict"', () => { - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern: noop, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - component.setState({ error: 'Conflict' }); - - expect(component).toMatchSnapshot(); - }); - - it('should remember error thrown by createIndexPatter() prop', async () => { - const createIndexPattern = async () => { - throw new Error('foobar'); - }; - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - await (component.instance() as StepTimeField).createIndexPattern(); - component.update(); - - expect(component.instance().state).toMatchObject({ - error: 'foobar', - }); - }); - - it('should call createIndexPattern with undefined time field when no time filter chosen', async () => { - const createIndexPattern = jest.fn(); - - const component = createComponentWithContext( - StepTimeField, - { - indexPattern: 'ki*', - goToPreviousStep: noop, - createIndexPattern, - indexPatternCreationType: mockIndexPatternCreationType, - }, - mockContext - ); - - await (component.instance() as StepTimeField).fetchTimeFields(); - - expect((component.state() as any).timeFields).toHaveLength(3); - - (component.instance() as StepTimeField).onTimeFieldChanged(({ - target: { value: undefined }, - } as unknown) as React.ChangeEvent); - - await (component.instance() as StepTimeField).createIndexPattern(); - - expect(createIndexPattern).toHaveBeenCalledWith(undefined, ''); - }); -}); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx deleted file mode 100644 index f489132946ec7..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx +++ /dev/null @@ -1,262 +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 React, { Component } from 'react'; -import { - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiTitle, - EuiSpacer, - EuiLoadingSpinner, - EuiHorizontalRule, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { ensureMinimumTime, extractTimeFields } from '../../lib'; - -import { Header } from './components/header'; -import { TimeField } from './components/time_field'; -import { AdvancedOptions } from './components/advanced_options'; -import { ActionButtons } from './components/action_buttons'; -import { context } from '../../../../../../kibana_react/public'; -import { IndexPatternManagmentContextValue } from '../../../../types'; -import { IndexPatternCreationConfig } from '../../../..'; - -interface StepTimeFieldProps { - indexPattern: string; - goToPreviousStep: () => void; - createIndexPattern: (selectedTimeField: string | undefined, indexPatternId: string) => void; - indexPatternCreationType: IndexPatternCreationConfig; - selectedTimeField?: string; -} - -interface StepTimeFieldState { - error: string; - timeFields: TimeFieldConfig[]; - selectedTimeField?: string; - timeFieldSet: boolean; - isAdvancedOptionsVisible: boolean; - isFetchingTimeFields: boolean; - isCreating: boolean; - indexPatternId: string; - indexPatternType: string; - indexPatternName: string; -} - -interface TimeFieldConfig { - display: string; - fieldName?: string; - isDisabled?: boolean; -} - -export class StepTimeField extends Component { - static contextType = context; - - public readonly context!: IndexPatternManagmentContextValue; - - state: StepTimeFieldState = { - error: '', - timeFields: [], - selectedTimeField: undefined, - timeFieldSet: false, - isAdvancedOptionsVisible: false, - isFetchingTimeFields: false, - isCreating: false, - indexPatternId: '', - indexPatternType: '', - indexPatternName: '', - }; - - constructor(props: StepTimeFieldProps) { - super(props); - this.state.indexPatternType = props.indexPatternCreationType.getIndexPatternType() || ''; - this.state.indexPatternName = props.indexPatternCreationType.getIndexPatternName(); - this.state.selectedTimeField = props.selectedTimeField; - if (props.selectedTimeField) { - this.state.timeFieldSet = true; - } - } - - mounted = false; - - componentDidMount() { - this.mounted = true; - this.fetchTimeFields(); - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchTimeFields = async () => { - const { indexPattern: pattern } = this.props; - const { getFetchForWildcardOptions } = this.props.indexPatternCreationType; - - this.setState({ isFetchingTimeFields: true }); - const fields = await ensureMinimumTime( - this.context.services.data.indexPatterns.getFieldsForWildcard({ - pattern, - ...getFetchForWildcardOptions(), - }) - ); - const timeFields = extractTimeFields(fields); - - this.setState({ timeFields, isFetchingTimeFields: false }); - }; - - onTimeFieldChanged = (e: React.ChangeEvent) => { - const value = e.target.value; - - // Find the time field based on the selected value - const timeField = this.state.timeFields.find( - (timeFld: TimeFieldConfig) => timeFld.fieldName === value - ); - - // If the value is an empty string, it's not a valid selection - const validSelection = value !== ''; - - this.setState({ - selectedTimeField: timeField ? (timeField as TimeFieldConfig).fieldName : undefined, - timeFieldSet: validSelection, - }); - }; - - onChangeIndexPatternId = (e: React.ChangeEvent) => { - this.setState({ indexPatternId: e.target.value }); - }; - - toggleAdvancedOptions = () => { - this.setState((state) => ({ - isAdvancedOptionsVisible: !state.isAdvancedOptionsVisible, - })); - }; - - createIndexPattern = async () => { - const { createIndexPattern } = this.props; - const { selectedTimeField, indexPatternId } = this.state; - this.setState({ isCreating: true }); - try { - await createIndexPattern(selectedTimeField, indexPatternId); - } catch (error) { - if (!this.mounted) return; - this.setState({ - error: error instanceof Error ? error.message : String(error), - isCreating: false, - }); - } - }; - - formatErrorMessage(message: string) { - // `createIndexPattern` throws "Conflict" when index pattern ID already exists. - return message === 'Conflict' ? ( - - ) : ( - message - ); - } - - render() { - const { - timeFields, - selectedTimeField, - timeFieldSet, - isAdvancedOptionsVisible, - indexPatternId, - isCreating, - isFetchingTimeFields, - indexPatternName, - } = this.state; - - if (isCreating) { - return ( - - - -

    - -

    -
    -
    - - - - -
    - ); - } - - const { indexPattern, goToPreviousStep } = this.props; - - const timeFieldOptions = - timeFields.length > 0 - ? [ - { text: '', value: '' }, - ...timeFields.map((timeField: TimeFieldConfig) => ({ - text: timeField.display, - value: timeField.fieldName, - disabled: ((timeFields as unknown) as TimeFieldConfig).isDisabled, - })), - ] - : []; - - const showTimeField = !timeFields || timeFields.length > 1; - const submittable = !showTimeField || timeFieldSet; - const error = this.state.error ? ( - <> - - } - color="danger" - iconType="cross" - > -

    {this.formatErrorMessage(this.state.error)}

    -
    - - - ) : null; - - return ( - <> -
    - - - - - - {error} - - - ); - } -} diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/constants/index.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/constants/index.ts deleted file mode 100644 index 8bc64bdaa490f..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/constants/index.ts +++ /dev/null @@ -1,17 +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. - */ - -// This isn't ideal. We want to avoid searching for 20 indices -// then filtering out the majority of them because they are system indices. -// We'd like to filter system indices out in the query -// so if we can accomplish that in the future, this logic can go away -export const ESTIMATED_NUMBER_OF_SYSTEM_INDICES = 100; -export const MAX_NUMBER_OF_MATCHING_INDICES = 100; -export const MAX_SEARCH_SIZE = MAX_NUMBER_OF_MATCHING_INDICES + ESTIMATED_NUMBER_OF_SYSTEM_INDICES; - -export const PER_PAGE_INCREMENTS = [5, 10, 20, 50]; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.test.tsx deleted file mode 100644 index a6c7e71445ae3..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.test.tsx +++ /dev/null @@ -1,162 +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 { CreateIndexPatternWizard } from './create_index_pattern_wizard'; -import { IndexPattern } from '../../../../../plugins/data/public'; -import { mockManagementPlugin } from '../../mocks'; -import { IndexPatternCreationConfig } from '../../'; -import { createComponentWithContext } from '../test_utils'; - -jest.mock('./components/step_index_pattern', () => ({ StepIndexPattern: 'StepIndexPattern' })); -jest.mock('./components/step_time_field', () => ({ StepTimeField: 'StepTimeField' })); -jest.mock('./components/header', () => ({ Header: 'Header' })); -jest.mock('./components/loading_state', () => ({ LoadingState: 'LoadingState' })); -jest.mock('./lib/get_indices', () => ({ - getIndices: () => { - return [{ name: 'kibana' }]; - }, -})); -const routeComponentPropsMock = { - history: { - push: jest.fn(), - } as any, - location: {} as any, - match: {} as any, -}; -const mockContext = mockManagementPlugin.createIndexPatternManagmentContext(); -mockContext.indexPatternManagementStart.creation.getType = () => { - return new IndexPatternCreationConfig({ - type: 'default', - name: 'name', - }); -}; - -describe('CreateIndexPatternWizard', () => { - test(`defaults to the loading state`, () => { - const component = createComponentWithContext( - CreateIndexPatternWizard, - { ...routeComponentPropsMock }, - mockContext - ); - - expect(component).toMatchSnapshot(); - }); - - test('renders the empty state when there are no indices', async () => { - const component = createComponentWithContext( - CreateIndexPatternWizard, - { ...routeComponentPropsMock }, - mockContext - ); - - component.setState({ - isInitiallyLoadingIndices: false, - allIndices: [], - remoteClustersExist: false, - }); - - await component.update(); - expect(component).toMatchSnapshot(); - }); - - test('renders when there are no indices but there are remote clusters', async () => { - const component = createComponentWithContext( - CreateIndexPatternWizard, - { ...routeComponentPropsMock }, - mockContext - ); - - component.setState({ - isInitiallyLoadingIndices: false, - allIndices: [], - remoteClustersExist: true, - }); - - await component.update(); - expect(component).toMatchSnapshot(); - }); - - test('shows system indices even if there are no other indices if the include system indices is toggled', async () => { - const component = createComponentWithContext( - CreateIndexPatternWizard, - { ...routeComponentPropsMock }, - mockContext - ); - - component.setState({ - isInitiallyLoadingIndices: false, - isIncludingSystemIndices: true, - allIndices: [{ name: '.kibana ' }], - }); - - await component.update(); - expect(component).toMatchSnapshot(); - }); - - test('renders index pattern step when there are indices', async () => { - const component = createComponentWithContext( - CreateIndexPatternWizard, - { ...routeComponentPropsMock }, - mockContext - ); - - component.setState({ - isInitiallyLoadingIndices: false, - allIndices: [{ name: 'myIndexPattern' }], - }); - - await component.update(); - expect(component).toMatchSnapshot(); - }); - - test('renders time field step when step is set to 2', async () => { - const component = createComponentWithContext( - CreateIndexPatternWizard, - { ...routeComponentPropsMock }, - mockContext - ); - - component.setState({ - isInitiallyLoadingIndices: false, - allIndices: [{ name: 'myIndexPattern' }], - step: 2, - }); - - await component.update(); - expect(component).toMatchSnapshot(); - }); - - test('invokes the provided services when creating an index pattern', async () => { - const newIndexPatternAndSave = jest.fn().mockImplementation(async () => { - return indexPattern; - }); - const clear = jest.fn(); - mockContext.data.indexPatterns.clearCache = clear; - const indexPattern = ({ - id: '1', - title: 'my-fake-index-pattern', - timeFieldName: 'timestamp', - fields: [], - _fetchFields: jest.fn(), - } as unknown) as IndexPattern; - mockContext.data.indexPatterns.createAndSave = newIndexPatternAndSave; - mockContext.data.indexPatterns.setDefault = jest.fn(); - - const component = createComponentWithContext( - CreateIndexPatternWizard, - { ...routeComponentPropsMock }, - mockContext - ); - - component.setState({ indexPattern: 'foo' }); - await (component.instance() as CreateIndexPatternWizard).createIndexPattern(undefined, 'id'); - expect(newIndexPatternAndSave).toBeCalled(); - expect(clear).toBeCalledWith('1'); - expect(routeComponentPropsMock.history.push).toBeCalledWith(`/patterns/1`); - }); -}); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx deleted file mode 100644 index 5bc53105dbcf8..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx +++ /dev/null @@ -1,284 +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 React, { Component, ReactElement } from 'react'; - -import { EuiGlobalToastList, EuiGlobalToastListToast, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { DocLinksStart } from 'src/core/public'; -import { StepIndexPattern } from './components/step_index_pattern'; -import { StepTimeField } from './components/step_time_field'; -import { Header } from './components/header'; -import { LoadingState } from './components/loading_state'; - -import { context as contextType } from '../../../../kibana_react/public'; -import { getCreateBreadcrumbs } from '../breadcrumbs'; -import { ensureMinimumTime, getIndices } from './lib'; -import { IndexPatternCreationConfig } from '../..'; -import { IndexPatternManagmentContextValue } from '../../types'; -import { MatchedItem } from './types'; -import { DuplicateIndexPatternError, IndexPattern } from '../../../../data/public'; - -interface CreateIndexPatternWizardState { - step: number; - indexPattern: string; - allIndices: MatchedItem[]; - remoteClustersExist: boolean; - isInitiallyLoadingIndices: boolean; - toasts: EuiGlobalToastListToast[]; - indexPatternCreationType: IndexPatternCreationConfig; - selectedTimeField?: string; - docLinks: DocLinksStart; -} - -export class CreateIndexPatternWizard extends Component< - RouteComponentProps, - CreateIndexPatternWizardState -> { - static contextType = contextType; - - public readonly context!: IndexPatternManagmentContextValue; - - constructor(props: RouteComponentProps, context: IndexPatternManagmentContextValue) { - super(props, context); - - context.services.setBreadcrumbs(getCreateBreadcrumbs()); - - const type = new URLSearchParams(props.location.search).get('type') || undefined; - const indexPattern = new URLSearchParams(props.location.search).get('name') || ''; - - this.state = { - step: 1, - indexPattern, - allIndices: [], - remoteClustersExist: false, - isInitiallyLoadingIndices: true, - toasts: [], - indexPatternCreationType: context.services.indexPatternManagementStart.creation.getType(type), - docLinks: context.services.docLinks, - }; - } - - async UNSAFE_componentWillMount() { - this.fetchData(); - } - - catchAndWarn = async ( - asyncFn: Promise, - errorValue: [] | string[], - errorMsg: ReactElement - ) => { - try { - return await asyncFn; - } catch (errors) { - this.setState((prevState) => ({ - toasts: prevState.toasts.concat([ - { - title: errorMsg, - id: errorMsg.props.id, - color: 'warning', - iconType: 'alert', - }, - ]), - })); - return errorValue; - } - }; - - fetchData = async () => { - const indicesFailMsg = ( - - ); - - const clustersFailMsg = ( - - ); - - // query local and remote indices, updating state independently - ensureMinimumTime( - this.catchAndWarn( - getIndices( - this.context.services.http, - (indexName: string) => this.state.indexPatternCreationType.getIndexTags(indexName), - `*`, - false - ), - - [], - indicesFailMsg - ) - ).then((allIndices: MatchedItem[]) => - this.setState({ allIndices, isInitiallyLoadingIndices: false }) - ); - - this.catchAndWarn( - // if we get an error from remote cluster query, supply fallback value that allows user entry. - // ['a'] is fallback value - getIndices( - this.context.services.http, - (indexName: string) => this.state.indexPatternCreationType.getIndexTags(indexName), - `*:*`, - false - ), - - ['a'], - clustersFailMsg - ).then((remoteIndices: string[] | MatchedItem[]) => - this.setState({ remoteClustersExist: !!remoteIndices.length }) - ); - }; - - createIndexPattern = async (timeFieldName: string | undefined, indexPatternId: string) => { - let emptyPattern: IndexPattern; - const { history } = this.props; - const { indexPattern } = this.state; - - try { - emptyPattern = await this.context.services.data.indexPatterns.createAndSave({ - id: indexPatternId, - title: indexPattern, - timeFieldName, - ...this.state.indexPatternCreationType.getIndexPatternMappings(), - }); - } catch (err) { - if (err instanceof DuplicateIndexPatternError) { - const confirmMessage = i18n.translate( - 'indexPatternManagement.indexPattern.titleExistsLabel', - { - values: { title: emptyPattern!.title }, - defaultMessage: "An index pattern with the title '{title}' already exists.", - } - ); - - const isConfirmed = await this.context.services.overlays.openConfirm(confirmMessage, { - confirmButtonText: i18n.translate( - 'indexPatternManagement.indexPattern.goToPatternButtonLabel', - { - defaultMessage: 'Go to existing pattern', - } - ), - }); - - if (isConfirmed) { - return history.push(`/patterns/${indexPatternId}`); - } else { - return; - } - } else { - throw err; - } - } - - await this.context.services.data.indexPatterns.setDefault(emptyPattern.id as string); - - this.context.services.data.indexPatterns.clearCache(emptyPattern.id as string); - history.push(`/patterns/${emptyPattern.id}`); - }; - - goToTimeFieldStep = (indexPattern: string, selectedTimeField?: string) => { - this.setState({ step: 2, indexPattern, selectedTimeField }); - }; - - goToIndexPatternStep = () => { - this.setState({ step: 1 }); - }; - - renderHeader() { - const { docLinks, indexPatternCreationType } = this.state; - return ( -
    - ); - } - - renderContent() { - const { allIndices, isInitiallyLoadingIndices, step, indexPattern } = this.state; - - if (isInitiallyLoadingIndices) { - return ; - } - - const header = this.renderHeader(); - - if (step === 1) { - const { location } = this.props; - const initialQuery = new URLSearchParams(location.search).get('id') || undefined; - - return ( - <> - {header} - - - - ); - } - - if (step === 2) { - return ( - <> - {header} - - - - ); - } - - return null; - } - - removeToast = (id: string) => { - this.setState((prevState) => ({ - toasts: prevState.toasts.filter((toast) => toast.id !== id), - })); - }; - - render() { - const content = this.renderContent(); - - return ( - <> - {content} - { - this.removeToast(id); - }} - toastLifeTimeMs={6000} - /> - - ); - } -} - -export const CreateIndexPatternWizardWithRouter = withRouter(CreateIndexPatternWizard); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/index.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/index.ts deleted file mode 100644 index 52087f388cb97..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { CreateIndexPatternWizardWithRouter } from './create_index_pattern_wizard'; diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/extract_time_fields.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/extract_time_fields.ts deleted file mode 100644 index e2af9339e57a3..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/lib/extract_time_fields.ts +++ /dev/null @@ -1,53 +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 { i18n } from '@kbn/i18n'; -import { IndexPatternField } from '../../../../../../plugins/data/public'; - -export function extractTimeFields(fields: IndexPatternField[]) { - const dateFields = fields.filter((field) => field.type === 'date'); - const label = i18n.translate( - 'indexPatternManagement.createIndexPattern.stepTime.noTimeFieldsLabel', - { - defaultMessage: "The indices which match this index pattern don't contain any time fields.", - } - ); - - if (dateFields.length === 0) { - return [ - { - display: label, - }, - ]; - } - - const disabledDividerOption = { - isDisabled: true, - display: '───', - fieldName: '', - }; - const noTimeFieldLabel = i18n.translate( - 'indexPatternManagement.createIndexPattern.stepTime.noTimeFieldOptionLabel', - { - defaultMessage: "I don't want to use the time filter", - } - ); - const noTimeFieldOption = { - display: noTimeFieldLabel, - fieldName: undefined, - }; - - return [ - ...dateFields.map((field) => ({ - display: field.name, - fieldName: field.name, - })), - disabledDividerOption, - noTimeFieldOption, - ]; -} diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts deleted file mode 100644 index 8d511a30c3532..0000000000000 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/types.ts +++ /dev/null @@ -1,59 +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. - */ - -export interface MatchedItem { - name: string; - tags: Tag[]; - item: { - name: string; - backing_indices?: string[]; - timestamp_field?: string; - indices?: string[]; - aliases?: string[]; - attributes?: ResolveIndexResponseItemIndexAttrs[]; - data_stream?: string; - }; -} - -export interface ResolveIndexResponse { - indices?: ResolveIndexResponseItemIndex[]; - aliases?: ResolveIndexResponseItemAlias[]; - data_streams?: ResolveIndexResponseItemDataStream[]; -} - -export interface ResolveIndexResponseItem { - name: string; -} - -export interface ResolveIndexResponseItemDataStream extends ResolveIndexResponseItem { - backing_indices: string[]; - timestamp_field: string; -} - -export interface ResolveIndexResponseItemAlias extends ResolveIndexResponseItem { - indices: string[]; -} - -export interface ResolveIndexResponseItemIndex extends ResolveIndexResponseItem { - aliases?: string[]; - attributes?: ResolveIndexResponseItemIndexAttrs[]; - data_stream?: string; -} - -export enum ResolveIndexResponseItemIndexAttrs { - OPEN = 'open', - CLOSED = 'closed', - HIDDEN = 'hidden', - FROZEN = 'frozen', -} - -export interface Tag { - name: string; - key: string; - color: string; -} diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx index 6609605da87d1..a8f89b471e4eb 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx @@ -25,6 +25,7 @@ import { useKibana } from '../../../../../plugins/kibana_react/public'; import { IndexPatternManagmentContext } from '../../types'; import { Tabs } from './tabs'; import { IndexHeader } from './index_header'; +import { getTags } from '../utils'; export interface EditIndexPatternProps extends RouteComponentProps { indexPattern: IndexPattern; @@ -57,7 +58,6 @@ export const EditIndexPattern = withRouter( ({ indexPattern, history, location }: EditIndexPatternProps) => { const { uiSettings, - indexPatternManagementStart, overlays, chrome, data, @@ -77,13 +77,8 @@ export const EditIndexPattern = withRouter( }, [indexPattern]); useEffect(() => { - const indexPatternTags = - indexPatternManagementStart.list.getIndexPatternTags( - indexPattern, - indexPattern.id === defaultIndex - ) || []; - setTags(indexPatternTags); - }, [defaultIndex, indexPattern, indexPatternManagementStart.list]); + setTags(getTags(indexPattern, indexPattern.id === defaultIndex)); + }, [defaultIndex, indexPattern]); const setDefaultPattern = useCallback(() => { uiSettings.set('defaultIndex', indexPattern.id); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx index 6d37e8f13d6b3..2f288dc072914 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { IndexPatternField, IndexPattern, IndexPatternType } from 'src/plugins/data/public'; import { IndexedFieldsTable } from './indexed_fields_table'; -import { RollupIndexPatternListConfig } from '../../../service/list'; +import { getFieldInfo } from '../../utils'; jest.mock('@elastic/eui', () => ({ EuiFlexGroup: 'eui-flex-group', @@ -30,7 +30,7 @@ const helpers = { editField: (fieldName: string) => {}, deleteField: (fieldName: string) => {}, // getFieldInfo handles non rollups as well - getFieldInfo: new RollupIndexPatternListConfig().getFieldInfo, + getFieldInfo, }; const indexPattern = ({ diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx index 4d99bd504cd0f..6594d677884c2 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx @@ -35,6 +35,7 @@ import { SourceFiltersTable } from '../source_filters_table'; import { IndexedFieldsTable } from '../indexed_fields_table'; import { ScriptedFieldsTable } from '../scripted_fields_table'; import { getTabs, getPath, convertToEuiSelectOption } from './utils'; +import { getFieldInfo } from '../../utils'; interface TabsProps extends Pick { indexPattern: IndexPattern; @@ -81,7 +82,6 @@ export function Tabs({ }: TabsProps) { const { uiSettings, - indexPatternManagementStart, docLinks, indexPatternFieldEditor, } = useKibana().services; @@ -227,7 +227,7 @@ export function Tabs({ helpers={{ editField: openFieldEditor, deleteField, - getFieldInfo: indexPatternManagementStart.list.getFieldInfo, + getFieldInfo, }} /> )} @@ -280,7 +280,6 @@ export function Tabs({ getFilterSection, history, indexPattern, - indexPatternManagementStart.list.getFieldInfo, indexedFieldTypeFilter, refreshFilters, scriptedFieldLanguageFilter, @@ -293,15 +292,13 @@ export function Tabs({ const euiTabs: EuiTabbedContentTab[] = useMemo( () => - getTabs(indexPattern, fieldFilter, indexPatternManagementStart.list).map( - (tab: Pick) => { - return { - ...tab, - content: getContent(tab.id), - }; - } - ), - [fieldFilter, getContent, indexPattern, indexPatternManagementStart.list] + getTabs(indexPattern, fieldFilter).map((tab: Pick) => { + return { + ...tab, + content: getContent(tab.id), + }; + }), + [fieldFilter, getContent, indexPattern] ); const [selectedTabId, setSelectedTabId] = useState(euiTabs[0].id); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts index afa786b4d71e6..76bb86eb88d9b 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts @@ -9,8 +9,8 @@ import { Dictionary, countBy, defaults, uniq } from 'lodash'; import { i18n } from '@kbn/i18n'; import { IndexPattern, IndexPatternField } from '../../../../../../plugins/data/public'; -import { IndexPatternManagementStart } from '../../../../../../plugins/index_pattern_management/public'; import { TAB_INDEXED_FIELDS, TAB_SCRIPTED_FIELDS, TAB_SOURCE_FILTERS } from '../constants'; +import { areScriptedFieldsEnabled } from '../../utils'; function filterByName(items: IndexPatternField[], filter: string) { const lowercaseFilter = (filter || '').toLowerCase(); @@ -68,11 +68,7 @@ function getTitle(type: string, filteredCount: Dictionary, totalCount: D return title + count; } -export function getTabs( - indexPattern: IndexPattern, - fieldFilter: string, - indexPatternListProvider: IndexPatternManagementStart['list'] -) { +export function getTabs(indexPattern: IndexPattern, fieldFilter: string) { const totalCount = getCounts(indexPattern.fields.getAll(), indexPattern.getSourceFiltering()); const filteredCount = getCounts( indexPattern.fields.getAll(), @@ -88,7 +84,7 @@ export function getTabs( 'data-test-subj': 'tab-indexedFields', }); - if (indexPatternListProvider.areScriptedFieldsEnabled(indexPattern)) { + if (areScriptedFieldsEnabled(indexPattern)) { tabs.push({ name: getTitle('scripted', filteredCount, totalCount), id: TAB_SCRIPTED_FIELDS, 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 c2f0c7cbfd64c..1a06fdf425500 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 545a464da0eb3..3689ae70639c8 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/components/scripting_call_outs/__snapshots__/warning_call_out.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/warning_call_out.test.tsx.snap index ba0f2aee0565f..5d417dadca923 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/warning_call_out.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/field_editor/components/scripting_call_outs/__snapshots__/warning_call_out.test.tsx.snap @@ -152,6 +152,7 @@ exports[`ScriptingWarningCallOut should render normally 1`] = ` >
    ({})); 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 b5c0186cd998c..bdc18938f9d6c 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/index_pattern_management/public/components/index.ts b/src/plugins/index_pattern_management/public/components/index.ts index 022f32fb3defc..bbe10af809c9f 100644 --- a/src/plugins/index_pattern_management/public/components/index.ts +++ b/src/plugins/index_pattern_management/public/components/index.ts @@ -13,4 +13,3 @@ export { CreateEditField, CreateEditFieldContainer, } from './edit_index_pattern'; -export { CreateIndexPatternWizardWithRouter } from './create_index_pattern_wizard'; diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/index.ts b/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/index.ts deleted file mode 100644 index 4e19a60d5a769..0000000000000 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/empty_state/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { EmptyState } from './empty_state'; diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx index 6405a81282471..ef99be4df7cb8 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx +++ b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx @@ -8,6 +8,7 @@ import { EuiBadge, + EuiButton, EuiBadgeGroup, EuiButtonEmpty, EuiInMemoryTable, @@ -15,19 +16,14 @@ import { EuiSpacer, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; +import { RouteComponentProps, withRouter, useLocation } from 'react-router-dom'; import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { reactRouterNavigate, useKibana } from '../../../../../plugins/kibana_react/public'; import { IndexPatternManagmentContext } from '../../types'; -import { CreateButton } from '../create_button'; -import { IndexPatternCreationOption, IndexPatternTableItem } from '../types'; +import { IndexPatternTableItem } from '../types'; import { getIndexPatterns } from '../utils'; import { getListBreadcrumbs } from '../breadcrumbs'; -import { EmptyState } from './empty_state'; -import { MatchedItem, ResolveIndexResponseItemAlias } from '../create_index_pattern_wizard/types'; -import { EmptyIndexPatternPrompt } from './empty_index_pattern_prompt'; -import { getIndices } from '../create_index_pattern_wizard/lib'; const pagination = { initialPageSize: 10, @@ -56,67 +52,45 @@ const title = i18n.translate('indexPatternManagement.indexPatternTable.title', { interface Props extends RouteComponentProps { canSave: boolean; + showCreateDialog?: boolean; } -export const IndexPatternTable = ({ canSave, history }: Props) => { +export const IndexPatternTable = ({ + history, + canSave, + showCreateDialog: showCreateDialogProp = false, +}: Props) => { const { setBreadcrumbs, uiSettings, indexPatternManagementStart, chrome, - docLinks, - application, - http, data, + IndexPatternEditor, } = useKibana().services; const [indexPatterns, setIndexPatterns] = useState([]); - const [creationOptions, setCreationOptions] = useState([]); - const [sources, setSources] = useState([]); - const [remoteClustersExist, setRemoteClustersExist] = useState(false); - const [isLoadingSources, setIsLoadingSources] = useState(true); const [isLoadingIndexPatterns, setIsLoadingIndexPatterns] = useState(true); + const [showCreateDialog, setShowCreateDialog] = useState(showCreateDialogProp); setBreadcrumbs(getListBreadcrumbs()); useEffect(() => { (async function () { - const options = await indexPatternManagementStart.creation.getIndexPatternCreationOptions( - history.push - ); const gettedIndexPatterns: IndexPatternTableItem[] = await getIndexPatterns( uiSettings.get('defaultIndex'), - indexPatternManagementStart, data.indexPatterns ); - setIsLoadingIndexPatterns(false); - setCreationOptions(options); setIndexPatterns(gettedIndexPatterns); + setIsLoadingIndexPatterns(false); + if (gettedIndexPatterns.length === 0) { + setShowCreateDialog(true); + } })(); - }, [history.push, indexPatterns.length, indexPatternManagementStart, uiSettings, data]); - - const removeAliases = (item: MatchedItem) => - !((item as unknown) as ResolveIndexResponseItemAlias).indices; - - const loadSources = () => { - getIndices(http, () => [], '*', false).then((dataSources) => - setSources(dataSources.filter(removeAliases)) - ); - getIndices(http, () => [], '*:*', false).then((dataSources) => - setRemoteClustersExist(!!dataSources.filter(removeAliases).length) - ); - }; - - useEffect(() => { - getIndices(http, () => [], '*', false).then((dataSources) => { - setSources(dataSources.filter(removeAliases)); - setIsLoadingSources(false); - }); - getIndices(http, () => [], '*:*', false).then((dataSources) => - setRemoteClustersExist(!!dataSources.filter(removeAliases).length) - ); - }, [http, creationOptions]); + }, [indexPatternManagementStart, uiSettings, data]); chrome.docTitle.change(title); + const isRollup = new URLSearchParams(useLocation().search).get('type') === 'rollup'; + const columns = [ { field: 'title', @@ -150,43 +124,36 @@ export const IndexPatternTable = ({ canSave, history }: Props) => { ]; const createButton = canSave ? ( - + setShowCreateDialog(true)} + data-test-subj="createIndexPatternButton" + > - + ) : ( <> ); - if (isLoadingSources || isLoadingIndexPatterns) { + if (isLoadingIndexPatterns) { return <>; } - const hasDataIndices = sources.some(({ name }: MatchedItem) => !name.startsWith('.')); - - if (!indexPatterns.length) { - if (!hasDataIndices && !remoteClustersExist) { - return ( - - ); - } else { - return ( - - ); - } - } + const displayIndexPatternEditor = showCreateDialog ? ( + { + history.push(`patterns/${indexPattern.id}`); + }} + onCancel={() => setShowCreateDialog(false)} + defaultTypeIsRollup={isRollup} + /> + ) : ( + <> + ); return (
    @@ -214,6 +181,7 @@ export const IndexPatternTable = ({ canSave, history }: Props) => { sorting={sorting} search={search} /> + {displayIndexPatternEditor}
    ); }; diff --git a/src/plugins/index_pattern_management/public/components/utils.test.ts b/src/plugins/index_pattern_management/public/components/utils.test.ts index 15e0a65390f4d..6215a8dd15c4c 100644 --- a/src/plugins/index_pattern_management/public/components/utils.test.ts +++ b/src/plugins/index_pattern_management/public/components/utils.test.ts @@ -7,7 +7,6 @@ */ import { IndexPatternsContract } from 'src/plugins/data/public'; import { getIndexPatterns } from './utils'; -import { mockManagementPlugin } from '../mocks'; const indexPatternContractMock = ({ getIdsWithTitle: jest.fn().mockReturnValue( @@ -25,13 +24,7 @@ const indexPatternContractMock = ({ get: jest.fn().mockReturnValue(Promise.resolve({})), } as unknown) as jest.Mocked; -const mockManagementPluginStart = mockManagementPlugin.createStartContract(); - test('getting index patterns', async () => { - const indexPatterns = await getIndexPatterns( - 'test', - mockManagementPluginStart, - indexPatternContractMock - ); + const indexPatterns = await getIndexPatterns('test', indexPatternContractMock); expect(indexPatterns).toMatchSnapshot(); }); diff --git a/src/plugins/index_pattern_management/public/components/utils.ts b/src/plugins/index_pattern_management/public/components/utils.ts index 68e78199798b4..6520de95028c6 100644 --- a/src/plugins/index_pattern_management/public/components/utils.ts +++ b/src/plugins/index_pattern_management/public/components/utils.ts @@ -7,11 +7,29 @@ */ import { IndexPatternsContract } from 'src/plugins/data/public'; -import { IndexPatternManagementStart } from '../plugin'; +import { IndexPattern, IFieldType } from 'src/plugins/data/public'; +import { i18n } from '@kbn/i18n'; + +const defaultIndexPatternListName = i18n.translate( + 'indexPatternManagement.editIndexPattern.list.defaultIndexPatternListName', + { + defaultMessage: 'Default', + } +); + +const rollupIndexPatternListName = i18n.translate( + 'indexPatternManagement.editIndexPattern.list.rollupIndexPatternListName', + { + defaultMessage: 'Rollup', + } +); + +const isRollup = (indexPattern: IndexPattern) => { + return indexPattern.type === 'rollup'; +}; export async function getIndexPatterns( defaultIndex: string, - indexPatternManagementStart: IndexPatternManagementStart, indexPatternsService: IndexPatternsContract ) { const existingIndexPatterns = await indexPatternsService.getIdsWithTitle(true); @@ -19,10 +37,7 @@ export async function getIndexPatterns( existingIndexPatterns.map(async ({ id, title }) => { const isDefault = defaultIndex === id; const pattern = await indexPatternsService.get(id); - const tags = (indexPatternManagementStart as IndexPatternManagementStart).list.getIndexPatternTags( - pattern, - isDefault - ); + const tags = getTags(pattern, isDefault); return { id, @@ -49,3 +64,78 @@ export async function getIndexPatterns( }) || [] ); } + +export const getTags = (indexPattern: IndexPattern, isDefault: boolean) => { + const tags = []; + if (isDefault) { + tags.push({ + key: 'default', + name: defaultIndexPatternListName, + }); + } + if (isRollup(indexPattern)) { + tags.push({ + key: 'rollup', + name: rollupIndexPatternListName, + }); + } + return tags; +}; + +export const areScriptedFieldsEnabled = (indexPattern: IndexPattern) => { + return !isRollup(indexPattern); +}; + +export const getFieldInfo = (indexPattern: IndexPattern, field: IFieldType) => { + if (!isRollup(indexPattern)) { + return []; + } + + const allAggs = indexPattern.typeMeta && indexPattern.typeMeta.aggs; + const fieldAggs = allAggs && Object.keys(allAggs).filter((agg) => allAggs[agg][field.name]); + + if (!fieldAggs || !fieldAggs.length) { + return []; + } + + return ['Rollup aggregations:'].concat( + fieldAggs.map((aggName) => { + const agg = allAggs![aggName][field.name]; + switch (aggName) { + case 'date_histogram': + return i18n.translate( + 'indexPatternManagement.editIndexPattern.list.dateHistogramSummary', + { + defaultMessage: '{aggName} (interval: {interval}, {delay} {time_zone})', + values: { + aggName, + interval: agg.fixed_interval, + delay: agg.delay + ? i18n.translate( + 'indexPatternManagement.editIndexPattern.list.DateHistogramDelaySummary', + { + defaultMessage: 'delay: {delay},', + values: { + delay: agg.delay, + }, + } + ) + : '', + time_zone: agg.time_zone, + }, + } + ); + case 'histogram': + return i18n.translate('indexPatternManagement.editIndexPattern.list.histogramSummary', { + defaultMessage: '{aggName} (interval: {interval})', + values: { + aggName, + interval: agg.interval, + }, + }); + default: + return aggName; + } + }) + ); +}; diff --git a/src/plugins/index_pattern_management/public/index.ts b/src/plugins/index_pattern_management/public/index.ts index 726c055d1b8c3..45a2f0b5a468b 100644 --- a/src/plugins/index_pattern_management/public/index.ts +++ b/src/plugins/index_pattern_management/public/index.ts @@ -24,9 +24,3 @@ export { IndexPatternManagementSetup, IndexPatternManagementStart } from './plug export function plugin(initializerContext: PluginInitializerContext) { return new IndexPatternManagementPlugin(initializerContext); } - -export { - IndexPatternCreationConfig, - IndexPatternCreationOption, - IndexPatternListConfig, -} from './service'; diff --git a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx index ec5b7c74020a5..e493595c848cf 100644 --- a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx +++ b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx @@ -20,7 +20,6 @@ import { IndexPatternTableWithRouter, EditIndexPatternContainer, CreateEditFieldContainer, - CreateIndexPatternWizardWithRouter, } from '../components'; import { IndexPatternManagementStartDependencies, IndexPatternManagementStart } from '../plugin'; import { IndexPatternManagmentContext } from '../types'; @@ -41,7 +40,7 @@ export async function mountManagementSection( ) { const [ { chrome, application, uiSettings, notifications, overlays, http, docLinks }, - { data, indexPatternFieldEditor }, + { data, indexPatternFieldEditor, indexPatternEditor }, indexPatternManagementStart, ] = await getStartServices(); const canSave = Boolean(application.capabilities.indexPatterns.save); @@ -63,6 +62,7 @@ export async function mountManagementSection( indexPatternManagementStart: indexPatternManagementStart as IndexPatternManagementStart, setBreadcrumbs: params.setBreadcrumbs, fieldFormatEditors: indexPatternFieldEditor.fieldFormatEditors, + IndexPatternEditor: indexPatternEditor.IndexPatternEditorComponent, }; ReactDOM.render( @@ -71,7 +71,7 @@ export async function mountManagementSection( - + diff --git a/src/plugins/index_pattern_management/public/mocks.ts b/src/plugins/index_pattern_management/public/mocks.ts index 7671a532d1cb8..5bcca1f09029f 100644 --- a/src/plugins/index_pattern_management/public/mocks.ts +++ b/src/plugins/index_pattern_management/public/mocks.ts @@ -12,6 +12,7 @@ import { managementPluginMock } from '../../management/public/mocks'; import { urlForwardingPluginMock } from '../../url_forwarding/public/mocks'; import { dataPluginMock } from '../../data/public/mocks'; import { indexPatternFieldEditorPluginMock } from '../../index_pattern_field_editor/public/mocks'; +import { indexPatternEditorPluginMock } from '../../index_pattern_editor/public/mocks'; import { IndexPatternManagementSetup, IndexPatternManagementStart, @@ -19,19 +20,9 @@ import { } from './plugin'; import { IndexPatternManagmentContext } from './types'; -const createSetupContract = (): IndexPatternManagementSetup => {}; +const createSetupContract = (): IndexPatternManagementSetup => ({}); -const createStartContract = (): IndexPatternManagementStart => ({ - creation: { - getType: jest.fn(), - getIndexPatternCreationOptions: jest.fn(), - } as any, - list: { - getIndexPatternTags: jest.fn(), - getFieldInfo: jest.fn(), - areScriptedFieldsEnabled: jest.fn(), - } as any, -}); +const createStartContract = (): IndexPatternManagementStart => ({}); const createInstance = async () => { const plugin = new IndexPatternManagementPlugin({} as PluginInitializerContext); @@ -40,11 +31,7 @@ const createInstance = async () => { management: managementPluginMock.createSetupContract(), urlForwarding: urlForwardingPluginMock.createSetupContract(), }); - const doStart = () => - plugin.start(coreMock.createStart(), { - data: dataPluginMock.createStartContract(), - indexPatternFieldEditor: indexPatternFieldEditorPluginMock.createStartContract(), - }); + const doStart = () => plugin.start(); return { plugin, @@ -84,6 +71,8 @@ const createIndexPatternManagmentContext = (): { indexPatternManagementStart: createStartContract(), setBreadcrumbs: () => {}, fieldFormatEditors: indexPatternFieldEditor.fieldFormatEditors, + IndexPatternEditor: indexPatternEditorPluginMock.createStartContract() + .IndexPatternEditorComponent, }; }; diff --git a/src/plugins/index_pattern_management/public/plugin.ts b/src/plugins/index_pattern_management/public/plugin.ts index d254691a0270d..9527a04a6b0e2 100644 --- a/src/plugins/index_pattern_management/public/plugin.ts +++ b/src/plugins/index_pattern_management/public/plugin.ts @@ -7,17 +7,13 @@ */ import { i18n } from '@kbn/i18n'; -import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { PluginInitializerContext, CoreSetup, Plugin } from 'src/core/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { UrlForwardingSetup } from '../../url_forwarding/public'; -import { - IndexPatternManagementService, - IndexPatternManagementServiceSetup, - IndexPatternManagementServiceStart, -} from './service'; import { ManagementSetup } from '../../management/public'; import { IndexPatternFieldEditorStart } from '../../index_pattern_field_editor/public'; +import { IndexPatternEditorStart } from '../../index_pattern_editor/public'; export interface IndexPatternManagementSetupDependencies { management: ManagementSetup; @@ -27,11 +23,14 @@ export interface IndexPatternManagementSetupDependencies { export interface IndexPatternManagementStartDependencies { data: DataPublicPluginStart; indexPatternFieldEditor: IndexPatternFieldEditorStart; + indexPatternEditor: IndexPatternEditorStart; } -export type IndexPatternManagementSetup = IndexPatternManagementServiceSetup; +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IndexPatternManagementSetup {} -export type IndexPatternManagementStart = IndexPatternManagementServiceStart; +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IndexPatternManagementStart {} const sectionsHeader = i18n.translate('indexPatternManagement.indexPattern.sectionsHeader', { defaultMessage: 'Index Patterns', @@ -47,8 +46,6 @@ export class IndexPatternManagementPlugin IndexPatternManagementSetupDependencies, IndexPatternManagementStartDependencies > { - private readonly indexPatternManagementService = new IndexPatternManagementService(); - constructor(initializerContext: PluginInitializerContext) {} public setup( @@ -80,16 +77,12 @@ export class IndexPatternManagementPlugin return mountManagementSection(core.getStartServices, params); }, }); + return {}; } - public start(core: CoreStart, plugins: IndexPatternManagementStartDependencies) { - return this.indexPatternManagementService.start({ - httpClient: core.http, - uiSettings: core.uiSettings, - }); + public start() { + return {}; } - public stop() { - this.indexPatternManagementService.stop(); - } + public stop() {} } diff --git a/src/plugins/index_pattern_management/public/service/creation/components/rollup_prompt/index.ts b/src/plugins/index_pattern_management/public/service/creation/components/rollup_prompt/index.ts deleted file mode 100644 index d1fc2fa242eb1..0000000000000 --- a/src/plugins/index_pattern_management/public/service/creation/components/rollup_prompt/index.ts +++ /dev/null @@ -1,9 +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. - */ - -export { RollupPrompt } from './rollup_prompt'; diff --git a/src/plugins/index_pattern_management/public/service/creation/components/rollup_prompt/rollup_prompt.tsx b/src/plugins/index_pattern_management/public/service/creation/components/rollup_prompt/rollup_prompt.tsx deleted file mode 100644 index 81fcdaedb90c9..0000000000000 --- a/src/plugins/index_pattern_management/public/service/creation/components/rollup_prompt/rollup_prompt.tsx +++ /dev/null @@ -1,40 +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 React from 'react'; -import { i18n } from '@kbn/i18n'; - -import { EuiCallOut } from '@elastic/eui'; - -export const RollupPrompt = () => ( - -

    - {i18n.translate( - 'indexPatternManagement.editRollupIndexPattern.rollupPrompt.betaCalloutParagraph1Text', - { - defaultMessage: - "Kibana's support for rollup index patterns is in beta. You might encounter issues using " + - 'these patterns in saved searches, visualizations, and dashboards. They are not supported in ' + - 'some advanced features, such as Timelion, and Machine Learning.', - } - )} -

    -

    - {i18n.translate( - 'indexPatternManagement.editRollupIndexPattern.rollupPrompt.betaCalloutParagraph2Text', - { - defaultMessage: - 'You can match a rollup index pattern against one rollup index and zero or more regular ' + - 'indices. A rollup index pattern has limited metrics, fields, intervals, and aggregations. A ' + - 'rollup index is limited to indices that have one job configuration, or multiple jobs with ' + - 'compatible configurations.', - } - )} -

    -
    -); diff --git a/src/plugins/index_pattern_management/public/service/creation/config.ts b/src/plugins/index_pattern_management/public/service/creation/config.ts deleted file mode 100644 index 6d7e3aa9e5ede..0000000000000 --- a/src/plugins/index_pattern_management/public/service/creation/config.ts +++ /dev/null @@ -1,112 +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 { i18n } from '@kbn/i18n'; -import { MatchedItem } from '../../components/create_index_pattern_wizard/types'; - -const indexPatternTypeName = i18n.translate( - 'indexPatternManagement.editIndexPattern.createIndex.defaultTypeName', - { defaultMessage: 'index pattern' } -); - -const indexPatternButtonText = i18n.translate( - 'indexPatternManagement.editIndexPattern.createIndex.defaultButtonText', - { defaultMessage: 'Standard index pattern' } -); - -const indexPatternButtonDescription = i18n.translate( - 'indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription', - { defaultMessage: 'Perform full aggregations against any data' } -); - -export type UrlHandler = (url: string) => void; - -export interface IndexPatternCreationOption { - text: string; - description: string; - testSubj: string; - onClick: () => void; - isBeta?: boolean; -} - -export class IndexPatternCreationConfig { - public readonly key = 'default'; - - protected type?: string; - protected name: string; - protected showSystemIndices: boolean; - protected httpClient: object | null; - protected isBeta: boolean; - - constructor({ - type = undefined, - name = indexPatternTypeName, - showSystemIndices = true, - httpClient = null, - isBeta = false, - }: { - type?: string; - name?: string; - showSystemIndices?: boolean; - httpClient?: object | null; - isBeta?: boolean; - }) { - this.type = type; - this.name = name; - this.showSystemIndices = showSystemIndices; - this.httpClient = httpClient; - this.isBeta = isBeta; - } - - public getIndexPatternCreationOption(urlHandler: UrlHandler): IndexPatternCreationOption { - return { - text: indexPatternButtonText, - description: indexPatternButtonDescription, - testSubj: `createStandardIndexPatternButton`, - onClick: () => { - urlHandler('/create'); - }, - }; - } - - public getIndexPatternType() { - return this.type; - } - - public getIndexPatternName() { - return this.name; - } - - public getIsBeta() { - return this.isBeta; - } - - public getShowSystemIndices() { - return this.showSystemIndices; - } - - public getIndexTags(indexName: string) { - return []; - } - - public checkIndicesForErrors(indices: MatchedItem[]) { - return undefined; - } - - public getIndexPatternMappings() { - return {}; - } - - public renderPrompt() { - return null; - } - - public getFetchForWildcardOptions() { - return {}; - } -} diff --git a/src/plugins/index_pattern_management/public/service/creation/index.ts b/src/plugins/index_pattern_management/public/service/creation/index.ts deleted file mode 100644 index e1f464b01e550..0000000000000 --- a/src/plugins/index_pattern_management/public/service/creation/index.ts +++ /dev/null @@ -1,12 +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. - */ - -export { IndexPatternCreationConfig, IndexPatternCreationOption } from './config'; -export { IndexPatternCreationManager } from './manager'; -// @ts-ignore -export { RollupIndexPatternCreationConfig } from './rollup_creation_config'; diff --git a/src/plugins/index_pattern_management/public/service/creation/manager.ts b/src/plugins/index_pattern_management/public/service/creation/manager.ts deleted file mode 100644 index cc2285bbfcafb..0000000000000 --- a/src/plugins/index_pattern_management/public/service/creation/manager.ts +++ /dev/null @@ -1,70 +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 { once } from 'lodash'; -import { HttpStart, CoreStart } from '../../../../../core/public'; -import { IndexPatternCreationConfig, UrlHandler, IndexPatternCreationOption } from './config'; -import { CONFIG_ROLLUPS } from '../../constants'; -// @ts-ignore -import { RollupIndexPatternCreationConfig } from './rollup_creation_config'; - -interface IndexPatternCreationManagerStart { - httpClient: HttpStart; - uiSettings: CoreStart['uiSettings']; -} - -export class IndexPatternCreationManager { - start({ httpClient, uiSettings }: IndexPatternCreationManagerStart) { - const getConfigs = once(() => { - const configs: IndexPatternCreationConfig[] = []; - configs.push(new IndexPatternCreationConfig({ httpClient })); - - if (uiSettings.isDeclared(CONFIG_ROLLUPS) && uiSettings.get(CONFIG_ROLLUPS)) { - configs.push(new RollupIndexPatternCreationConfig({ httpClient })); - } - - return configs; - }); - - const getType = (key: string | undefined): IndexPatternCreationConfig => { - const configs = getConfigs(); - if (key) { - const index = configs.findIndex((config) => config.key === key); - const config = configs[index]; - - if (config) { - return config; - } else { - throw new Error(`Index pattern creation type not found: ${key}`); - } - } else { - return getType('default'); - } - }; - - return { - getType, - getIndexPatternCreationOptions: async (urlHandler: UrlHandler) => { - const options: IndexPatternCreationOption[] = []; - - await Promise.all( - getConfigs().map(async (config) => { - const option = config.getIndexPatternCreationOption - ? await config.getIndexPatternCreationOption(urlHandler) - : null; - if (option) { - options.push(option); - } - }) - ); - - return options; - }, - }; - } -} diff --git a/src/plugins/index_pattern_management/public/service/creation/rollup_creation_config.js b/src/plugins/index_pattern_management/public/service/creation/rollup_creation_config.js deleted file mode 100644 index 2a85dfa01143c..0000000000000 --- a/src/plugins/index_pattern_management/public/service/creation/rollup_creation_config.js +++ /dev/null @@ -1,169 +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 React from 'react'; -import { i18n } from '@kbn/i18n'; - -import { RollupPrompt } from './components/rollup_prompt'; -import { IndexPatternCreationConfig } from '.'; - -const rollupIndexPatternTypeName = i18n.translate( - 'indexPatternManagement.editRollupIndexPattern.createIndex.defaultTypeName', - { defaultMessage: 'rollup index pattern' } -); - -const rollupIndexPatternButtonText = i18n.translate( - 'indexPatternManagement.editRollupIndexPattern.createIndex.defaultButtonText', - { defaultMessage: 'Rollup index pattern' } -); - -const rollupIndexPatternButtonDescription = i18n.translate( - 'indexPatternManagement.editRollupIndexPattern.createIndex.defaultButtonDescription', - { defaultMessage: 'Perform limited aggregations against summarized data' } -); - -const rollupIndexPatternNoMatchError = i18n.translate( - 'indexPatternManagement.editRollupIndexPattern.createIndex.noMatchError', - { defaultMessage: 'Rollup index pattern error: must match one rollup index' } -); - -const rollupIndexPatternTooManyMatchesError = i18n.translate( - 'indexPatternManagement.editRollupIndexPattern.createIndex.tooManyMatchesError', - { defaultMessage: 'Rollup index pattern error: can only match one rollup index' } -); - -const rollupIndexPatternIndexLabel = i18n.translate( - 'indexPatternManagement.editRollupIndexPattern.createIndex.indexLabel', - { defaultMessage: 'Rollup' } -); - -export class RollupIndexPatternCreationConfig extends IndexPatternCreationConfig { - key = 'rollup'; - - constructor(options) { - super({ - type: 'rollup', - name: rollupIndexPatternTypeName, - showSystemIndices: false, - isBeta: true, - ...options, - }); - - this.rollupIndex = null; - this.rollupJobs = []; - this.rollupIndicesCapabilities = {}; - this.rollupIndices = []; - } - - async setRollupIndices() { - try { - // This is a hack intended to prevent the getRollupIndices() request from being sent if - // we're on /logout. There is a race condition that can arise on that page, whereby this - // request resolves after the logout request resolves, and un-clears the session ID. - const isAnonymous = this.httpClient.anonymousPaths.isAnonymous(window.location.pathname); - if (!isAnonymous) { - const response = await this.httpClient.get('/api/rollup/indices'); - this.rollupIndicesCapabilities = response || {}; - } - - this.rollupIndices = Object.keys(this.rollupIndicesCapabilities); - } catch (e) { - // Silently swallow failure responses such as expired trials - } - } - - async getIndexPatternCreationOption(urlHandler) { - await this.setRollupIndices(); - return this.rollupIndices && this.rollupIndices.length - ? { - text: rollupIndexPatternButtonText, - description: rollupIndexPatternButtonDescription, - testSubj: `createRollupIndexPatternButton`, - isBeta: this.isBeta, - onClick: () => { - urlHandler('/create?type=rollup'); - }, - } - : null; - } - - isRollupIndex = (indexName) => { - return this.rollupIndices.includes(indexName); - }; - - getIndexTags(indexName) { - return this.isRollupIndex(indexName) - ? [ - { - key: this.type, - name: rollupIndexPatternIndexLabel, - color: 'primary', - }, - ] - : []; - } - - checkIndicesForErrors = (indices) => { - this.rollupIndex = null; - - if (!indices || !indices.length) { - return; - } - - const rollupIndices = indices.filter((index) => this.isRollupIndex(index.name)); - - if (!rollupIndices.length) { - return [rollupIndexPatternNoMatchError]; - } else if (rollupIndices.length > 1) { - return [rollupIndexPatternTooManyMatchesError]; - } - - const rollupIndexName = rollupIndices[0].name; - const error = this.rollupIndicesCapabilities[rollupIndexName].error; - - if (error) { - const errorMessage = i18n.translate( - 'indexPatternManagement.editRollupIndexPattern.createIndex.uncaughtError', - { - defaultMessage: 'Rollup index pattern error: {error}', - values: { - error, - }, - } - ); - return [errorMessage]; - } - - this.rollupIndex = rollupIndexName; - }; - - getIndexPatternMappings = () => { - return this.rollupIndex - ? { - type: this.type, - typeMeta: { - params: { - rollup_index: this.rollupIndex, - }, - aggs: this.rollupIndicesCapabilities[this.rollupIndex].aggs, - }, - } - : {}; - }; - - renderPrompt = () => { - return ; - }; - - getFetchForWildcardOptions = () => { - return { - type: this.type, - rollupIndex: this.rollupIndex, - }; - }; -} diff --git a/src/plugins/index_pattern_management/public/service/index.ts b/src/plugins/index_pattern_management/public/service/index.ts deleted file mode 100644 index 8bf09cf544648..0000000000000 --- a/src/plugins/index_pattern_management/public/service/index.ts +++ /dev/null @@ -1,11 +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. - */ - -export * from './index_pattern_management_service'; -export { IndexPatternCreationConfig, IndexPatternCreationOption } from './creation'; -export { IndexPatternListConfig } from './list'; diff --git a/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts b/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts deleted file mode 100644 index 25a36faa1c3e3..0000000000000 --- a/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 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 { HttpStart, CoreStart } from '../../../../core/public'; -import { IndexPatternCreationManager } from './creation'; -import { IndexPatternListManager } from './list'; - -interface StartDependencies { - httpClient: HttpStart; - uiSettings: CoreStart['uiSettings']; -} - -/** - * Index patterns management service - * - * @internal - */ -export class IndexPatternManagementService { - indexPatternCreationManager: IndexPatternCreationManager; - indexPatternListConfig: IndexPatternListManager; - - constructor() { - this.indexPatternCreationManager = new IndexPatternCreationManager(); - this.indexPatternListConfig = new IndexPatternListManager(); - } - - public setup() {} - - public start({ httpClient, uiSettings }: StartDependencies) { - return { - creation: this.indexPatternCreationManager.start({ httpClient, uiSettings }), - list: this.indexPatternListConfig.start({ uiSettings }), - }; - } - - public stop() { - // nothing to do here yet. - } -} - -/** @internal */ -export type IndexPatternManagementServiceSetup = ReturnType; -export type IndexPatternManagementServiceStart = ReturnType; diff --git a/src/plugins/index_pattern_management/public/service/list/config.ts b/src/plugins/index_pattern_management/public/service/list/config.ts deleted file mode 100644 index 4be27fc47d0db..0000000000000 --- a/src/plugins/index_pattern_management/public/service/list/config.ts +++ /dev/null @@ -1,45 +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 { i18n } from '@kbn/i18n'; -import { IndexPattern, IndexPatternField, IndexPatternType } from '../../../../data/public'; - -export interface IndexPatternTag { - key: string; - name: string; -} - -const defaultIndexPatternListName = i18n.translate( - 'indexPatternManagement.editIndexPattern.list.defaultIndexPatternListName', - { - defaultMessage: 'Default', - } -); - -export class IndexPatternListConfig { - public readonly key: IndexPatternType = IndexPatternType.DEFAULT; - - public getIndexPatternTags(indexPattern: IndexPattern, isDefault: boolean): IndexPatternTag[] { - return isDefault - ? [ - { - key: 'default', - name: defaultIndexPatternListName, - }, - ] - : []; - } - - public getFieldInfo(indexPattern: IndexPattern, field: IndexPatternField): string[] { - return []; - } - - public areScriptedFieldsEnabled(indexPattern: IndexPattern): boolean { - return true; - } -} diff --git a/src/plugins/index_pattern_management/public/service/list/index.ts b/src/plugins/index_pattern_management/public/service/list/index.ts deleted file mode 100644 index 738b807ac7624..0000000000000 --- a/src/plugins/index_pattern_management/public/service/list/index.ts +++ /dev/null @@ -1,12 +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. - */ - -export { IndexPatternListConfig } from './config'; -export { IndexPatternListManager } from './manager'; -// @ts-ignore -export { RollupIndexPatternListConfig } from './rollup_list_config'; diff --git a/src/plugins/index_pattern_management/public/service/list/manager.ts b/src/plugins/index_pattern_management/public/service/list/manager.ts deleted file mode 100644 index d9cefbd8001a5..0000000000000 --- a/src/plugins/index_pattern_management/public/service/list/manager.ts +++ /dev/null @@ -1,55 +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 { IndexPattern, IndexPatternField } from 'src/plugins/data/public'; -import { once } from 'lodash'; -import { CoreStart } from '../../../../../core/public'; -import { IndexPatternListConfig, IndexPatternTag } from './config'; -import { CONFIG_ROLLUPS } from '../../constants'; -import { RollupIndexPatternListConfig } from './rollup_list_config'; - -interface IndexPatternListManagerStart { - uiSettings: CoreStart['uiSettings']; -} - -export class IndexPatternListManager { - start({ uiSettings }: IndexPatternListManagerStart) { - const getConfigs = once(() => { - const configs: IndexPatternListConfig[] = []; - configs.push(new IndexPatternListConfig()); - - if (uiSettings.isDeclared(CONFIG_ROLLUPS) && uiSettings.get(CONFIG_ROLLUPS)) { - configs.push(new RollupIndexPatternListConfig()); - } - - return configs; - }); - return { - getIndexPatternTags: (indexPattern: IndexPattern, isDefault: boolean) => - getConfigs().reduce( - (tags: IndexPatternTag[], config) => - config.getIndexPatternTags - ? tags.concat(config.getIndexPatternTags(indexPattern, isDefault)) - : tags, - [] - ), - - getFieldInfo: (indexPattern: IndexPattern, field: IndexPatternField): string[] => - getConfigs().reduce( - (info: string[], config) => - config.getFieldInfo ? info.concat(config.getFieldInfo(indexPattern, field)) : info, - [] - ), - - areScriptedFieldsEnabled: (indexPattern: IndexPattern): boolean => - getConfigs().every((config) => - config.areScriptedFieldsEnabled ? config.areScriptedFieldsEnabled(indexPattern) : true - ), - }; - } -} diff --git a/src/plugins/index_pattern_management/public/service/list/rollup_list_config.ts b/src/plugins/index_pattern_management/public/service/list/rollup_list_config.ts deleted file mode 100644 index bb9da0d461701..0000000000000 --- a/src/plugins/index_pattern_management/public/service/list/rollup_list_config.ts +++ /dev/null @@ -1,62 +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 { IndexPattern, IndexPatternField, IndexPatternType } from '../../../../data/public'; -import { IndexPatternListConfig } from '.'; - -function isRollup(indexPattern: IndexPattern) { - return indexPattern.type === IndexPatternType.ROLLUP; -} - -export class RollupIndexPatternListConfig extends IndexPatternListConfig { - key = IndexPatternType.ROLLUP; - - getIndexPatternTags = (indexPattern: IndexPattern) => { - return isRollup(indexPattern) - ? [ - { - key: 'rollup', - name: 'Rollup', - }, - ] - : []; - }; - - getFieldInfo = (indexPattern: IndexPattern, field: IndexPatternField) => { - if (!isRollup(indexPattern)) { - return []; - } - - const allAggs = indexPattern.typeMeta && indexPattern.typeMeta.aggs; - const fieldAggs = allAggs && Object.keys(allAggs).filter((agg) => allAggs[agg][field.name]); - - if (!fieldAggs || !fieldAggs.length) { - return []; - } - - return ['Rollup aggregations:'].concat( - fieldAggs.map((aggName) => { - const agg = allAggs![aggName][field.name]; - switch (aggName) { - case 'date_histogram': - return `${aggName} (interval: ${agg.fixed_interval}, ${ - agg.delay ? `delay: ${agg.delay},` : '' - } ${agg.time_zone})`; - case 'histogram': - return `${aggName} (interval: ${agg.interval})`; - default: - return aggName; - } - }) - ); - }; - - areScriptedFieldsEnabled = (indexPattern: IndexPattern) => { - return !isRollup(indexPattern); - }; -} diff --git a/src/plugins/index_pattern_management/public/types.ts b/src/plugins/index_pattern_management/public/types.ts index a61eeb99b25a5..b2c77fccf90b9 100644 --- a/src/plugins/index_pattern_management/public/types.ts +++ b/src/plugins/index_pattern_management/public/types.ts @@ -20,6 +20,7 @@ import { ManagementAppMountParams } from '../../management/public'; import { IndexPatternManagementStart } from './index'; import { KibanaReactContextValue } from '../../kibana_react/public'; import { IndexPatternFieldEditorStart } from '../../index_pattern_field_editor/public'; +import { IndexPatternEditorStart } from '../../index_pattern_editor/public'; export interface IndexPatternManagmentContext { chrome: ChromeStart; @@ -34,6 +35,7 @@ export interface IndexPatternManagmentContext { indexPatternManagementStart: IndexPatternManagementStart; setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs']; fieldFormatEditors: IndexPatternFieldEditorStart['fieldFormatEditors']; + IndexPatternEditor: IndexPatternEditorStart['IndexPatternEditorComponent']; } export type IndexPatternManagmentContextValue = KibanaReactContextValue; diff --git a/src/plugins/index_pattern_management/tsconfig.json b/src/plugins/index_pattern_management/tsconfig.json index 37bd3e4aa5bbb..16afcb3599fec 100644 --- a/src/plugins/index_pattern_management/tsconfig.json +++ b/src/plugins/index_pattern_management/tsconfig.json @@ -20,5 +20,6 @@ { "path": "../kibana_utils/tsconfig.json" }, { "path": "../es_ui_shared/tsconfig.json" }, { "path": "../index_pattern_field_editor/tsconfig.json" }, + { "path": "../index_pattern_editor/tsconfig.json" }, ] } 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 ff7da1725fa7f..b797ea44d1f91 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/index_pattern_management/public/components/create_index_pattern_wizard/components/header/index.ts b/src/plugins/kibana_react/public/code_editor/languages/yaml/constants.ts similarity index 91% rename from src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/index.ts rename to src/plugins/kibana_react/public/code_editor/languages/yaml/constants.ts index 8f737b3a42613..71a34088a216b 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/header/index.ts +++ b/src/plugins/kibana_react/public/code_editor/languages/yaml/constants.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { Header } from './header'; +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 0000000000000..f501de74debec --- /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 0000000000000..d17f284b28408 --- /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 b4a0f4d53cdf4..a32318a7e4b20 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_react/public/overview_page/overview_page_footer/overview_page_footer.tsx b/src/plugins/kibana_react/public/overview_page/overview_page_footer/overview_page_footer.tsx index 09554292072f9..4c96b45353cc9 100644 --- a/src/plugins/kibana_react/public/overview_page/overview_page_footer/overview_page_footer.tsx +++ b/src/plugins/kibana_react/public/overview_page/overview_page_footer/overview_page_footer.tsx @@ -50,7 +50,9 @@ export const OverviewPageFooter: FC = ({ iconType="home" size="xs" onClick={(event: MouseEvent) => { - application.navigateToUrl(addBasePath('/app/management/kibana/settings#defaultRoute')); + application.navigateToUrl( + addBasePath('/app/management/kibana/settings?query=default+route') + ); if (onChangeDefaultRoute) { onChangeDefaultRoute(event); } 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 ace26dc9e6295..57c9893f0cfea 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 95faaee88fa61..52cf38d32c2e2 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/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap index 5a8cd06b8ecc0..9c9349b0524c0 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/intro.test.tsx.snap @@ -59,6 +59,7 @@ exports[`Intro component renders correctly 1`] = ` >
    diff --git a/src/plugins/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts index 8aeb26e8253b6..f6b99badca492 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 0000000000000..74d45f6a9f7d4 --- /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 0000000000000..a0af7878afef6 --- /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 cc4ff102742d7..eb268639cad91 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 418aeace77c70..405c6620e802a 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 04fabe3d62b17..73dc07d7a4fb9 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 94630c0cb8d80..b23ba127c1522 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 515d2039a4a11..4e52ec3a7e6ed 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 6947b6cc3ce38..3fceb7913eae6 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 8123ae4c66e3c..8d75f0aba1726 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 0000000000000..0823b521b23a7 --- /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 0000000000000..5507c3e1f6675 --- /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/telemetry/server/config/index.ts b/src/plugins/telemetry/server/config/index.ts new file mode 100644 index 0000000000000..19ccd73e17fcd --- /dev/null +++ b/src/plugins/telemetry/server/config/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { 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 fb188a2414b98..e15b5be2604ec 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 530f7c499c3f2..6b56996c756aa 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 40714bf4cf2be..d38f054a4402e 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 196631f33384d..e3fad0642303c 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 6660a36e76199..acc9a863af61b 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 77ec981d4c35b..f6b7eddcbe765 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 014142a2a3d06..7c9154cba4f88 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 fe6f8e254142b..b8332317e6b68 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 24583260329a6..2e3b1beb64d3c 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 c89e5ec1abaff..93a5afed6a92e 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 542055d185ec5..74f254c6176bc 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 27bb775c2d0e8..34003bced5ad0 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 66be8febc496f..87ca902f6c090 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 7441848d5a430..bee666a5906cd 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 b79eed2087a16..c0f4a8a6112f8 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 251ff8acc698e..f1a4791821c12 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 e1029b11a7b75..5addae51dd011 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 f12db316f1972..69bd2855b9843 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 cbc3db6585a7d..2f423538568bd 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 8f35909d3bfba..4df6f2b94e17b 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 0280637acc099..51eef1446bac6 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 ccff3c349cf21..50277d51e8748 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_timeseries/kibana.json b/src/plugins/vis_type_timeseries/kibana.json index 3a2ad6bdd77cf..c172640a4c14e 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 04af5786d2e8e..9b9beae67e44f 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 4db038de912f5..ba235a20b97ce 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/vis.test.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.test.js index b2aa4a6d0c9fe..fd155623d5da7 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_vislib/kibana.json b/src/plugins/vis_type_vislib/kibana.json index 26d20b1ff839f..412264a3e7270 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 5e1f0bfbb4464..04ab8db1cda8f 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 04afc86548f22..815d0e10aafb2 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 b7f5b966a08c6..65f7df6459bfe 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 0dc1e18270f78..a25d408769273 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/setup_node_env/exit_on_warning.js b/src/setup_node_env/exit_on_warning.js index 9a6e608b53f0f..5fbee02708083 100644 --- a/src/setup_node_env/exit_on_warning.js +++ b/src/setup_node_env/exit_on_warning.js @@ -29,6 +29,14 @@ var IGNORE_WARNINGS = [ file: '/node_modules/supertest/node_modules/superagent/lib/node/index.js', line: 418, }, + { + // TODO: @elastic/es-clients - The new client will attempt a Product check and it will `process.emitWarning` + // that the security features are blocking such check. + // Such emit is causing Node.js to crash unless we explicitly catch it. + // We need to discard that warning + message: + 'The client is unable to verify that the server is Elasticsearch due to security privileges on the server side. Some functionality may not be compatible if the server is running an unsupported product.', + }, ]; if (process.noProcessWarnings !== true) { diff --git a/test/accessibility/apps/management.ts b/test/accessibility/apps/management.ts index 7a99e5832448f..538755b482fbf 100644 --- a/test/accessibility/apps/management.ts +++ b/test/accessibility/apps/management.ts @@ -70,6 +70,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.settings.clickAddNewIndexPatternButton(); await PageObjects.header.waitUntilLoadingHasFinished(); await a11y.testAppSnapshot(); + await testSubjects.click('closeFlyoutButton'); }); // We are navigating back to index pattern page to test field formatters diff --git a/test/common/config.js b/test/common/config.js index 84848347f94cd..5b5d01cfeb1e4 100644 --- a/test/common/config.js +++ b/test/common/config.js @@ -48,8 +48,7 @@ export default function () { '--telemetry.banner=false', '--telemetry.optIn=false', // These are *very* important to have them pointing to staging - '--telemetry.url=https://telemetry-staging.elastic.co/xpack/v2/send', - '--telemetry.optInStatusUrl=https://telemetry-staging.elastic.co/opt_in_status/v2/send', + '--telemetry.sendUsageTo=staging', `--server.maxPayload=1679958`, // newsfeed mock service `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'newsfeed')}`, diff --git a/test/functional/apps/management/_create_index_pattern_wizard.js b/test/functional/apps/management/_create_index_pattern_wizard.js index d4b49d74d1b90..b2f24e530cb12 100644 --- a/test/functional/apps/management/_create_index_pattern_wizard.js +++ b/test/functional/apps/management/_create_index_pattern_wizard.js @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import expect from '@kbn/expect'; - export default function ({ getService, getPageObjects }) { const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); @@ -23,23 +21,6 @@ export default function ({ getService, getPageObjects }) { await PageObjects.settings.clickKibanaIndexPatterns(); }); - describe('step 1 next button', function () { - it('is disabled by default', async function () { - await (await testSubjects.find('createIndexPatternButton')).click(); - const btn = await PageObjects.settings.getCreateIndexPatternGoToStep2Button(); - const isEnabled = await btn.isEnabled(); - expect(isEnabled).not.to.be.ok(); - }); - - it('is enabled once an index pattern with matching indices has been entered', async function () { - await PageObjects.settings.setIndexPatternField(); - await PageObjects.common.sleep(1000); - const btn = await PageObjects.settings.getCreateIndexPatternGoToStep2Button(); - const isEnabled = await btn.isEnabled(); - expect(isEnabled).to.be.ok(); - }); - }); - describe('index alias', () => { before(async function () { await security.testUser.setRoles(['kibana_admin', 'test_alias1_reader']); diff --git a/test/functional/apps/management/_index_pattern_create_delete.js b/test/functional/apps/management/_index_pattern_create_delete.js index b195e4c8d5857..5be5cb3418bf8 100644 --- a/test/functional/apps/management/_index_pattern_create_delete.js +++ b/test/functional/apps/management/_index_pattern_create_delete.js @@ -14,6 +14,7 @@ export default function ({ getService, getPageObjects }) { const log = getService('log'); const retry = getService('retry'); const testSubjects = getService('testSubjects'); + const find = getService('find'); const PageObjects = getPageObjects(['settings', 'common', 'header']); describe('creating and deleting default index', function describeIndexTests() { @@ -29,6 +30,30 @@ export default function ({ getService, getPageObjects }) { }); }); + describe('can open and close editor', function () { + it('without creating index pattern', async function () { + await PageObjects.settings.clickKibanaIndexPatterns(); + await PageObjects.settings.clickAddNewIndexPatternButton(); + await testSubjects.click('closeFlyoutButton'); + await testSubjects.find('createIndexPatternButton'); + }); + }); + + describe('validation', function () { + it('can display errors', async function () { + await PageObjects.settings.clickAddNewIndexPatternButton(); + await PageObjects.settings.setIndexPatternField('log*'); + await (await PageObjects.settings.getSaveIndexPatternButton()).click(); + await find.byClassName('euiFormErrorText'); + }); + + it('can resolve errors and submit', async function () { + await PageObjects.settings.selectTimeFieldOption('@timestamp'); + await (await PageObjects.settings.getSaveIndexPatternButton()).click(); + await PageObjects.settings.removeIndexPattern(); + }); + }); + describe('special character handling', () => { it('should handle special charaters in template input', async () => { await PageObjects.settings.clickAddNewIndexPatternButton(); diff --git a/test/functional/apps/visualize/legacy/index.ts b/test/functional/apps/visualize/legacy/index.ts index d474287860b25..37cf8a5950592 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/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index cb8f198177017..9cdd33ef768f9 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -43,7 +43,10 @@ export class SettingsPageObject extends FtrService { async clickKibanaIndexPatterns() { this.log.debug('clickKibanaIndexPatterns link'); - await this.testSubjects.click('indexPatterns'); + const currentUrl = await this.browser.getCurrentUrl(); + if (!currentUrl.endsWith('indexPatterns')) { + await this.testSubjects.click('indexPatterns'); + } await this.header.waitUntilLoadingHasFinished(); } @@ -122,38 +125,28 @@ export class SettingsPageObject extends FtrService { } async getIndexPatternField() { - return await this.testSubjects.find('createIndexPatternNameInput'); - } - - async clickTimeFieldNameField() { - return await this.testSubjects.click('createIndexPatternTimeFieldSelect'); + return this.testSubjects.find('createIndexPatternNameInput'); } async getTimeFieldNameField() { - return await this.testSubjects.find('createIndexPatternTimeFieldSelect'); + const wrapperElement = await this.testSubjects.find('timestampField'); + return wrapperElement.findByTestSubject('comboBoxSearchInput'); } async selectTimeFieldOption(selection: string) { // open dropdown - await this.clickTimeFieldNameField(); - // close dropdown, keep focus - await this.clickTimeFieldNameField(); - await this.header.waitUntilLoadingHasFinished(); - return await this.retry.try(async () => { - this.log.debug(`selectTimeFieldOption(${selection})`); - const timeFieldOption = await this.getTimeFieldOption(selection); - await timeFieldOption.click(); - const selected = await timeFieldOption.isSelected(); - if (!selected) throw new Error('option not selected: ' + selected); - }); + const timefield = await this.getTimeFieldNameField(); + await timefield.click(); + await this.browser.pressKeys(selection); + await this.browser.pressKeys(this.browser.keys.TAB); } async getTimeFieldOption(selection: string) { return await this.find.displayedByCssSelector('option[value="' + selection + '"]'); } - async getCreateIndexPatternButton() { - return await this.testSubjects.find('createIndexPatternButton'); + async getSaveIndexPatternButton() { + return await this.testSubjects.find('saveIndexPatternButton'); } async getCreateButton() { @@ -350,25 +343,19 @@ export class SettingsPageObject extends FtrService { await this.header.waitUntilLoadingHasFinished(); await this.clickAddNewIndexPatternButton(); + await this.header.waitUntilLoadingHasFinished(); if (!isStandardIndexPattern) { - await this.clickCreateNewRollupButton(); + await this.selectRollupIndexPatternType(); } - await this.header.waitUntilLoadingHasFinished(); await this.retry.try(async () => { await this.setIndexPatternField(indexPatternName); }); - const btn = await this.getCreateIndexPatternGoToStep2Button(); - await this.retry.waitFor(`index pattern Go To Step 2 button to be enabled`, async () => { - return await btn.isEnabled(); - }); - await btn.click(); - await this.common.sleep(2000); if (timefield) { await this.selectTimeFieldOption(timefield); } - await (await this.getCreateIndexPatternButton()).click(); + await (await this.getSaveIndexPatternButton()).click(); }); await this.header.waitUntilLoadingHasFinished(); await this.retry.try(async () => { @@ -381,16 +368,38 @@ export class SettingsPageObject extends FtrService { } }); + if (!isStandardIndexPattern) { + const badges = await this.find.allByCssSelector('.euiBadge__text'); + const text = await badges[1].getVisibleText(); + expect(text).to.equal('Rollup'); + } + return await this.getIndexPatternIdFromUrl(); } async clickAddNewIndexPatternButton() { await this.common.scrollKibanaBodyTop(); - await this.testSubjects.click('createIndexPatternButton'); + + // if flyout is open + const flyoutView = await this.testSubjects.exists('createIndexPatternButtonFlyout'); + if (flyoutView) { + await this.testSubjects.click('createIndexPatternButtonFlyout'); + return; + } + + const tableView = await this.testSubjects.exists('createIndexPatternButton'); + if (tableView) { + await this.testSubjects.click('createIndexPatternButton'); + } + const flyoutView2 = await this.testSubjects.exists('createIndexPatternButtonFlyout'); + if (flyoutView2) { + await this.testSubjects.click('createIndexPatternButtonFlyout'); + } } - async clickCreateNewRollupButton() { - await this.testSubjects.click('createRollupIndexPatternButton'); + async selectRollupIndexPatternType() { + await this.testSubjects.click('typeField'); + await this.testSubjects.click('rollupType'); } async getIndexPatternIdFromUrl() { diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index 617cd42c06546..966a9d29b3264 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, }); diff --git a/tsconfig.json b/tsconfig.json index ab5a02702e611..3d6c29875c902 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -71,6 +71,7 @@ { "path": "./src/plugins/visualize/tsconfig.json" }, { "path": "./src/plugins/index_pattern_management/tsconfig.json" }, { "path": "./src/plugins/index_pattern_field_editor/tsconfig.json" }, + { "path": "./src/plugins/index_pattern_editor/tsconfig.json" }, { "path": "./x-pack/plugins/actions/tsconfig.json" }, { "path": "./x-pack/plugins/alerting/tsconfig.json" }, { "path": "./x-pack/plugins/apm/tsconfig.json" }, diff --git a/tsconfig.refs.json b/tsconfig.refs.json index 1def88087ed86..1807a7014e389 100644 --- a/tsconfig.refs.json +++ b/tsconfig.refs.json @@ -57,6 +57,7 @@ { "path": "./src/plugins/visualizations/tsconfig.json" }, { "path": "./src/plugins/visualize/tsconfig.json" }, { "path": "./src/plugins/index_pattern_management/tsconfig.json" }, + { "path": "./src/plugins/index_pattern_editor/tsconfig.json" }, { "path": "./test/tsconfig.json" }, { "path": "./x-pack/plugins/actions/tsconfig.json" }, { "path": "./x-pack/plugins/alerting/tsconfig.json" }, diff --git a/typings/index.d.ts b/typings/index.d.ts index 2a5c5e3fa430f..85c001b26031b 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/common/connections.ts b/x-pack/plugins/apm/common/connections.ts index aa3863374337c..1fde50ce5489e 100644 --- a/x-pack/plugins/apm/common/connections.ts +++ b/x-pack/plugins/apm/common/connections.ts @@ -47,6 +47,10 @@ export interface ConnectionStatsItem { value: number | null; timeseries: Coordinate[]; }; + totalTime: { + value: number | null; + timeseries: Coordinate[]; + }; }; } diff --git a/x-pack/plugins/apm/common/service_map.ts b/x-pack/plugins/apm/common/service_map.ts index 303f6b02c0ea2..b8e6922414ebf 100644 --- a/x-pack/plugins/apm/common/service_map.ts +++ b/x-pack/plugins/apm/common/service_map.ts @@ -58,9 +58,9 @@ export interface Connection { destination: ConnectionNode; } -export interface ServiceNodeStats { - avgMemoryUsage: number | null; - avgCpuUsage: number | null; +export interface NodeStats { + avgMemoryUsage?: number | null; + avgCpuUsage?: number | null; transactionStats: { avgTransactionDuration: number | null; avgRequestsPerMinute: number | null; diff --git a/x-pack/plugins/apm/common/utils/formatters/datetime.ts b/x-pack/plugins/apm/common/utils/formatters/datetime.ts index 88f70753f47c8..df9cb38b5d009 100644 --- a/x-pack/plugins/apm/common/utils/formatters/datetime.ts +++ b/x-pack/plugins/apm/common/utils/formatters/datetime.ts @@ -108,7 +108,10 @@ function getFormatsAccordingToDateDifference( } export function asAbsoluteDateTime( - time: number, + /** + * timestamp in milliseconds or ISO timestamp + */ + time: number | string, timeUnit: TimeUnit = 'milliseconds' ) { const momentTime = moment(time); diff --git a/x-pack/plugins/apm/e2e/cypress/integration/apm.feature b/x-pack/plugins/apm/e2e/cypress/integration/apm.feature deleted file mode 100644 index 0cc8f00d48dfd..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/integration/apm.feature +++ /dev/null @@ -1,6 +0,0 @@ -Feature: APM - - Scenario: Transaction latency charts - Given a user browses the APM UI application - When the user inspects the opbeans-node service - Then should redirect to correct path diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/apm.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/apm.ts deleted file mode 100644 index d41f4cf508396..0000000000000 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/apm.ts +++ /dev/null @@ -1,31 +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 { Given, When, Then } from 'cypress-cucumber-preprocessor/steps'; -import { loginAndWaitForPage } from '../../integration/helpers'; - -/** The default time in ms to wait for a Cypress command to complete */ -export const DEFAULT_TIMEOUT = 60 * 1000; - -Given(`a user browses the APM UI application`, () => { - // Open service inventory page - loginAndWaitForPage(`/app/apm/services`, { - from: '2020-06-01T14:59:32.686Z', - to: '2020-06-16T16:59:36.219Z', - }); -}); - -When(`the user inspects the opbeans-node service`, () => { - // click opbeans-node service - cy.get(':contains(opbeans-node)', { timeout: DEFAULT_TIMEOUT }) - .last() - .click({ force: true }); -}); - -Then(`should redirect to correct path`, () => { - cy.url().should('contain', `/app/apm/services/opbeans-node/overview`); -}); diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/List/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/List/index.tsx index 51a025df88d8e..140584a625b90 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/List/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/List/index.tsx @@ -127,10 +127,10 @@ export function AgentConfigurationList({ width: theme.eui.euiSizeXL, name: '', sortable: true, - render: (isApplied: boolean) => ( + render: (_, { applied_by_agent: appliedByAgent }) => ( ), @@ -177,7 +177,7 @@ export function AgentConfigurationList({ { defaultMessage: 'Service environment' } ), sortable: true, - render: (environment: string) => getOptionLabel(environment), + render: (_, { service }) => getOptionLabel(service.environment), }, { align: 'right', @@ -187,8 +187,8 @@ export function AgentConfigurationList({ { defaultMessage: 'Last updated' } ), sortable: true, - render: (value: number) => ( - + render: (_, item) => ( + ), }, ...(canSave diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx index d520036d5f387..81a0da9792830 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx @@ -43,7 +43,7 @@ const columns: Array> = [ 'xpack.apm.settings.anomalyDetection.jobList.actionColumnLabel', { defaultMessage: 'Action' } ), - render: (jobId: string) => ( + render: (_, { job_id: jobId }) => ( {i18n.translate( 'xpack.apm.settings.anomalyDetection.jobList.mlJobLinkText', diff --git a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/custom_link_table.tsx b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/custom_link_table.tsx index f49f27d94a085..4a242bb661e3a 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/custom_link_table.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/custom_link_table.tsx @@ -18,7 +18,7 @@ import React, { useState } from 'react'; import { CustomLink } from '../../../../../../common/custom_link/custom_link_types'; import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; import { LoadingStatePrompt } from '../../../../shared/LoadingStatePrompt'; -import { ManagedTable } from '../../../../shared/managed_table'; +import { ITableColumn, ManagedTable } from '../../../../shared/managed_table'; import { TimestampTooltip } from '../../../../shared/TimestampTooltip'; interface Props { @@ -31,7 +31,7 @@ export function CustomLinkTable({ items = [], onCustomLinkSelected }: Props) { const { core } = useApmPluginContext(); const canSave = core.application.capabilities.apm.save; - const columns = [ + const columns: Array> = [ { field: 'label', name: i18n.translate( diff --git a/x-pack/plugins/apm/public/components/app/backend_inventory/backend_inventory_dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/app/backend_inventory/backend_inventory_dependencies_table/index.tsx index e4b3d48992efa..c89526b332084 100644 --- a/x-pack/plugins/apm/public/components/app/backend_inventory/backend_inventory_dependencies_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/backend_inventory/backend_inventory_dependencies_table/index.tsx @@ -51,11 +51,11 @@ export function BackendInventoryDependenciesTable() { return callApmApi({ endpoint: 'GET /api/apm/backends/top_backends', params: { - query: { start, end, environment, numBuckets: 20, offset }, + query: { start, end, environment, numBuckets: 20, offset, kuery }, }, }); }, - [start, end, environment, offset] + [start, end, environment, offset, kuery] ); const dependencies = diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/List/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/List/index.tsx index 6e181535cc05c..0575146a6bf5a 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/List/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/List/index.tsx @@ -17,7 +17,7 @@ import { truncate, unit } from '../../../../utils/style'; import { ErrorDetailLink } from '../../../shared/Links/apm/ErrorDetailLink'; import { ErrorOverviewLink } from '../../../shared/Links/apm/ErrorOverviewLink'; import { APMQueryParams } from '../../../shared/Links/url_helpers'; -import { ManagedTable } from '../../../shared/managed_table'; +import { ITableColumn, ManagedTable } from '../../../shared/managed_table'; import { TimestampTooltip } from '../../../shared/TimestampTooltip'; const GroupIdLink = euiStyled(ErrorDetailLink)` @@ -52,8 +52,8 @@ interface Props { function ErrorGroupList({ items, serviceName }: Props) { const { urlParams } = useUrlParams(); - const columns = useMemo( - () => [ + const columns = useMemo(() => { + return [ { name: ( <> @@ -80,7 +80,7 @@ function ErrorGroupList({ items, serviceName }: Props) { field: 'groupId', sortable: false, width: `${unit * 6}px`, - render: (groupId: string) => { + render: (_, { groupId }) => { return ( {groupId.slice(0, 5) || NOT_AVAILABLE_LABEL} @@ -94,7 +94,7 @@ function ErrorGroupList({ items, serviceName }: Props) { }), field: 'type', sortable: false, - render: (type: string) => { + render: (_, { type }) => { return ( { + render: (_, item: ErrorGroupItem) => { return ( - {message || NOT_AVAILABLE_LABEL} + {item.message || NOT_AVAILABLE_LABEL}
    @@ -151,8 +151,8 @@ function ErrorGroupList({ items, serviceName }: Props) { field: 'handled', sortable: false, align: 'right', - render: (isUnhandled: boolean) => - isUnhandled === false && ( + render: (_, { handled }) => + handled === false && ( {i18n.translate('xpack.apm.errorsTable.unhandledLabel', { defaultMessage: 'Unhandled', @@ -167,8 +167,10 @@ function ErrorGroupList({ items, serviceName }: Props) { field: 'occurrenceCount', sortable: true, dataType: 'number', - render: (value?: number) => - value ? numeral(value).format('0.[0]a') : NOT_AVAILABLE_LABEL, + render: (_, { occurrenceCount }) => + occurrenceCount + ? numeral(occurrenceCount).format('0.[0]a') + : NOT_AVAILABLE_LABEL, }, { field: 'latestOccurrenceAt', @@ -180,16 +182,15 @@ function ErrorGroupList({ items, serviceName }: Props) { } ), align: 'right', - render: (value?: number) => - value ? ( - + render: (_, { latestOccurrenceAt }) => + latestOccurrenceAt ? ( + ) : ( NOT_AVAILABLE_LABEL ), }, - ], - [serviceName, urlParams] - ); + ] as Array>; + }, [serviceName, urlParams]); return ( ; + return ( + <> + + + +

    + {i18n.translate( + 'xpack.apm.serviceDependencies.breakdownChartTitle', + { + defaultMessage: 'Time spent by dependency', + } + )} +

    +
    + +
    +
    + + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_dependencies/service_dependencies_breakdown_chart.tsx b/x-pack/plugins/apm/public/components/app/service_dependencies/service_dependencies_breakdown_chart.tsx new file mode 100644 index 0000000000000..a33b0db7c4baf --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_dependencies/service_dependencies_breakdown_chart.tsx @@ -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 React from 'react'; +import { getVizColorForIndex } from '../../../../common/viz_colors'; +import { Coordinate, TimeSeries } from '../../../../typings/timeseries'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { useApmParams } from '../../../hooks/use_apm_params'; +import { useFetcher } from '../../../hooks/use_fetcher'; +import { useTimeRange } from '../../../hooks/use_time_range'; +import { BreakdownChart } from '../../shared/charts/breakdown_chart'; + +export function ServiceDependenciesBreakdownChart({ + height, +}: { + height: number; +}) { + const { start, end } = useTimeRange(); + const { serviceName } = useApmServiceContext(); + + const { + query: { kuery, environment }, + } = useApmParams('/services/:serviceName/dependencies'); + + const { data, status } = useFetcher( + (callApmApi) => { + return callApmApi({ + endpoint: 'GET /api/apm/services/{serviceName}/dependencies/breakdown', + params: { + path: { + serviceName, + }, + query: { + start, + end, + kuery, + environment, + }, + }, + }); + }, + [serviceName, start, end, kuery, environment] + ); + + const timeseries: Array> = + data?.breakdown.map((item, index) => { + return { + title: item.title, + data: item.data, + type: 'area', + color: getVizColorForIndex(index), + }; + }) ?? []; + + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/Buttons.test.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/Buttons.test.tsx deleted file mode 100644 index f6376d201f2e1..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/Buttons.test.tsx +++ /dev/null @@ -1,41 +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, { ReactNode } from 'react'; -import { Buttons } from './Buttons'; -import { render } from '@testing-library/react'; -import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; - -function Wrapper({ children }: { children?: ReactNode }) { - return {children}; -} - -describe('Popover Buttons', () => { - it('renders', () => { - expect(() => - render(, { - wrapper: Wrapper, - }) - ).not.toThrowError(); - }); - - it('handles focus click', async () => { - const onFocusClick = jest.fn(); - const result = render( - , - { wrapper: Wrapper } - ); - const focusButton = await result.findByText('Focus map'); - - focusButton.click(); - - expect(onFocusClick).toHaveBeenCalledTimes(1); - }); -}); diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/Buttons.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/Buttons.tsx deleted file mode 100644 index c4ff430e23fe8..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/Buttons.tsx +++ /dev/null @@ -1,60 +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. - */ - -/* eslint-disable @elastic/eui/href-or-on-click */ - -import { EuiButton, EuiFlexItem } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React, { MouseEvent } from 'react'; -import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { getAPMHref } from '../../../shared/Links/apm/APMLink'; -import { APMQueryParams } from '../../../shared/Links/url_helpers'; - -interface ButtonsProps { - onFocusClick?: (event: MouseEvent) => void; - selectedNodeServiceName: string; -} - -export function Buttons({ - onFocusClick = () => {}, - selectedNodeServiceName, -}: ButtonsProps) { - const { core } = useApmPluginContext(); - const { basePath } = core.http; - const urlParams = useUrlParams().urlParams as APMQueryParams; - - const detailsUrl = getAPMHref({ - basePath, - path: `/services/${selectedNodeServiceName}`, - query: urlParams, - }); - const focusUrl = getAPMHref({ - basePath, - path: `/services/${selectedNodeServiceName}/service-map`, - query: urlParams, - }); - - return ( - <> - - - {i18n.translate('xpack.apm.serviceMap.serviceDetailsButtonText', { - defaultMessage: 'Service Details', - })} - - - - - {i18n.translate('xpack.apm.serviceMap.focusMapButtonText', { - defaultMessage: 'Focus map', - })} - - - - ); -} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/Contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/Contents.tsx deleted file mode 100644 index 9030aeed7b13d..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/Contents.tsx +++ /dev/null @@ -1,91 +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 { - EuiFlexGroup, - EuiFlexItem, - EuiHorizontalRule, - EuiTitle, -} from '@elastic/eui'; -import cytoscape from 'cytoscape'; -import React, { MouseEvent } from 'react'; -import { Buttons } from './Buttons'; -import { Info } from './Info'; -import { ServiceStatsFetcher } from './ServiceStatsFetcher'; -import { popoverWidth } from '../cytoscape_options'; - -interface ContentsProps { - isService: boolean; - label: string; - onFocusClick: (event: MouseEvent) => void; - selectedNodeData: cytoscape.NodeDataDefinition; - selectedNodeServiceName: string; -} - -// IE 11 does not handle flex properties as expected. With browser detection, -// we can use regular div elements to render contents that are almost identical. -// -// This method of detecting IE is from a Stack Overflow answer: -// https://stackoverflow.com/a/21825207 -// -// @ts-expect-error `documentMode` is not recognized as a valid property of `document`. -const isIE11 = !!window.MSInputMethodContext && !!document.documentMode; - -function FlexColumnGroup(props: { - children: React.ReactNode; - style: React.CSSProperties; - direction: 'column'; - gutterSize: 's'; -}) { - if (isIE11) { - const { direction, gutterSize, ...rest } = props; - return
    ; - } - return ; -} -function FlexColumnItem(props: { children: React.ReactNode }) { - return isIE11 ?
    : ; -} - -export function Contents({ - selectedNodeData, - isService, - label, - onFocusClick, - selectedNodeServiceName, -}: ContentsProps) { - return ( - - - -

    {label}

    -
    - -
    - - {isService ? ( - - ) : ( - - )} - - {isService && ( - - )} -
    - ); -} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/Info.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/Info.tsx deleted file mode 100644 index 9577a02d68cf2..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/Info.tsx +++ /dev/null @@ -1,116 +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 { - EuiDescriptionList, - EuiDescriptionListTitle, - EuiDescriptionListDescription, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import cytoscape from 'cytoscape'; -import React, { Fragment } from 'react'; -import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; -import { - SPAN_SUBTYPE, - SPAN_TYPE, -} from '../../../../../common/elasticsearch_fieldnames'; -import { ExternalConnectionNode } from '../../../../../common/service_map'; - -const ItemRow = euiStyled.div` - line-height: 2; -`; - -const SubduedDescriptionListTitle = euiStyled(EuiDescriptionListTitle)` - &&& { - color: ${({ theme }) => theme.eui.euiTextSubduedColor}; - } -`; - -const ExternalResourcesList = euiStyled.section` - max-height: 360px; - overflow: auto; -`; - -interface InfoProps extends cytoscape.NodeDataDefinition { - type?: string; - subtype?: string; - className?: string; -} - -export function Info(data: InfoProps) { - // For nodes with span.type "db", convert it to "database". - // Otherwise leave it as-is. - const type = data[SPAN_TYPE] === 'db' ? 'database' : data[SPAN_TYPE]; - - // Externals should not have a subtype so make it undefined if the type is external. - const subtype = data[SPAN_TYPE] !== 'external' && data[SPAN_SUBTYPE]; - - const listItems = [ - { - title: i18n.translate('xpack.apm.serviceMap.typePopoverStat', { - defaultMessage: 'Type', - }), - description: type, - }, - { - title: i18n.translate('xpack.apm.serviceMap.subtypePopoverStat', { - defaultMessage: 'Subtype', - }), - description: subtype, - }, - ]; - - if (data.groupedConnections) { - return ( - - - {data.groupedConnections.map((resource: ExternalConnectionNode) => { - const title = - resource.label || resource['span.destination.service.resource']; - const desc = `${resource['span.type']} (${resource['span.subtype']})`; - return ( - - - {title} - - - {desc} - - - ); - })} - - - ); - } - - return ( - <> - {listItems.map( - ({ title, description }) => - description && ( -
    - - - {title} - - - {description} - - -
    - ) - )} - - ); -} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/Popover.stories.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/Popover.stories.tsx index 324a38ea5db39..faf807d4d4fc3 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/Popover.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/Popover.stories.tsx @@ -5,91 +5,158 @@ * 2.0. */ +import { Meta, Story } from '@storybook/react'; import cytoscape from 'cytoscape'; import { CoreStart } from 'kibana/public'; -import React, { ComponentType } from 'react'; +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { Popover } from '.'; +import { createKibanaReactContext } from '../../../../../../../../src/plugins/kibana_react/public'; import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider'; import { createCallApmApi } from '../../../../services/rest/createCallApmApi'; import { CytoscapeContext } from '../Cytoscape'; -import { Popover } from '.'; import exampleGroupedConnectionsData from '../__stories__/example_grouped_connections.json'; -export default { +interface Args { + nodeData: cytoscape.NodeDataDefinition; +} + +const stories: Meta = { title: 'app/ServiceMap/Popover', component: Popover, decorators: [ - (Story: ComponentType) => { + (StoryComponent) => { const coreMock = ({ http: { - get: async () => ({ - avgCpuUsage: 0.32809666568309237, - avgErrorRate: 0.556068173242986, - avgMemoryUsage: 0.5504868173242986, - transactionStats: { - avgRequestsPerMinute: 164.47222031860858, - avgTransactionDuration: 61634.38905590272, - }, - }), + get: () => { + return { + avgCpuUsage: 0.32809666568309237, + avgErrorRate: 0.556068173242986, + avgMemoryUsage: 0.5504868173242986, + transactionStats: { + avgRequestsPerMinute: 164.47222031860858, + avgTransactionDuration: 61634.38905590272, + }, + }; + }, }, + notifications: { toasts: { add: () => {} } }, + uiSettings: { get: () => ({}) }, } as unknown) as CoreStart; + const KibanaReactContext = createKibanaReactContext(coreMock); + createCallApmApi(coreMock); return ( - - -
    - -
    -
    -
    + + + + +
    + +
    +
    +
    +
    +
    + ); + }, + (StoryComponent, { args }) => { + const node = { + data: args?.nodeData!, + }; + + const cy = cytoscape({ + elements: [ + { data: { id: 'upstreamService' } }, + { + data: { + id: 'edge', + source: 'upstreamService', + target: node.data.id, + }, + }, + node, + ], + }); + + setTimeout(() => { + cy.$id(node.data.id!).select(); + }, 0); + + return ( + + + ); }, ], }; +export default stories; -export function Example() { +export const Backend: Story = () => { return ; -} -Example.decorators = [ - (Story: ComponentType) => { - const node = { - data: { id: 'example service', 'service.name': 'example service' }, - }; - - const cy = cytoscape({ elements: [node] }); - - setTimeout(() => { - cy.$id('example service').select(); - }, 0); +}; +Backend.args = { + nodeData: { + 'span.subtype': 'postgresql', + 'span.destination.service.resource': 'postgresql', + 'span.type': 'db', + id: '>postgresql', + label: 'postgresql', + }, +}; - return ( - - - - ); +export const BackendWithLongTitle: Story = () => { + return ; +}; +BackendWithLongTitle.args = { + nodeData: { + 'span.subtype': 'http', + 'span.destination.service.resource': + '8b37cb7ca2ae49ada54db165f32d3a19.us-central1.gcp.foundit.no:9243', + 'span.type': 'external', + id: '>8b37cb7ca2ae49ada54db165f32d3a19.us-central1.gcp.foundit.no:9243', + label: '8b37cb7ca2ae49ada54db165f32d3a19.us-central1.gcp.foundit.no:9243', }, -]; +}; -export function Externals() { +export const ExternalsList: Story = () => { return ; -} -Externals.decorators = [ - (Story: ComponentType) => { - const node = { - data: exampleGroupedConnectionsData, - }; - const cy = cytoscape({ elements: [node] }); +}; +ExternalsList.args = { + nodeData: exampleGroupedConnectionsData, +}; - setTimeout(() => { - cy.$id(exampleGroupedConnectionsData.id).select(); - }, 0); +export const Resource: Story = () => { + return ; +}; +Resource.args = { + nodeData: { + id: '>cdn.loom.com:443', + label: 'cdn.loom.com:443', + 'span.destination.service.resource': 'cdn.loom.com:443', + 'span.subtype': 'css', + 'span.type': 'resource', + }, +}; - return ( - - - - ); +export const Service: Story = () => { + return ; +}; +Service.args = { + nodeData: { + id: 'example service', + 'service.name': 'example service', + serviceAnomalyStats: { + serviceName: 'opbeans-java', + jobId: 'apm-production-802c-high_mean_transaction_duration', + transactionType: 'request', + actualValue: 16258.496000000017, + anomalyScore: 0, + healthStatus: 'healthy', + }, }, -]; +}; diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/ServiceStatsFetcher.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/ServiceStatsFetcher.tsx deleted file mode 100644 index 3155a65b06aca..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/ServiceStatsFetcher.tsx +++ /dev/null @@ -1,115 +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 from 'react'; -import { - EuiLoadingSpinner, - EuiFlexGroup, - EuiHorizontalRule, - EuiText, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { isNumber } from 'lodash'; -import { ServiceNodeStats } from '../../../../../common/service_map'; -import { ServiceStatsList } from './ServiceStatsList'; -import { useFetcher, FETCH_STATUS } from '../../../../hooks/use_fetcher'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { AnomalyDetection } from './anomaly_detection'; -import { ServiceAnomalyStats } from '../../../../../common/anomaly_detection'; - -interface ServiceStatsFetcherProps { - environment?: string; - serviceName: string; - serviceAnomalyStats: ServiceAnomalyStats | undefined; -} - -export function ServiceStatsFetcher({ - serviceName, - serviceAnomalyStats, -}: ServiceStatsFetcherProps) { - const { - urlParams: { environment, start, end }, - } = useUrlParams(); - - const { - data = { transactionStats: {} } as ServiceNodeStats, - status, - } = useFetcher( - (callApmApi) => { - if (serviceName && start && end) { - return callApmApi({ - endpoint: 'GET /api/apm/service-map/service/{serviceName}', - params: { - path: { serviceName }, - query: { environment, start, end }, - }, - }); - } - }, - [environment, serviceName, start, end], - { - preservePreviousData: false, - } - ); - - const isLoading = status === FETCH_STATUS.LOADING; - - if (isLoading) { - return ; - } - - const { - avgCpuUsage, - avgErrorRate, - avgMemoryUsage, - transactionStats: { avgRequestsPerMinute, avgTransactionDuration }, - } = data; - - const hasServiceData = [ - avgCpuUsage, - avgErrorRate, - avgMemoryUsage, - avgRequestsPerMinute, - avgTransactionDuration, - ].some((stat) => isNumber(stat)); - - if (!hasServiceData) { - return ( - - {i18n.translate('xpack.apm.serviceMap.popoverMetrics.noDataText', { - defaultMessage: `No data for selected environment. Try switching to another environment.`, - })} - - ); - } - return ( - <> - {serviceAnomalyStats && ( - <> - - - - )} - - - ); -} - -function LoadingSpinner() { - return ( - - - - ); -} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/ServiceStatsList.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/ServiceStatsList.tsx deleted file mode 100644 index 766debc6d5587..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/ServiceStatsList.tsx +++ /dev/null @@ -1,96 +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 { i18n } from '@kbn/i18n'; -import { isNumber } from 'lodash'; -import React from 'react'; -import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; -import { - asDuration, - asPercent, - asTransactionRate, -} from '../../../../../common/utils/formatters'; -import { ServiceNodeStats } from '../../../../../common/service_map'; - -export const ItemRow = euiStyled('tr')` - line-height: 2; -`; - -export const ItemTitle = euiStyled('td')` - color: ${({ theme }) => theme.eui.euiTextSubduedColor}; - padding-right: 1rem; -`; - -export const ItemDescription = euiStyled('td')` - text-align: right; -`; - -type ServiceStatsListProps = ServiceNodeStats; - -export function ServiceStatsList({ - transactionStats, - avgErrorRate, - avgCpuUsage, - avgMemoryUsage, -}: ServiceStatsListProps) { - const listItems = [ - { - title: i18n.translate( - 'xpack.apm.serviceMap.avgTransDurationPopoverStat', - { - defaultMessage: 'Latency (avg.)', - } - ), - description: isNumber(transactionStats.avgTransactionDuration) - ? asDuration(transactionStats.avgTransactionDuration) - : null, - }, - { - title: i18n.translate( - 'xpack.apm.serviceMap.avgReqPerMinutePopoverMetric', - { - defaultMessage: 'Throughput (avg.)', - } - ), - description: asTransactionRate(transactionStats.avgRequestsPerMinute), - }, - { - title: i18n.translate('xpack.apm.serviceMap.errorRatePopoverStat', { - defaultMessage: 'Trans. error rate (avg.)', - }), - description: asPercent(avgErrorRate, 1, ''), - }, - { - title: i18n.translate('xpack.apm.serviceMap.avgCpuUsagePopoverStat', { - defaultMessage: 'CPU usage (avg.)', - }), - description: asPercent(avgCpuUsage, 1, ''), - }, - { - title: i18n.translate('xpack.apm.serviceMap.avgMemoryUsagePopoverStat', { - defaultMessage: 'Memory usage (avg.)', - }), - description: asPercent(avgMemoryUsage, 1, ''), - }, - ]; - - return ( - - - {listItems.map( - ({ title, description }) => - description && ( - - {title} - {description} - - ) - )} - -
    - ); -} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx new file mode 100644 index 0000000000000..e0fef269f3faf --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx @@ -0,0 +1,75 @@ +/* + * 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 { EuiButton, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { TypeOf } from '@kbn/typed-react-router-config'; +import React from 'react'; +import { ContentsProps } from '.'; +import { NodeStats } from '../../../../../common/service_map'; +import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; +import { useApmParams } from '../../../../hooks/use_apm_params'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; +import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; +import { ApmRoutes } from '../../../routing/apm_route_config'; +import { StatsList } from './stats_list'; + +export function BackendContents({ nodeData }: ContentsProps) { + const { query } = useApmParams('/*'); + const apmRouter = useApmRouter(); + const { + urlParams: { environment, start, end }, + } = useUrlParams(); + + const backendName = nodeData.label; + + const { data = { transactionStats: {} } as NodeStats, status } = useFetcher( + (callApmApi) => { + if (backendName && start && end) { + return callApmApi({ + endpoint: 'GET /api/apm/service-map/backend/{backendName}', + params: { + path: { backendName }, + query: { + environment, + start, + end, + }, + }, + }); + } + }, + [environment, backendName, start, end], + { + preservePreviousData: false, + } + ); + + const isLoading = status === FETCH_STATUS.LOADING; + const detailsUrl = apmRouter.link('/backends/:backendName/overview', { + path: { backendName }, + query: query as TypeOf< + ApmRoutes, + '/backends/:backendName/overview' + >['query'], + }); + + return ( + <> + + + + + + {i18n.translate('xpack.apm.serviceMap.backendDetailsButtonText', { + defaultMessage: 'Backend Details', + })} + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/externals_list_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/externals_list_contents.tsx new file mode 100644 index 0000000000000..c8ab7a653b201 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/externals_list_contents.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiDescriptionList, + EuiDescriptionListDescription, + EuiDescriptionListTitle, + EuiFlexItem, +} from '@elastic/eui'; +import React, { Fragment } from 'react'; +import { ContentsProps } from '.'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { + SPAN_DESTINATION_SERVICE_RESOURCE, + SPAN_TYPE, + SPAN_SUBTYPE, +} from '../../../../../common/elasticsearch_fieldnames'; +import { ExternalConnectionNode } from '../../../../../common/service_map'; + +const ExternalResourcesList = euiStyled.section` + max-height: 360px; + overflow: auto; +`; + +export function ExternalsListContents({ nodeData }: ContentsProps) { + return ( + + + + {nodeData.groupedConnections.map( + (resource: ExternalConnectionNode) => { + const title = + resource.label || resource[SPAN_DESTINATION_SERVICE_RESOURCE]; + const desc = `${resource[SPAN_TYPE]} (${resource[SPAN_SUBTYPE]})`; + return ( + + + {title} + + + {desc} + + + ); + } + )} + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/index.tsx index 4999459b30dcf..df923730a2227 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/index.tsx @@ -5,6 +5,14 @@ * 2.0. */ +import { + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiPopover, + EuiTitle, +} from '@elastic/eui'; +import cytoscape from 'cytoscape'; import React, { CSSProperties, MouseEvent, @@ -14,13 +22,39 @@ import React, { useRef, useState, } from 'react'; -import { EuiPopover } from '@elastic/eui'; -import cytoscape from 'cytoscape'; +import { + SERVICE_NAME, + SPAN_TYPE, +} from '../../../../../common/elasticsearch_fieldnames'; import { useTheme } from '../../../../hooks/use_theme'; -import { SERVICE_NAME } from '../../../../../common/elasticsearch_fieldnames'; import { CytoscapeContext } from '../Cytoscape'; -import { getAnimationOptions } from '../cytoscape_options'; -import { Contents } from './Contents'; +import { getAnimationOptions, popoverWidth } from '../cytoscape_options'; +import { BackendContents } from './backend_contents'; +import { ExternalsListContents } from './externals_list_contents'; +import { ResourceContents } from './resource_contents'; +import { ServiceContents } from './service_contents'; + +function getContentsComponent(selectedNodeData: cytoscape.NodeDataDefinition) { + if ( + selectedNodeData.groupedConnections && + Array.isArray(selectedNodeData.groupedConnections) + ) { + return ExternalsListContents; + } + if (selectedNodeData[SERVICE_NAME]) { + return ServiceContents; + } + if (selectedNodeData[SPAN_TYPE] === 'resource') { + return ResourceContents; + } + + return BackendContents; +} + +export interface ContentsProps { + nodeData: cytoscape.NodeDataDefinition; + onFocusClick: (event: MouseEvent) => void; +} interface PopoverProps { focusedServiceName?: string; @@ -42,7 +76,6 @@ export function Popover({ focusedServiceName }: PopoverProps) { const renderedWidth = selectedNode?.renderedWidth() ?? 0; const { x, y } = selectedNode?.renderedPosition() ?? { x: -10000, y: -10000 }; const isOpen = !!selectedNode; - const isService = selectedNode?.data(SERVICE_NAME) !== undefined; const triggerStyle: CSSProperties = { background: 'transparent', height: renderedHeight, @@ -58,9 +91,8 @@ export function Popover({ focusedServiceName }: PopoverProps) { transform: `translate(${x}px, ${translateY}px)`, }; const selectedNodeData = selectedNode?.data() ?? {}; - const selectedNodeServiceName = selectedNodeData.id; - const label = selectedNodeData.label || selectedNodeServiceName; const popoverRef = useRef(null); + const selectedNodeId = selectedNodeData.id; // Set up Cytoscape event handlers useEffect(() => { @@ -99,19 +131,21 @@ export function Popover({ focusedServiceName }: PopoverProps) { if (cy) { cy.animate({ ...getAnimationOptions(theme), - center: { eles: cy.getElementById(selectedNodeServiceName) }, + center: { eles: cy.getElementById(selectedNodeId) }, }); } }, - [cy, selectedNodeServiceName, theme] + [cy, selectedNodeId, theme] ); - const isAlreadyFocused = focusedServiceName === selectedNodeServiceName; + const isAlreadyFocused = focusedServiceName === selectedNodeId; const onFocusClick = isAlreadyFocused ? centerSelectedNode : (_event: MouseEvent) => deselect(); + const ContentsComponent = getContentsComponent(selectedNodeData); + return ( - + + + +

    + {selectedNodeData.label ?? selectedNodeId} +

    +
    + +
    + +
    ); } diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/popover.test.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/popover.test.tsx new file mode 100644 index 0000000000000..9678258c4740c --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/popover.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { composeStories } from '@storybook/testing-react'; +import { render, screen, waitFor } from '@testing-library/react'; +import React from 'react'; +import * as stories from './Popover.stories'; + +const { Backend, ExternalsList, Resource, Service } = composeStories(stories); + +describe('Popover', () => { + describe('with backend data', () => { + it('renders a backend link', async () => { + render(); + + await waitFor(() => { + expect( + screen.getByRole('link', { name: /backend details/i }) + ).toBeInTheDocument(); + }); + }); + }); + + describe('with externals list data', () => { + it('renders an externals list', async () => { + render(); + + await waitFor(() => { + expect( + screen.getByText(/813-mam-392.mktoresp.com:443/) + ).toBeInTheDocument(); + }); + }); + }); + + describe('with resource data', () => { + it('renders with no buttons', async () => { + render(); + + await waitFor(() => { + expect(screen.queryByRole('link')).not.toBeInTheDocument(); + }); + }); + }); + + describe('with service data', () => { + it('renders contents for a service', async () => { + render(); + + await waitFor(() => { + expect( + screen.getByRole('link', { name: /service details/i }) + ).toBeInTheDocument(); + }); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/resource_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/resource_contents.tsx new file mode 100644 index 0000000000000..e0cfb8f16f61d --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/resource_contents.tsx @@ -0,0 +1,69 @@ +/* + * 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 { + EuiDescriptionListDescription, + EuiDescriptionListTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import type { ContentsProps } from '.'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { + SPAN_SUBTYPE, + SPAN_TYPE, +} from '../../../../../common/elasticsearch_fieldnames'; + +const ItemRow = euiStyled.div` + line-height: 2; +`; + +const SubduedDescriptionListTitle = euiStyled(EuiDescriptionListTitle)` + &&& { + color: ${({ theme }) => theme.eui.euiTextSubduedColor}; + } +`; + +export function ResourceContents({ nodeData }: ContentsProps) { + const subtype = nodeData[SPAN_SUBTYPE]; + const type = nodeData[SPAN_TYPE]; + + const listItems = [ + { + title: i18n.translate('xpack.apm.serviceMap.typePopoverStat', { + defaultMessage: 'Type', + }), + description: type, + }, + { + title: i18n.translate('xpack.apm.serviceMap.subtypePopoverStat', { + defaultMessage: 'Subtype', + }), + description: subtype, + }, + ]; + + return ( + <> + {listItems.map( + ({ title, description }) => + description && ( +
    + + + {title} + + + {description} + + +
    + ) + )} + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx new file mode 100644 index 0000000000000..b486e5e19fb03 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx @@ -0,0 +1,90 @@ +/* + * 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. + */ + +/* eslint-disable @elastic/eui/href-or-on-click */ + +import { EuiButton, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import type { ContentsProps } from '.'; +import { NodeStats } from '../../../../../common/service_map'; +import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; +import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; +import { AnomalyDetection } from './anomaly_detection'; +import { StatsList } from './stats_list'; + +export function ServiceContents({ onFocusClick, nodeData }: ContentsProps) { + const apmRouter = useApmRouter(); + + const { + urlParams: { environment, start, end }, + } = useUrlParams(); + + const serviceName = nodeData.id!; + + const { data = { transactionStats: {} } as NodeStats, status } = useFetcher( + (callApmApi) => { + if (serviceName && start && end) { + return callApmApi({ + endpoint: 'GET /api/apm/service-map/service/{serviceName}', + params: { + path: { serviceName }, + query: { environment, start, end }, + }, + }); + } + }, + [environment, serviceName, start, end], + { + preservePreviousData: false, + } + ); + + const isLoading = status === FETCH_STATUS.LOADING; + + const detailsUrl = apmRouter.link('/services/:serviceName', { + path: { serviceName }, + }); + + const focusUrl = apmRouter.link('/services/:serviceName/service-map', { + path: { serviceName }, + }); + + const { serviceAnomalyStats } = nodeData; + + return ( + <> + + {serviceAnomalyStats && ( + <> + + + + )} + + + + + {i18n.translate('xpack.apm.serviceMap.serviceDetailsButtonText', { + defaultMessage: 'Service Details', + })} + + + + + {i18n.translate('xpack.apm.serviceMap.focusMapButtonText', { + defaultMessage: 'Focus map', + })} + + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/service_stats_list.stories.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/service_stats_list.stories.tsx deleted file mode 100644 index f1a89043f826e..0000000000000 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/service_stats_list.stories.tsx +++ /dev/null @@ -1,56 +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 from 'react'; -import { ServiceStatsList } from './ServiceStatsList'; - -export default { - title: 'app/ServiceMap/Popover/ServiceStatsList', - component: ServiceStatsList, -}; - -export function Example() { - return ( - - ); -} - -export function SomeNullValues() { - return ( - - ); -} - -export function AllNullValues() { - return ( - - ); -} diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/stats_list.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/stats_list.tsx new file mode 100644 index 0000000000000..88915b9bc9f34 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/stats_list.tsx @@ -0,0 +1,139 @@ +/* + * 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 { EuiFlexGroup, EuiLoadingSpinner, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { isNumber } from 'lodash'; +import React from 'react'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { NodeStats } from '../../../../../common/service_map'; +import { + asDuration, + asPercent, + asTransactionRate, +} from '../../../../../common/utils/formatters'; + +export const ItemRow = euiStyled.tr` + line-height: 2; +`; + +export const ItemTitle = euiStyled.td` + color: ${({ theme }) => theme.eui.euiTextSubduedColor}; + padding-right: 1rem; +`; + +export const ItemDescription = euiStyled.td` + text-align: right; +`; + +function LoadingSpinner() { + return ( + + + + ); +} + +function NoDataMessage() { + return ( + + {i18n.translate('xpack.apm.serviceMap.popover.noDataText', { + defaultMessage: `No data for selected environment. Try switching to another environment.`, + })} + + ); +} + +interface StatsListProps { + isLoading: boolean; + data: NodeStats; +} + +export function StatsList({ data, isLoading }: StatsListProps) { + const { + avgCpuUsage, + avgErrorRate, + avgMemoryUsage, + transactionStats: { avgRequestsPerMinute, avgTransactionDuration }, + } = data; + + const hasData = [ + avgCpuUsage, + avgErrorRate, + avgMemoryUsage, + avgRequestsPerMinute, + avgTransactionDuration, + ].some((stat) => isNumber(stat)); + + if (isLoading) { + return ; + } + + if (!hasData) { + return ; + } + + const items = [ + { + title: i18n.translate( + 'xpack.apm.serviceMap.avgTransDurationPopoverStat', + { + defaultMessage: 'Latency (avg.)', + } + ), + description: isNumber(avgTransactionDuration) + ? asDuration(avgTransactionDuration) + : null, + }, + { + title: i18n.translate( + 'xpack.apm.serviceMap.avgReqPerMinutePopoverMetric', + { + defaultMessage: 'Throughput (avg.)', + } + ), + description: asTransactionRate(avgRequestsPerMinute), + }, + { + title: i18n.translate('xpack.apm.serviceMap.errorRatePopoverStat', { + defaultMessage: 'Trans. error rate (avg.)', + }), + description: asPercent(avgErrorRate, 1, ''), + }, + { + title: i18n.translate('xpack.apm.serviceMap.avgCpuUsagePopoverStat', { + defaultMessage: 'CPU usage (avg.)', + }), + description: asPercent(avgCpuUsage, 1, ''), + }, + { + title: i18n.translate('xpack.apm.serviceMap.avgMemoryUsagePopoverStat', { + defaultMessage: 'Memory usage (avg.)', + }), + description: asPercent(avgMemoryUsage, 1, ''), + }, + ]; + + return ( + + + {items.map(({ title, description }) => { + return description ? ( + + {title} + {description} + + ) : null; + })} + +
    + ); +} diff --git a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx index 6431a8ad6d01c..4346b391b3666 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx @@ -79,7 +79,7 @@ function ServiceNodeOverview() { ), field: 'name', sortable: true, - render: (name: string) => { + render: (_, { name }) => { const { displayedName, tooltip } = name === SERVICE_NODE_NAME_MISSING ? { @@ -112,7 +112,7 @@ function ServiceNodeOverview() { }), field: 'cpu', sortable: true, - render: (value: number | null) => asPercent(value, 1), + render: (_, { cpu }) => asPercent(cpu, 1), }, { name: i18n.translate('xpack.apm.jvmsTable.heapMemoryColumnLabel', { diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx index 08d554b9a54e8..d9df9acf9ff65 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx @@ -34,7 +34,7 @@ export function ServiceOverviewDependenciesTable() { const { query, query: { kuery, rangeFrom, rangeTo }, - } = useApmParams('/services/:serviceName/overview'); + } = useApmParams('/services/:serviceName/*'); const { offset } = getTimeRangeComparison({ start, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx index b458f6147b3f1..cd2c70b0f10b7 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx @@ -51,14 +51,14 @@ export function getColumns({ }, }, { - field: 'last_seen', + field: 'lastSeen', name: i18n.translate( 'xpack.apm.serviceOverview.errorsTableColumnLastSeen', { defaultMessage: 'Last seen', } ), - render: (_, { last_seen: lastSeen }) => { + render: (_, { lastSeen }) => { return ; }, width: `${unit * 9}px`, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx index 5c3384df567fe..c8604be50ee15 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx @@ -32,7 +32,7 @@ type ErrorGroupMainStatistics = APIReturnType<'GET /api/apm/services/{serviceNam type ErrorGroupDetailedStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/detailed_statistics'>; type SortDirection = 'asc' | 'desc'; -type SortField = 'name' | 'last_seen' | 'occurrences'; +type SortField = 'name' | 'lastSeen' | 'occurrences'; const PAGE_SIZE = 5; const DEFAULT_SORT = { diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx index 719409b0f97ff..a6ef16fe85510 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx @@ -25,14 +25,6 @@ interface ServiceOverviewInstancesChartAndTableProps { serviceName: string; } -export interface MainStatsServiceInstanceItem { - serviceNodeName: string; - errorRate: number; - throughput: number; - latency: number; - cpuUsage: number; - memoryUsage: number; -} type ApiResponseMainStats = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; type ApiResponseDetailedStats = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx index d956612a9ac49..1ff896cff57f7 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx @@ -31,9 +31,10 @@ import { MetricOverviewLink } from '../../../shared/Links/apm/MetricOverviewLink import { ServiceNodeMetricOverviewLink } from '../../../shared/Links/apm/ServiceNodeMetricOverviewLink'; import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; import { getLatencyColumnLabel } from '../../../shared/transactions_table/get_latency_column_label'; -import { MainStatsServiceInstanceItem } from '../service_overview_instances_chart_and_table'; import { InstanceActionsMenu } from './instance_actions_menu'; +type ServiceInstanceMainStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; +type MainStatsServiceInstanceItem = ServiceInstanceMainStatistics['currentPeriod'][0]; type ServiceInstanceDetailedStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; export function getColumns({ diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx index bfd5fea82c6cd..91955dbea47e5 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx @@ -20,7 +20,6 @@ import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper'; import { PAGE_SIZE, - MainStatsServiceInstanceItem, SortDirection, SortField, } from '../service_overview_instances_chart_and_table'; @@ -28,6 +27,8 @@ import { OverviewTableContainer } from '../../../shared/overview_table_container import { getColumns } from './get_columns'; import { InstanceDetails } from './intance_details'; +type ServiceInstanceMainStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; +type MainStatsServiceInstanceItem = ServiceInstanceMainStatistics['currentPeriod'][0]; type ServiceInstanceDetailedStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; export interface TableOptions { diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx index f1c8df553abf7..231c49a9a2197 100644 --- a/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx +++ b/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx @@ -73,7 +73,8 @@ const traceListColumns: Array> = [ }), sortable: true, dataType: 'number', - render: (time: number) => asMillisecondDuration(time), + render: (_, { averageResponseTime }) => + asMillisecondDuration(averageResponseTime), }, { field: 'transactionsPerMinute', @@ -82,7 +83,8 @@ const traceListColumns: Array> = [ }), sortable: true, dataType: 'number', - render: (value: number) => asTransactionRate(value), + render: (_, { transactionsPerMinute }) => + asTransactionRate(transactionsPerMinute), }, { field: 'impact', @@ -112,7 +114,7 @@ const traceListColumns: Array> = [ width: '20%', align: 'left', sortable: true, - render: (value: number) => , + render: (_, { impact }) => , }, ]; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/span_flyout/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/span_flyout/index.tsx index 93acf38a094bc..50fc56dff7f85 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/span_flyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/span_flyout/index.tsx @@ -8,6 +8,7 @@ import { EuiBadge, EuiButtonEmpty, + EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiFlyoutBody, @@ -21,6 +22,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; +import { CompositeSpanDurationSummaryItem } from '../../../../../../shared/Summary/CompositeSpanDurationSummaryItem'; import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common'; import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; @@ -123,7 +125,6 @@ export function SpanFlyout({
    - @@ -137,6 +138,21 @@ export function SpanFlyout({ + {span.span.composite && ( + + + + {i18n.translate( + 'xpack.apm.transactionDetails.spanFlyout.compositeExampleWarning', + { + defaultMessage: + 'This is a sample document for a group of consecutive, similar spans', + } + )} + + + + )} @@ -144,11 +160,19 @@ export function SpanFlyout({ , - , + <> + + {span.span.composite && ( + + )} + , <> {spanHttpUrl && ( diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_helpers/waterfall_helpers.ts index 9ace59fae6320..b6e427e8cc0a1 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_helpers/waterfall_helpers.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_helpers/waterfall_helpers.ts @@ -7,12 +7,13 @@ import { euiPaletteColorBlind } from '@elastic/eui'; import { first, flatten, groupBy, isEmpty, sortBy, sum, uniq } from 'lodash'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TraceAPIResponse } from '../../../../../../../../server/lib/traces/get_trace'; +import { APIReturnType } from '../../../../../../../services/rest/createCallApmApi'; import { APMError } from '../../../../../../../../typings/es_schemas/ui/apm_error'; import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; +type TraceAPIResponse = APIReturnType<'GET /api/apm/traces/{traceId}'>; + interface IWaterfallGroup { [key: string]: IWaterfallSpanOrTransaction[]; } diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx index a2c2c869a079c..cd892ae2a16eb 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx @@ -147,7 +147,15 @@ function HttpStatusCode({ item }: { item: IWaterfallSpanOrTransaction }) { function NameLabel({ item }: { item: IWaterfallSpanOrTransaction }) { switch (item.docType) { case 'span': - return {item.doc.span.name}; + let name = item.doc.span.name; + if (item.doc.span.composite) { + const compositePrefix = + item.doc.span.composite.compression_strategy === 'exact_match' + ? 'x' + : ''; + name = `${item.doc.span.composite.count}${compositePrefix} ${name}`; + } + return {name}; case 'transaction': return ( @@ -182,6 +190,9 @@ export function WaterfallItem({ } ); + const isCompositeSpan = item.docType === 'span' && item.doc.span.composite; + const itemBarStyle = getItemBarStyle(item, color, width, left); + return ( ); } + +function getItemBarStyle( + item: IWaterfallSpanOrTransaction, + color: string, + width: number, + left: number +): React.CSSProperties { + let itemBarStyle = { left: `${left}%`, width: `${width}%` }; + + if (item.docType === 'span' && item.doc.span.composite) { + const percNumItems = 100.0 / item.doc.span.composite.count; + const spanSumRatio = + item.doc.span.composite.sum.us / item.doc.span.duration.us; + const percDuration = percNumItems * spanSumRatio; + + itemBarStyle = { + ...itemBarStyle, + ...{ + backgroundImage: + `repeating-linear-gradient(90deg, ${color},` + + ` ${color} max(${percDuration}%,3px),` + + ` transparent max(${percDuration}%,3px),` + + ` transparent max(${percNumItems}%,max(${percDuration}%,3px) + 3px))`, + }, + }; + } + + return itemBarStyle; +} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx index 20ca3194fbfdf..f8abff2c9609c 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx @@ -7,9 +7,8 @@ import React, { ComponentType } from 'react'; import { MemoryRouter } from 'react-router-dom'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TraceAPIResponse } from '../../../../../../server/lib/traces/get_trace'; import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; +import { APIReturnType } from '../../../../../services/rest/createCallApmApi'; import { WaterfallContainer } from './index'; import { getWaterfall } from './Waterfall/waterfall_helpers/waterfall_helpers'; import { @@ -20,6 +19,8 @@ import { urlParams, } from './waterfallContainer.stories.data'; +type TraceAPIResponse = APIReturnType<'GET /api/apm/traces/{traceId}'>; + export default { title: 'app/TransactionDetails/Waterfall', component: WaterfallContainer, 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 37ec76f2b299e..66595430f618d 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 c5171a012dc63..d92d7a8d94922 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/routing/track_pageview.tsx b/x-pack/plugins/apm/public/components/routing/track_pageview.tsx index 20e02a505bc43..7f4a03cae90be 100644 --- a/x-pack/plugins/apm/public/components/routing/track_pageview.tsx +++ b/x-pack/plugins/apm/public/components/routing/track_pageview.tsx @@ -11,8 +11,8 @@ import { useTrackPageview } from '../../../../observability/public'; export function TrackPageview({ children }: { children: React.ReactElement }) { const routePath = useRoutePath(); - useTrackPageview({ app: 'apm', path: routePath }); - useTrackPageview({ app: 'apm', path: routePath, delay: 15000 }); + useTrackPageview({ app: 'apm', path: routePath }, [routePath]); + useTrackPageview({ app: 'apm', path: routePath, delay: 15000 }, [routePath]); return children; } diff --git a/x-pack/plugins/apm/public/components/shared/Summary/CompositeSpanDurationSummaryItem.tsx b/x-pack/plugins/apm/public/components/shared/Summary/CompositeSpanDurationSummaryItem.tsx new file mode 100644 index 0000000000000..34f9a631e62aa --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/Summary/CompositeSpanDurationSummaryItem.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiToolTip, EuiText } from '@elastic/eui'; +import { asDuration } from '../../../../common/utils/formatters'; + +interface Props { + count: number; + durationSum: number; +} + +function CompositeSpanDurationSummaryItem({ count, durationSum }: Props) { + const avgDuration = durationSum / count; + + return ( + + + {i18n.translate('xpack.apm.compositeSpanCallsLabel', { + defaultMessage: `, {count} calls, on avg. {duration}`, + values: { + count, + duration: asDuration(avgDuration), + }, + })} + + + ); +} + +export { CompositeSpanDurationSummaryItem }; diff --git a/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx b/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx index 17c802ab6a381..c4ba189221c85 100644 --- a/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx @@ -15,9 +15,9 @@ import { interface Props { /** - * timestamp in milliseconds + * timestamp in milliseconds or ISO timestamp */ - time: number; + time: number | string; timeUnit?: TimeUnit; } diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/transaction_breakdown_chart_contents.tsx b/x-pack/plugins/apm/public/components/shared/charts/breakdown_chart/index.tsx similarity index 88% rename from x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/transaction_breakdown_chart_contents.tsx rename to x-pack/plugins/apm/public/components/shared/charts/breakdown_chart/index.tsx index 4fdfbff7917d1..f39c39113fedc 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/transaction_breakdown_chart_contents.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/breakdown_chart/index.tsx @@ -16,19 +16,21 @@ import { Position, ScaleType, Settings, + TickFormatter, } from '@elastic/charts'; import { EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import React from 'react'; import { useHistory } from 'react-router-dom'; +import { Annotation } from '../../../../../common/annotations'; import { useChartTheme } from '../../../../../../observability/public'; import { asAbsoluteDateTime, + asDuration, asPercent, } from '../../../../../common/utils/formatters'; import { Coordinate, TimeSeries } from '../../../../../typings/timeseries'; -import { useAnnotationsContext } from '../../../../context/annotations/use_annotations_context'; import { useChartPointerEventContext } from '../../../../context/chart_pointer_event/use_chart_pointer_event_context'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; @@ -41,17 +43,23 @@ interface Props { fetchStatus: FETCH_STATUS; height?: number; showAnnotations: boolean; + annotations: Annotation[]; timeseries?: Array>; + yAxisType: 'duration' | 'percentage'; } -export function TransactionBreakdownChartContents({ +const asPercentBound = (y: number | null) => asPercent(y, 1); +const asDurationBound = (y: number | null) => asDuration(y); + +export function BreakdownChart({ fetchStatus, height = unit * 16, showAnnotations, + annotations, timeseries, + yAxisType, }: Props) { const history = useHistory(); - const { annotations } = useAnnotationsContext(); const chartTheme = useChartTheme(); const { chartRef, setPointerEvent } = useChartPointerEventContext(); @@ -68,6 +76,9 @@ export function TransactionBreakdownChartContents({ const isEmpty = isTimeseriesEmpty(timeseries); + const yTickFormat: TickFormatter = + yAxisType === 'duration' ? asDurationBound : asPercentBound; + return ( @@ -98,7 +109,7 @@ export function TransactionBreakdownChartContents({ id="y-axis" ticks={3} position={Position.Left} - tickFormat={(y: number) => asPercent(y ?? 0, 1)} + tickFormat={yTickFormat} /> {showAnnotations && ( @@ -133,7 +144,9 @@ export function TransactionBreakdownChartContents({ yAccessors={['y']} data={serie.data} stackAccessors={['x']} - stackMode={'percentage'} + stackMode={ + yAxisType === 'percentage' ? 'percentage' : undefined + } color={serie.areaColor} curve={CurveType.CURVE_MONOTONE_X} /> diff --git a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.stories.tsx b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.stories.tsx index 6128526c577e4..ae3c871e25a1a 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.stories.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.stories.tsx @@ -7,10 +7,13 @@ import { TooltipInfo } from '@elastic/charts'; import React from 'react'; +import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { getDurationFormatter } from '../../../../../common/utils/formatters'; -import { MainStatsServiceInstanceItem } from '../../../app/service_overview/service_overview_instances_chart_and_table'; import { CustomTooltip } from './custom_tooltip'; +type ServiceInstanceMainStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; +type MainStatsServiceInstanceItem = ServiceInstanceMainStatistics['currentPeriod'][0]; + function getLatencyFormatter(props: TooltipInfo) { const maxLatency = Math.max( ...props.values.map((value) => { diff --git a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.tsx b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.tsx index 027f764317e46..a0197c2f50c96 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.tsx @@ -9,13 +9,16 @@ import { TooltipInfo } from '@elastic/charts'; import { EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { getServiceNodeName } from '../../../../../common/service_nodes'; import { asTransactionRate, TimeFormatter, } from '../../../../../common/utils/formatters'; import { useTheme } from '../../../../hooks/use_theme'; -import { MainStatsServiceInstanceItem } from '../../../app/service_overview/service_overview_instances_chart_and_table'; + +type ServiceInstanceMainStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; +type MainStatsServiceInstanceItem = ServiceInstanceMainStatistics['currentPeriod'][0]; const latencyLabel = i18n.translate( 'xpack.apm.instancesLatencyDistributionChartTooltipLatencyLabel', diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/index.tsx index 40c5e39589fb1..0e2b1e185f9d9 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/index.tsx @@ -8,8 +8,9 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { useAnnotationsContext } from '../../../../context/annotations/use_annotations_context'; import { useTransactionBreakdown } from './use_transaction_breakdown'; -import { TransactionBreakdownChartContents } from './transaction_breakdown_chart_contents'; +import { BreakdownChart } from '../breakdown_chart'; export function TransactionBreakdownChart({ height, @@ -19,6 +20,7 @@ export function TransactionBreakdownChart({ showAnnotations?: boolean; }) { const { data, status } = useTransactionBreakdown(); + const { annotations } = useAnnotationsContext(); const { timeseries } = data; return ( @@ -34,11 +36,13 @@ export function TransactionBreakdownChart({ - diff --git a/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx b/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx index 98f8fb4162267..af8b317f5ec33 100644 --- a/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/managed_table/index.tsx @@ -21,6 +21,7 @@ export interface ITableColumn { align?: string; width?: string; sortable?: boolean; + truncateText?: boolean; render?: (value: any, item: T) => unknown; } diff --git a/x-pack/plugins/apm/public/components/shared/span_icon/get_span_icon.ts b/x-pack/plugins/apm/public/components/shared/span_icon/get_span_icon.ts index 93021fe177f6b..2abb7d03bce80 100644 --- a/x-pack/plugins/apm/public/components/shared/span_icon/get_span_icon.ts +++ b/x-pack/plugins/apm/public/components/shared/span_icon/get_span_icon.ts @@ -40,6 +40,7 @@ export const spanTypeIcons: { aws: { servicename: awsIcon, }, + cache: { redis: redisIcon }, db: { cassandra: cassandraIcon, cosmosdb: azureIcon, diff --git a/x-pack/plugins/apm/server/lib/backends/get_throughput_charts_for_backend.ts b/x-pack/plugins/apm/server/lib/backends/get_throughput_charts_for_backend.ts index 4fbd521ea4443..ae8a6db903d8a 100644 --- a/x-pack/plugins/apm/server/lib/backends/get_throughput_charts_for_backend.ts +++ b/x-pack/plugins/apm/server/lib/backends/get_throughput_charts_for_backend.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { SPAN_DESTINATION_SERVICE_RESOURCE } from '../../../common/elasticsearch_fieldnames'; +import { + SPAN_DESTINATION_SERVICE_RESOURCE, + SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT, +} from '../../../common/elasticsearch_fieldnames'; import { environmentQuery } from '../../../common/utils/environment_query'; import { kqlQuery, rangeQuery } from '../../../../observability/server'; import { ProcessorEvent } from '../../../common/processor_event'; @@ -63,7 +66,10 @@ export async function getThroughputChartsForBackend({ }), aggs: { throughput: { - rate: {}, + rate: { + field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT, + unit: 'minute', + }, }, }, }, diff --git a/x-pack/plugins/apm/server/lib/backends/get_top_backends.ts b/x-pack/plugins/apm/server/lib/backends/get_top_backends.ts index 2f9af0bafd37c..9b361774b8a9f 100644 --- a/x-pack/plugins/apm/server/lib/backends/get_top_backends.ts +++ b/x-pack/plugins/apm/server/lib/backends/get_top_backends.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { kqlQuery } from '../../../../observability/server'; +import { NodeType } from '../../../common/connections'; import { environmentQuery } from '../../../common/utils/environment_query'; import { getConnectionStats } from '../connections/get_connection_stats'; import { getConnectionStatsItemsWithRelativeImpact } from '../connections/get_connection_stats/get_connection_stats_items_with_relative_impact'; -import { NodeType } from '../../../common/connections'; import { Setup } from '../helpers/setup_request'; export async function getTopBackends({ @@ -18,6 +19,7 @@ export async function getTopBackends({ numBuckets, environment, offset, + kuery, }: { setup: Setup; start: number; @@ -25,13 +27,14 @@ export async function getTopBackends({ numBuckets: number; environment?: string; offset?: string; + kuery?: string; }) { const statsItems = await getConnectionStats({ setup, start, end, numBuckets, - filter: [...environmentQuery(environment)], + filter: [...environmentQuery(environment), ...kqlQuery(kuery)], offset, collapseBy: 'downstream', }); diff --git a/x-pack/plugins/apm/server/lib/connections/get_connection_stats/get_stats.ts b/x-pack/plugins/apm/server/lib/connections/get_connection_stats/get_stats.ts index 94a23f6218e5d..4f48f3388c017 100644 --- a/x-pack/plugins/apm/server/lib/connections/get_connection_stats/get_stats.ts +++ b/x-pack/plugins/apm/server/lib/connections/get_connection_stats/get_stats.ts @@ -119,6 +119,16 @@ export const getStats = async ({ }, }, }, + total_latency_sum: { + sum: { + field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM, + }, + }, + total_latency_count: { + sum: { + field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT, + }, + }, timeseries: { date_histogram: { field: '@timestamp', @@ -126,6 +136,7 @@ export const getStats = async ({ start: startWithOffset, end: endWithOffset, numBuckets, + minBucketSize: 60, }).intervalString, extended_bounds: { min: startWithOffset, diff --git a/x-pack/plugins/apm/server/lib/connections/get_connection_stats/index.ts b/x-pack/plugins/apm/server/lib/connections/get_connection_stats/index.ts index 37fcd9877327b..03b94defda6dd 100644 --- a/x-pack/plugins/apm/server/lib/connections/get_connection_stats/index.ts +++ b/x-pack/plugins/apm/server/lib/connections/get_connection_stats/index.ts @@ -113,6 +113,13 @@ export function getConnectionStats({ y: point.count > 0 ? point.latency_sum / point.count : null, })), }, + totalTime: { + value: mergedStats.value.latency_sum, + timeseries: mergedStats.timeseries.map((point) => ({ + x: point.x, + y: point.latency_sum, + })), + }, throughput: { value: mergedStats.value.count > 0 diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_backend_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_backend_node_info.ts new file mode 100644 index 0000000000000..17c3191d80ff4 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_backend_node_info.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { rangeQuery } from '../../../../observability/server'; +import { + EVENT_OUTCOME, + SPAN_DESTINATION_SERVICE_RESOURCE, + SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT, + SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM, +} from '../../../common/elasticsearch_fieldnames'; +import { EventOutcome } from '../../../common/event_outcome'; +import { ProcessorEvent } from '../../../common/processor_event'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { withApmSpan } from '../../utils/with_apm_span'; +import { calculateThroughput } from '../helpers/calculate_throughput'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; + +interface Options { + setup: Setup & SetupTimeRange; + environment?: string; + backendName: string; +} + +export function getServiceMapBackendNodeInfo({ + environment, + backendName, + setup, +}: Options) { + return withApmSpan('get_service_map_backend_node_stats', async () => { + const { apmEventClient, start, end } = setup; + + const response = await apmEventClient.search( + 'get_service_map_backend_node_stats', + { + apm: { + events: [ProcessorEvent.metric], + }, + body: { + size: 0, + query: { + bool: { + filter: [ + { term: { [SPAN_DESTINATION_SERVICE_RESOURCE]: backendName } }, + ...rangeQuery(start, end), + ...environmentQuery(environment), + ], + }, + }, + aggs: { + latency_sum: { + sum: { + field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM, + }, + }, + count: { + sum: { + field: SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT, + }, + }, + [EVENT_OUTCOME]: { + terms: { field: EVENT_OUTCOME, include: [EventOutcome.failure] }, + }, + }, + }, + } + ); + + const count = response.aggregations?.count.value ?? 0; + const errorCount = + response.aggregations?.[EVENT_OUTCOME].buckets[0]?.doc_count ?? 0; + const latencySum = response.aggregations?.latency_sum.value ?? 0; + + const avgErrorRate = errorCount / count; + const avgTransactionDuration = latencySum / count; + const avgRequestsPerMinute = calculateThroughput({ + start, + end, + value: count, + }); + + if (count === 0) { + return { + avgErrorRate: null, + transactionStats: { + avgRequestsPerMinute: null, + avgTransactionDuration: null, + }, + }; + } + + return { + avgErrorRate, + transactionStats: { + avgRequestsPerMinute, + avgTransactionDuration, + }, + }; + }); +} diff --git a/x-pack/plugins/apm/server/lib/services/get_service_dependencies_breakdown.ts b/x-pack/plugins/apm/server/lib/services/get_service_dependencies_breakdown.ts new file mode 100644 index 0000000000000..8ebf6b7e017d4 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/services/get_service_dependencies_breakdown.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { sortBy, take } from 'lodash'; +import { getNodeName } from '../../../common/connections'; +import { kqlQuery } from '../../../../observability/server'; +import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; +import { environmentQuery } from '../../../common/utils/environment_query'; +import { Setup } from '../helpers/setup_request'; +import { getConnectionStats } from '../connections/get_connection_stats'; + +export async function getServiceDependenciesBreakdown({ + setup, + start, + end, + serviceName, + environment, + kuery, +}: { + setup: Setup; + start: number; + end: number; + serviceName: string; + environment?: string; + kuery?: string; +}) { + const items = await getConnectionStats({ + setup, + start, + end, + numBuckets: 100, + collapseBy: 'downstream', + filter: [ + ...environmentQuery(environment), + ...kqlQuery(kuery), + { term: { [SERVICE_NAME]: serviceName } }, + ], + }); + + return take( + sortBy(items, (item) => item.stats.totalTime ?? 0).reverse(), + 20 + ).map((item) => { + const { stats, location } = item; + + return { + title: getNodeName(location), + data: stats.totalTime.timeseries, + }; + }); +} diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_main_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_main_statistics.ts index 0e4ed72814cbb..0d50e14fc89b0 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_main_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_main_statistics.ts @@ -82,7 +82,7 @@ export async function getServiceErrorGroupMainStatistics({ response.aggregations?.error_groups.buckets.map((bucket) => ({ group_id: bucket.key as string, name: getErrorName(bucket.sample.hits.hits[0]._source), - last_seen: new Date( + lastSeen: new Date( bucket.sample.hits.hits[0]?._source['@timestamp'] ).getTime(), occurrences: bucket.doc_count, diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts index b89bc86e29172..a58539da722c3 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts @@ -47,7 +47,7 @@ export async function getServiceErrorGroups({ pageIndex: number; numBuckets: number; sortDirection: 'asc' | 'desc'; - sortField: 'name' | 'last_seen' | 'occurrences'; + sortField: 'name' | 'lastSeen' | 'occurrences'; transactionType: string; }) { return withApmSpan('get_service_error_groups', async () => { @@ -108,7 +108,7 @@ export async function getServiceErrorGroups({ response.aggregations?.error_groups.buckets.map((bucket) => ({ group_id: bucket.key as string, name: getErrorName(bucket.sample.hits.hits[0]._source), - last_seen: new Date( + lastSeen: new Date( bucket.sample.hits.hits[0]?._source['@timestamp'] ).getTime(), occurrences: { diff --git a/x-pack/plugins/apm/server/lib/services/get_service_instance_metadata_details.ts b/x-pack/plugins/apm/server/lib/services/get_service_instance_metadata_details.ts index 1479b0fdbb73c..4d67a0a2b43ad 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_instance_metadata_details.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_instance_metadata_details.ts @@ -19,11 +19,6 @@ import { getProcessorEventForAggregatedTransactions, } from '../helpers/aggregated_transactions'; -export interface KeyValue { - key: string; - value: any | undefined; -} - export async function getServiceInstanceMetadataDetails({ serviceName, serviceNodeName, diff --git a/x-pack/plugins/apm/server/lib/traces/get_trace.ts b/x-pack/plugins/apm/server/lib/traces/get_trace.ts index 3c60cd84fca39..a0cc6b7241d4e 100644 --- a/x-pack/plugins/apm/server/lib/traces/get_trace.ts +++ b/x-pack/plugins/apm/server/lib/traces/get_trace.ts @@ -5,11 +5,9 @@ * 2.0. */ -import { PromiseReturnType } from '../../../../observability/typings/common'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getTraceItems } from './get_trace_items'; -export type TraceAPIResponse = PromiseReturnType; export async function getTrace(traceId: string, setup: Setup & SetupTimeRange) { const { errorsPerTransaction, ...trace } = await getTraceItems( traceId, diff --git a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts index 026cf9dcceb79..6c957df313866 100644 --- a/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts +++ b/x-pack/plugins/apm/server/lib/traces/get_trace_items.ts @@ -15,15 +15,10 @@ import { TRANSACTION_ID, ERROR_LOG_LEVEL, } from '../../../common/elasticsearch_fieldnames'; -import { APMError } from '../../../typings/es_schemas/ui/apm_error'; import { rangeQuery } from '../../../../observability/server'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { PromiseValueType } from '../../../typings/common'; -export interface ErrorsPerTransaction { - [transactionId: string]: number; -} - export async function getTraceItems( traceId: string, setup: Setup & SetupTimeRange @@ -96,10 +91,7 @@ export async function getTraceItems( const items = traceResponse.hits.hits.map((hit) => hit._source); - const errorFrequencies: { - errorsPerTransaction: ErrorsPerTransaction; - errorDocs: APMError[]; - } = { + const errorFrequencies = { errorDocs: errorResponse.hits.hits.map(({ _source }) => _source), errorsPerTransaction: errorResponse.aggregations?.by_transaction_id.buckets.reduce( @@ -109,7 +101,7 @@ export async function getTraceItems( [current.key]: current.doc_count, }; }, - {} as ErrorsPerTransaction + {} as Record ) ?? {}, }; diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 50dd09a6366e8..24d41e2574bbe 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/backends.ts b/x-pack/plugins/apm/server/routes/backends.ts index c738e9aa64007..e673770dbbc1a 100644 --- a/x-pack/plugins/apm/server/routes/backends.ts +++ b/x-pack/plugins/apm/server/routes/backends.ts @@ -25,7 +25,7 @@ const topBackendsRoute = createApmServerRoute({ query: t.intersection([rangeRt, t.type({ numBuckets: toNumberRt })]), }), t.partial({ - query: t.intersection([environmentRt, offsetRt]), + query: t.intersection([environmentRt, offsetRt, kueryRt]), }), ]), options: { @@ -35,9 +35,9 @@ const topBackendsRoute = createApmServerRoute({ const setup = await setupRequest(resources); const { start, end } = setup; - const { environment, offset, numBuckets } = resources.params.query; + const { environment, offset, numBuckets, kuery } = resources.params.query; - const opts = { setup, start, end, numBuckets, environment }; + const opts = { setup, start, end, numBuckets, environment, kuery }; const [currentBackends, previousBackends] = await Promise.all([ getTopBackends(opts), diff --git a/x-pack/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts index 267479de4c102..023753932d21f 100644 --- a/x-pack/plugins/apm/server/routes/service_map.ts +++ b/x-pack/plugins/apm/server/routes/service_map.ts @@ -7,16 +7,17 @@ import Boom from '@hapi/boom'; import * as t from 'io-ts'; +import { isActivePlatinumLicense } from '../../common/license_check'; import { invalidLicenseMessage } from '../../common/service_map'; +import { notifyFeatureUsage } from '../feature'; +import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions'; import { setupRequest } from '../lib/helpers/setup_request'; import { getServiceMap } from '../lib/service_map/get_service_map'; +import { getServiceMapBackendNodeInfo } from '../lib/service_map/get_service_map_backend_node_info'; import { getServiceMapServiceNodeInfo } from '../lib/service_map/get_service_map_service_node_info'; import { createApmServerRoute } from './create_apm_server_route'; -import { environmentRt, rangeRt } from './default_api_types'; -import { notifyFeatureUsage } from '../feature'; -import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions'; -import { isActivePlatinumLicense } from '../../common/license_check'; import { createApmServerRouteRepository } from './create_apm_server_route_repository'; +import { environmentRt, rangeRt } from './default_api_types'; const serviceMapRoute = createApmServerRoute({ endpoint: 'GET /api/apm/service-map', @@ -100,6 +101,40 @@ const serviceMapServiceNodeRoute = createApmServerRoute({ }, }); +const serviceMapBackendNodeRoute = createApmServerRoute({ + endpoint: 'GET /api/apm/service-map/backend/{backendName}', + params: t.type({ + path: t.type({ + backendName: t.string, + }), + query: t.intersection([environmentRt, rangeRt]), + }), + options: { tags: ['access:apm'] }, + handler: async (resources) => { + const { config, context, params } = resources; + + if (!config['xpack.apm.serviceMapEnabled']) { + throw Boom.notFound(); + } + if (!isActivePlatinumLicense(context.licensing.license)) { + throw Boom.forbidden(invalidLicenseMessage); + } + const setup = await setupRequest(resources); + + const { + path: { backendName }, + query: { environment }, + } = params; + + return getServiceMapBackendNodeInfo({ + environment, + setup, + backendName, + }); + }, +}); + export const serviceMapRouteRepository = createApmServerRouteRepository() .add(serviceMapRoute) - .add(serviceMapServiceNodeRoute); + .add(serviceMapServiceNodeRoute) + .add(serviceMapBackendNodeRoute); diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts index 87eac9374fd02..f5156fe85fbf5 100644 --- a/x-pack/plugins/apm/server/routes/services.ts +++ b/x-pack/plugins/apm/server/routes/services.ts @@ -42,6 +42,7 @@ import { } from './default_api_types'; import { offsetPreviousPeriodCoordinates } from '../../common/utils/offset_previous_period_coordinate'; import { getServicesDetailedStatistics } from '../lib/services/get_services_detailed_statistics'; +import { getServiceDependenciesBreakdown } from '../lib/services/get_service_dependencies_breakdown'; const servicesRoute = createApmServerRoute({ endpoint: 'GET /api/apm/services', @@ -692,6 +693,38 @@ export const serviceDependenciesRoute = createApmServerRoute({ }, }); +export const serviceDependenciesBreakdownRoute = createApmServerRoute({ + endpoint: 'GET /api/apm/services/{serviceName}/dependencies/breakdown', + params: t.type({ + path: t.type({ + serviceName: t.string, + }), + query: t.intersection([environmentRt, rangeRt, kueryRt]), + }), + options: { + tags: ['access:apm'], + }, + handler: async (resources) => { + const setup = await setupRequest(resources); + const { params } = resources; + const { serviceName } = params.path; + const { environment, start, end, kuery } = params.query; + + const breakdown = await getServiceDependenciesBreakdown({ + setup, + start, + end, + serviceName, + environment, + kuery, + }); + + return { + breakdown, + }; + }, +}); + const serviceProfilingTimelineRoute = createApmServerRoute({ endpoint: 'GET /api/apm/services/{serviceName}/profiling/timeline', params: t.type({ @@ -822,6 +855,7 @@ export const serviceRouteRepository = createApmServerRouteRepository() .add(serviceInstancesMainStatisticsRoute) .add(serviceInstancesDetailedStatisticsRoute) .add(serviceDependenciesRoute) + .add(serviceDependenciesBreakdownRoute) .add(serviceProfilingTimelineRoute) .add(serviceProfilingStatisticsRoute) .add(serviceAlertsRoute); 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 0161b11cf0784..0000000000000 --- 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/apm/typings/es_schemas/raw/span_raw.ts b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts index 6b6f94779a9af..4865396cae7b2 100644 --- a/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts +++ b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts @@ -54,6 +54,11 @@ export interface SpanRaw extends APMBaseDoc { body?: string; headers?: Record; }; + composite?: { + count: number; + sum: { us: number }; + compression_strategy: string; + }; }; timestamp: TimestampUs; transaction?: { 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 06376bf1d750b..76176f6ba2133 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/renderers/filters/time_filter/components/__stories__/__snapshots__/time_filter.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/__stories__/__snapshots__/time_filter.stories.storyshot index 422a61d4ef03e..a712563966e0b 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/__stories__/__snapshots__/time_filter.stories.storyshot +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/filters/time_filter/components/__stories__/__snapshots__/time_filter.stories.storyshot @@ -164,6 +164,7 @@ exports[`Storyshots renderers/TimeFilter with absolute time bounds 1`] = ` >
    @@ -440,6 +441,7 @@ exports[`Storyshots renderers/TimeFilter with relative time bounds 1`] = ` >
    diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot index c2f9d88724609..f7c50907424fd 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot +++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/__stories__/__snapshots__/extended_template.stories.storyshot @@ -169,6 +169,7 @@ exports[`Storyshots arguments/AxisConfig/components extended disabled 1`] = ` >

    Switch on to view axis settings 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 90f5da78f79b2..7c7d573bcd76c 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 501c3fb464c90..775588226aa80 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 263284564de80..c465176e7ed01 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/canvas/public/components/asset_manager/__stories__/__snapshots__/asset.stories.storyshot b/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset.stories.storyshot index 138f8a2af68f0..19b44540943b3 100644 --- a/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset.stories.storyshot @@ -43,6 +43,7 @@ exports[`Storyshots components/Assets/Asset airplane 1`] = `
    ( @@ -229,6 +230,7 @@ exports[`Storyshots components/Assets/Asset marker 1`] = `
    ( diff --git a/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset_manager.stories.storyshot b/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset_manager.stories.storyshot index d567d3cf85f13..05ef9df1c8601 100644 --- a/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset_manager.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/asset_manager/__stories__/__snapshots__/asset_manager.stories.storyshot @@ -95,6 +95,7 @@ exports[`Storyshots components/Assets/AssetManager no assets 1`] = ` >

    Below are the image assets in this workpad. Any assets that are currently in use cannot be determined at this time. To reclaim space, delete assets. @@ -278,6 +279,7 @@ exports[`Storyshots components/Assets/AssetManager two assets 1`] = ` >

    Below are the image assets in this workpad. Any assets that are currently in use cannot be determined at this time. To reclaim space, delete assets. @@ -325,6 +327,7 @@ exports[`Storyshots components/Assets/AssetManager two assets 1`] = `
    ( @@ -500,6 +503,7 @@ exports[`Storyshots components/Assets/AssetManager two assets 1`] = `
    ( diff --git a/x-pack/plugins/canvas/public/components/datasource/__stories__/__snapshots__/datasource_component.stories.storyshot b/x-pack/plugins/canvas/public/components/datasource/__stories__/__snapshots__/datasource_component.stories.storyshot index 836047959caee..6e665f48b3e9a 100644 --- a/x-pack/plugins/canvas/public/components/datasource/__stories__/__snapshots__/datasource_component.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/datasource/__stories__/__snapshots__/datasource_component.stories.storyshot @@ -41,6 +41,7 @@ exports[`Storyshots components/datasource/DatasourceComponent datasource with ex >

    The datasource has an argument controlled by an expression. Use the expression editor to modify the datasource. diff --git a/x-pack/plugins/canvas/public/components/home/my_workpads/__snapshots__/empty_prompt.stories.storyshot b/x-pack/plugins/canvas/public/components/home/my_workpads/__snapshots__/empty_prompt.stories.storyshot index c6468cf5a6f0a..6d782713d8fc1 100644 --- a/x-pack/plugins/canvas/public/components/home/my_workpads/__snapshots__/empty_prompt.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/home/my_workpads/__snapshots__/empty_prompt.stories.storyshot @@ -34,6 +34,7 @@ exports[`Storyshots Home/Components/Empty Prompt Empty Prompt 1`] = `

    ⌥ + P
    @@ -44,6 +45,7 @@ exports[`Storyshots components/ToolTipShortcut with cmd 1`] = ` >
    ⌘ + D
    @@ -70,6 +72,7 @@ exports[`Storyshots components/ToolTipShortcut with down arrow 1`] = ` >
    ⌘ + SHIFT + ↓
    @@ -96,6 +99,7 @@ exports[`Storyshots components/ToolTipShortcut with left arrow 1`] = ` >
    @@ -122,6 +126,7 @@ exports[`Storyshots components/ToolTipShortcut with right arrow 1`] = ` >
    @@ -148,6 +153,7 @@ exports[`Storyshots components/ToolTipShortcut with shortcut 1`] = ` >
    G
    @@ -174,6 +180,7 @@ exports[`Storyshots components/ToolTipShortcut with up arrow 1`] = ` >
    ⌘ + SHIFT + ↑
    diff --git a/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/delete_var.stories.storyshot b/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/delete_var.stories.storyshot index a83682140693d..f5351b0d8ea5f 100644 --- a/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/delete_var.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/var_config/__stories__/__snapshots__/delete_var.stories.storyshot @@ -46,6 +46,7 @@ Array [ >
    Deleting this variable may adversely affect the workpad. Are you sure you wish to continue?
    diff --git a/x-pack/plugins/canvas/shareable_runtime/components/__stories__/__snapshots__/canvas.stories.storyshot b/x-pack/plugins/canvas/shareable_runtime/components/__stories__/__snapshots__/canvas.stories.storyshot index d23371d0c3499..c5b6d768c89d8 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/__stories__/__snapshots__/canvas.stories.storyshot +++ b/x-pack/plugins/canvas/shareable_runtime/components/__stories__/__snapshots__/canvas.stories.storyshot @@ -1344,6 +1344,7 @@ exports[`Storyshots shareables/Canvas component 1`] = ` >
    Page 1 @@ -2826,6 +2828,7 @@ exports[`Storyshots shareables/Canvas contextual: austin 1`] = ` >
    Page 1 @@ -3124,6 +3128,7 @@ exports[`Storyshots shareables/Canvas contextual: hello 1`] = ` >
    Page 1 diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/footer.stories.storyshot b/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/footer.stories.storyshot index 57af8bcb4fecf..3f89e485e1ba0 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/footer.stories.storyshot +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/footer.stories.storyshot @@ -1297,6 +1297,7 @@ exports[`Storyshots shareables/Footer contextual: austin 1`] = ` >
    Page 1 @@ -1549,6 +1551,7 @@ exports[`Storyshots shareables/Footer contextual: hello 1`] = ` >
    Page 1 diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/page_controls.stories.storyshot b/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/page_controls.stories.storyshot index 81b0ee9cca832..791e1bd9081af 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/page_controls.stories.storyshot +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/page_controls.stories.storyshot @@ -58,6 +58,7 @@ exports[`Storyshots shareables/Footer/PageControls component 1`] = ` >
    Page 1 @@ -155,6 +156,7 @@ exports[`Storyshots shareables/Footer/PageControls contextual: austin 1`] = ` >
    Page 1 @@ -252,6 +254,7 @@ exports[`Storyshots shareables/Footer/PageControls contextual: hello 1`] = ` >
    Page 1 diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/title.stories.storyshot b/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/title.stories.storyshot index 7d27ac836e130..2b326fd0ec51a 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/title.stories.storyshot +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/__stories__/__snapshots__/title.stories.storyshot @@ -51,6 +51,7 @@ exports[`Storyshots shareables/Footer/Title component 1`] = ` >
    { + beforeEach(() => { + jest.clearAllMocks(); + }); + + [(CrawlerStatus.Starting, CrawlerStatus.Running, CrawlerStatus.Canceling)].forEach((status) => { + describe(`when the status is ${status}`, () => { + it('renders a callout', () => { + setMockValues({ + mostRecentCrawlRequestStatus: status, + }); + + const wrapper = shallow(); + + expect(wrapper.find(EuiCallOut)).toHaveLength(1); + }); + }); + }); + + [ + CrawlerStatus.Success, + CrawlerStatus.Failed, + CrawlerStatus.Canceled, + CrawlerStatus.Pending, + CrawlerStatus.Suspended, + CrawlerStatus.Suspending, + ].forEach((status) => { + describe(`when the status is ${status}`, () => { + it('does not render a banner/callout', () => { + setMockValues({ + mostRecentCrawlRequestStatus: status, + }); + + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_banner.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_banner.tsx new file mode 100644 index 0000000000000..833db286fc2e5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_banner.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues } from 'kea'; + +import { EuiCallOut } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { CrawlerOverviewLogic } from '../crawler_overview_logic'; +import { CrawlerStatus } from '../types'; + +export const CrawlerStatusBanner: React.FC = () => { + const { mostRecentCrawlRequestStatus } = useValues(CrawlerOverviewLogic); + if ( + mostRecentCrawlRequestStatus === CrawlerStatus.Running || + mostRecentCrawlRequestStatus === CrawlerStatus.Starting || + mostRecentCrawlRequestStatus === CrawlerStatus.Canceling + ) { + return ( + + ); + } + return null; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.test.tsx new file mode 100644 index 0000000000000..9d585789d8e50 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.test.tsx @@ -0,0 +1,156 @@ +/* + * 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 React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiButton } from '@elastic/eui'; + +import { CrawlerDomain, CrawlerStatus } from '../../types'; + +import { CrawlerStatusIndicator } from './crawler_status_indicator'; +import { StopCrawlPopoverContextMenu } from './stop_crawl_popover_context_menu'; + +const MOCK_VALUES = { + domains: [{}, {}] as CrawlerDomain[], + mostRecentCrawlRequestStatus: CrawlerStatus.Success, +}; + +const MOCK_ACTIONS = { + startCrawl: jest.fn(), + stopCrawl: jest.fn(), +}; + +describe('CrawlerStatusIndicator', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockActions(MOCK_ACTIONS); + }); + + describe('when status is not a valid status', () => { + it('is disabled', () => { + // this tests a codepath that should be impossible to reach, status should always be a CrawlerStatus + // but we use a switch statement and need to test the default case for this to recieve 100% coverage + setMockValues({ + ...MOCK_VALUES, + mostRecentCrawlRequestStatus: null, + }); + + const wrapper = shallow(); + expect(wrapper.is(EuiButton)).toEqual(true); + expect(wrapper.render().text()).toContain('Start a crawl'); + expect(wrapper.prop('disabled')).toEqual(true); + }); + }); + + describe('when there are no domains', () => { + it('is disabled', () => { + setMockValues({ + ...MOCK_VALUES, + domains: [], + }); + + const wrapper = shallow(); + expect(wrapper.is(EuiButton)).toEqual(true); + expect(wrapper.render().text()).toContain('Start a crawl'); + expect(wrapper.prop('disabled')).toEqual(true); + }); + }); + + describe('when the status is success', () => { + it('renders an CrawlerStatusIndicator with a start crawl button', () => { + setMockValues({ + ...MOCK_VALUES, + mostRecentCrawlRequestStatus: CrawlerStatus.Success, + }); + + const wrapper = shallow(); + expect(wrapper.is(EuiButton)).toEqual(true); + expect(wrapper.render().text()).toContain('Start a crawl'); + expect(wrapper.prop('onClick')).toEqual(MOCK_ACTIONS.startCrawl); + }); + }); + + [CrawlerStatus.Failed, CrawlerStatus.Canceled].forEach((status) => { + describe(`when the status is ready for retry: ${status}`, () => { + it('renders an CrawlerStatusIndicator with a retry crawl button', () => { + setMockValues({ + ...MOCK_VALUES, + mostRecentCrawlRequestStatus: status, + }); + + const wrapper = shallow(); + expect(wrapper.is(EuiButton)).toEqual(true); + expect(wrapper.render().text()).toContain('Retry crawl'); + expect(wrapper.prop('onClick')).toEqual(MOCK_ACTIONS.startCrawl); + }); + }); + }); + + [CrawlerStatus.Pending, CrawlerStatus.Suspended].forEach((status) => { + describe(`when the status is ${status}`, () => { + it('renders an CrawlerStatusIndicator with a pending indicator', () => { + setMockValues({ + ...MOCK_VALUES, + mostRecentCrawlRequestStatus: status, + }); + + const wrapper = shallow(); + expect(wrapper.is(EuiButton)).toEqual(true); + expect(wrapper.render().text()).toContain('Pending...'); + expect(wrapper.prop('disabled')).toEqual(true); + expect(wrapper.prop('isLoading')).toEqual(true); + }); + }); + }); + + describe('when the status is Starting', () => { + it('renders an appropriate CrawlerStatusIndicator', () => { + setMockValues({ + ...MOCK_VALUES, + mostRecentCrawlRequestStatus: CrawlerStatus.Starting, + }); + + const wrapper = shallow(); + expect(wrapper.is(EuiButton)).toEqual(true); + expect(wrapper.render().text()).toContain('Starting...'); + expect(wrapper.prop('isLoading')).toEqual(true); + }); + }); + + describe('when the status is Running', () => { + it('renders a stop crawl popover menu', () => { + setMockValues({ + ...MOCK_VALUES, + mostRecentCrawlRequestStatus: CrawlerStatus.Running, + }); + + const wrapper = shallow(); + expect(wrapper.is(StopCrawlPopoverContextMenu)).toEqual(true); + expect(wrapper.prop('stopCrawl')).toEqual(MOCK_ACTIONS.stopCrawl); + }); + }); + + [CrawlerStatus.Canceling, CrawlerStatus.Suspending].forEach((status) => { + describe(`when the status is ${status}`, () => { + it('renders an CrawlerStatusIndicator with a stopping indicator', () => { + setMockValues({ + ...MOCK_VALUES, + mostRecentCrawlRequestStatus: status, + }); + + const wrapper = shallow(); + expect(wrapper.is(EuiButton)).toEqual(true); + expect(wrapper.render().text()).toContain('Stopping...'); + expect(wrapper.prop('isLoading')).toEqual(true); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.tsx new file mode 100644 index 0000000000000..c1b8ad2073444 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.tsx @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useActions, useValues } from 'kea'; + +import { EuiButton } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { CrawlerOverviewLogic } from '../../crawler_overview_logic'; +import { CrawlerStatus } from '../../types'; + +import { StopCrawlPopoverContextMenu } from './stop_crawl_popover_context_menu'; + +export const CrawlerStatusIndicator: React.FC = () => { + const { domains, mostRecentCrawlRequestStatus } = useValues(CrawlerOverviewLogic); + const { startCrawl, stopCrawl } = useActions(CrawlerOverviewLogic); + + const disabledButton = ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.crawler.crawlerStatusIndicator.startACrawlButtonLabel', + { + defaultMessage: 'Start a crawl', + } + )} + + ); + + if (domains.length === 0) { + return disabledButton; + } + + switch (mostRecentCrawlRequestStatus) { + case CrawlerStatus.Success: + return ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.crawler.crawlerStatusIndicator.startACrawlButtonLabel', + { + defaultMessage: 'Start a crawl', + } + )} + + ); + case CrawlerStatus.Failed: + case CrawlerStatus.Canceled: + return ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.crawler.crawlerStatusIndicator.retryCrawlButtonLabel', + { + defaultMessage: 'Retry crawl', + } + )} + + ); + case CrawlerStatus.Pending: + case CrawlerStatus.Suspended: + return ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.crawler.crawlerStatusIndicator.pendingButtonLabel', + { + defaultMessage: 'Pending...', + } + )} + + ); + case CrawlerStatus.Starting: + return ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.crawler.crawlerStatusIndicator.startingButtonLabel', + { + defaultMessage: 'Starting...', + } + )} + + ); + case CrawlerStatus.Running: + return ; + case CrawlerStatus.Canceling: + case CrawlerStatus.Suspending: + return ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.crawler.crawlerStatusIndicator.stoppingButtonLabel', + { + defaultMessage: 'Stopping...', + } + )} + + ); + default: + // We should never get here, you would have to pass a CrawlerStatus option not covered + // in the switch cases above + return disabledButton; + } +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/stop_crawl_popover_context_menu.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/stop_crawl_popover_context_menu.test.tsx new file mode 100644 index 0000000000000..096238faff9a0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/stop_crawl_popover_context_menu.test.tsx @@ -0,0 +1,51 @@ +/* + * 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 from 'react'; + +import { shallow } from 'enzyme'; + +import { + EuiButton, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiPopover, + EuiResizeObserver, +} from '@elastic/eui'; + +import { mountWithIntl } from '../../../../../test_helpers'; + +import { StopCrawlPopoverContextMenu } from './stop_crawl_popover_context_menu'; + +const stopCrawl = jest.fn(); + +describe('StopCrawlsPopoverContextMenu', () => { + it('is initially closed', () => { + const wrapper = shallow(); + + expect(wrapper.is(EuiPopover)).toBe(true); + expect(wrapper.prop('isOpen')).toEqual(false); + }); + + it('can be opened to stop crawls', () => { + const wrapper = mountWithIntl(); + + wrapper.find(EuiButton).simulate('click'); + + expect(wrapper.find(EuiPopover).prop('isOpen')).toEqual(true); + + const menuItem = wrapper + .find(EuiContextMenuPanel) + .find(EuiResizeObserver) + .find(EuiContextMenuItem); + + expect(menuItem).toHaveLength(1); + + menuItem.simulate('click'); + + expect(stopCrawl).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/stop_crawl_popover_context_menu.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/stop_crawl_popover_context_menu.tsx new file mode 100644 index 0000000000000..6c0e91995e281 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/stop_crawl_popover_context_menu.tsx @@ -0,0 +1,86 @@ +/* + * 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 { + EuiButton, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingSpinner, + EuiPopover, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +interface StopCrawlPopoverContextMenuProps { + stopCrawl(): void; +} + +export const StopCrawlPopoverContextMenu: React.FC = ({ + stopCrawl, + ...rest +}) => { + const [isPopoverOpen, setPopover] = useState(false); + + const togglePopover = () => setPopover(!isPopoverOpen); + + const closePopover = () => setPopover(false); + + return ( + + + + + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.crawler.crawlerStatusIndicator.crawlingButtonLabel', + { + defaultMessage: 'Crawling...', + } + )} + + + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + anchorPosition="downLeft" + > + { + closePopover(); + stopCrawl(); + }} + > + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.crawler.crawlerStatusIndicator.cancelCrawlMenuItemLabel', + { + defaultMessage: 'Cancel Crawl', + } + )} + , + ]} + /> + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.test.tsx index e46804a658803..ae4fc6b04b002 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.test.tsx @@ -13,10 +13,14 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { getPageHeaderActions } from '../../../test_helpers'; + import { AddDomainFlyout } from './components/add_domain/add_domain_flyout'; import { AddDomainForm } from './components/add_domain/add_domain_form'; import { AddDomainFormSubmitButton } from './components/add_domain/add_domain_form_submit_button'; import { CrawlRequestsTable } from './components/crawl_requests_table'; +import { CrawlerStatusBanner } from './components/crawler_status_banner'; +import { CrawlerStatusIndicator } from './components/crawler_status_indicator/crawler_status_indicator'; import { DomainsTable } from './components/domains_table'; import { CrawlerOverview } from './crawler_overview'; import { @@ -75,6 +79,7 @@ const crawlRequests: CrawlRequestFromServer[] = [ describe('CrawlerOverview', () => { const mockActions = { fetchCrawlerData: jest.fn(), + getLatestCrawlRequests: jest.fn(), }; const mockValues = { @@ -88,12 +93,26 @@ describe('CrawlerOverview', () => { setMockActions(mockActions); }); - it('calls fetchCrawlerData on page load', () => { + it('calls fetchCrawlerData and starts polling on page load', () => { setMockValues(mockValues); shallow(); expect(mockActions.fetchCrawlerData).toHaveBeenCalledTimes(1); + expect(mockActions.getLatestCrawlRequests).toHaveBeenCalledWith(false); + }); + + it('contains a crawler status banner', () => { + setMockValues(mockValues); + const wrapper = shallow(); + + expect(wrapper.find(CrawlerStatusBanner)).toHaveLength(1); + }); + + it('contains a crawler status indicator', () => { + const wrapper = shallow(); + + expect(getPageHeaderActions(wrapper).find(CrawlerStatusIndicator)).toHaveLength(1); }); it('hides the domain and crawl request tables when there are no domains, and no crawl requests', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.tsx index 1f676467a5503..c18c1a753d247 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview.tsx @@ -21,6 +21,8 @@ import { AddDomainFlyout } from './components/add_domain/add_domain_flyout'; import { AddDomainForm } from './components/add_domain/add_domain_form'; import { AddDomainFormSubmitButton } from './components/add_domain/add_domain_form_submit_button'; import { CrawlRequestsTable } from './components/crawl_requests_table'; +import { CrawlerStatusBanner } from './components/crawler_status_banner'; +import { CrawlerStatusIndicator } from './components/crawler_status_indicator/crawler_status_indicator'; import { DomainsTable } from './components/domains_table'; import { CRAWLER_TITLE } from './constants'; import { CrawlerOverviewLogic } from './crawler_overview_logic'; @@ -28,18 +30,24 @@ import { CrawlerOverviewLogic } from './crawler_overview_logic'; export const CrawlerOverview: React.FC = () => { const { crawlRequests, dataLoading, domains } = useValues(CrawlerOverviewLogic); - const { fetchCrawlerData } = useActions(CrawlerOverviewLogic); + const { fetchCrawlerData, getLatestCrawlRequests } = useActions(CrawlerOverviewLogic); useEffect(() => { fetchCrawlerData(); + getLatestCrawlRequests(false); }, []); return ( ], + }} isLoading={dataLoading} > + + {domains.length > 0 ? ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview_logic.test.ts index 6ec0cb0f26a78..86f6e14631329 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview_logic.test.ts @@ -23,15 +23,16 @@ import { CrawlerRules, CrawlerStatus, CrawlRequest, - CrawlRequestFromServer, CrawlRule, } from './types'; -import { crawlerDataServerToClient, crawlRequestServerToClient } from './utils'; +import { crawlerDataServerToClient } from './utils'; const DEFAULT_VALUES: CrawlerOverviewValues = { crawlRequests: [], dataLoading: true, domains: [], + mostRecentCrawlRequestStatus: CrawlerStatus.Success, + timeoutId: null, }; const DEFAULT_CRAWL_RULE: CrawlRule = { @@ -55,36 +56,50 @@ const MOCK_SERVER_CRAWLER_DATA: CrawlerDataFromServer = { ], }; -const MOCK_SERVER_CRAWL_REQUESTS_DATA: CrawlRequestFromServer[] = [ - { - id: '618d0e66abe97bc688328900', - status: CrawlerStatus.Pending, - created_at: 'Mon, 31 Aug 2020 17:00:00 +0000', - began_at: null, - completed_at: null, - }, -]; - const MOCK_CLIENT_CRAWLER_DATA = crawlerDataServerToClient(MOCK_SERVER_CRAWLER_DATA); -const MOCK_CLIENT_CRAWL_REQUESTS_DATA = MOCK_SERVER_CRAWL_REQUESTS_DATA.map( - crawlRequestServerToClient -); describe('CrawlerOverviewLogic', () => { - const { mount } = new LogicMounter(CrawlerOverviewLogic); + const { mount, unmount } = new LogicMounter(CrawlerOverviewLogic); const { http } = mockHttpValues; const { flashAPIErrors, flashSuccessToast } = mockFlashMessageHelpers; beforeEach(() => { jest.clearAllMocks(); + jest.useFakeTimers(); // this should be run before every test to reset these mocks mount(); }); + afterAll(() => { + jest.useRealTimers(); + }); + it('has expected default values', () => { expect(CrawlerOverviewLogic.values).toEqual(DEFAULT_VALUES); }); describe('actions', () => { + describe('clearTimeoutId', () => { + it('clears the timeout in the logic', () => { + mount({ + timeoutId: setTimeout(() => {}, 1), + }); + + CrawlerOverviewLogic.actions.clearTimeoutId(); + + expect(CrawlerOverviewLogic.values.timeoutId).toEqual(null); + }); + }); + + describe('onCreateNewTimeout', () => { + it('sets the timeout in the logic', () => { + const timeout = setTimeout(() => {}, 1); + + CrawlerOverviewLogic.actions.onCreateNewTimeout(timeout); + + expect(CrawlerOverviewLogic.values.timeoutId).toEqual(timeout); + }); + }); + describe('onReceiveCrawlerData', () => { const crawlerData: CrawlerData = { domains: [ @@ -139,42 +154,20 @@ describe('CrawlerOverviewLogic', () => { describe('fetchCrawlerData', () => { it('updates logic with data that has been converted from server to client', async () => { jest.spyOn(CrawlerOverviewLogic.actions, 'onReceiveCrawlerData'); - // TODO this spyOn should be removed when crawl request polling is added - jest.spyOn(CrawlerOverviewLogic.actions, 'onReceiveCrawlRequests'); - - // TODO this first mock for MOCK_SERVER_CRAWL_REQUESTS_DATA should be removed when crawl request polling is added - http.get.mockReturnValueOnce(Promise.resolve(MOCK_SERVER_CRAWL_REQUESTS_DATA)); http.get.mockReturnValueOnce(Promise.resolve(MOCK_SERVER_CRAWLER_DATA)); + CrawlerOverviewLogic.actions.fetchCrawlerData(); await nextTick(); - expect(http.get).toHaveBeenNthCalledWith( - 1, - '/api/app_search/engines/some-engine/crawler/crawl_requests' - ); - expect(CrawlerOverviewLogic.actions.onReceiveCrawlRequests).toHaveBeenCalledWith( - MOCK_CLIENT_CRAWL_REQUESTS_DATA - ); - - expect(http.get).toHaveBeenNthCalledWith(2, '/api/app_search/engines/some-engine/crawler'); + expect(http.get).toHaveBeenCalledWith('/api/app_search/engines/some-engine/crawler'); expect(CrawlerOverviewLogic.actions.onReceiveCrawlerData).toHaveBeenCalledWith( MOCK_CLIENT_CRAWLER_DATA ); }); - // TODO this test should be removed when crawl request polling is added - it('calls flashApiErrors when there is an error on the request for crawl results', async () => { - http.get.mockReturnValueOnce(Promise.reject('error')); - CrawlerOverviewLogic.actions.fetchCrawlerData(); - await nextTick(); - - expect(flashAPIErrors).toHaveBeenCalledWith('error'); - }); - it('calls flashApiErrors when there is an error on the request for crawler data', async () => { - // TODO this first mock for MOCK_SERVER_CRAWL_REQUESTS_DATA should be removed when crawl request polling is added - http.get.mockReturnValueOnce(Promise.resolve(MOCK_SERVER_CRAWL_REQUESTS_DATA)); http.get.mockReturnValueOnce(Promise.reject('error')); + CrawlerOverviewLogic.actions.fetchCrawlerData(); await nextTick(); @@ -185,8 +178,8 @@ describe('CrawlerOverviewLogic', () => { describe('deleteDomain', () => { it('calls onReceiveCrawlerData with retrieved data that has been converted from server to client', async () => { jest.spyOn(CrawlerOverviewLogic.actions, 'onReceiveCrawlerData'); - http.delete.mockReturnValue(Promise.resolve(MOCK_SERVER_CRAWLER_DATA)); + CrawlerOverviewLogic.actions.deleteDomain({ id: '1234' } as CrawlerDomain); await nextTick(); @@ -204,11 +197,248 @@ describe('CrawlerOverviewLogic', () => { it('calls flashApiErrors when there is an error', async () => { http.delete.mockReturnValue(Promise.reject('error')); + CrawlerOverviewLogic.actions.deleteDomain({ id: '1234' } as CrawlerDomain); await nextTick(); expect(flashAPIErrors).toHaveBeenCalledWith('error'); }); }); + + describe('startCrawl', () => { + describe('success path', () => { + it('creates a new crawl request and then fetches the latest crawl requests', async () => { + jest.spyOn(CrawlerOverviewLogic.actions, 'getLatestCrawlRequests'); + http.post.mockReturnValueOnce(Promise.resolve()); + + CrawlerOverviewLogic.actions.startCrawl(); + await nextTick(); + + expect(http.post).toHaveBeenCalledWith( + '/api/app_search/engines/some-engine/crawler/crawl_requests' + ); + expect(CrawlerOverviewLogic.actions.getLatestCrawlRequests).toHaveBeenCalled(); + }); + }); + + describe('on failure', () => { + it('flashes an error message', async () => { + http.post.mockReturnValueOnce(Promise.reject('error')); + + CrawlerOverviewLogic.actions.startCrawl(); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledWith('error'); + }); + }); + }); + + describe('stopCrawl', () => { + describe('success path', () => { + it('stops the crawl starts and then fetches the latest crawl requests', async () => { + jest.spyOn(CrawlerOverviewLogic.actions, 'getLatestCrawlRequests'); + http.post.mockReturnValueOnce(Promise.resolve()); + + CrawlerOverviewLogic.actions.stopCrawl(); + await nextTick(); + + expect(http.post).toHaveBeenCalledWith( + '/api/app_search/engines/some-engine/crawler/crawl_requests/cancel' + ); + expect(CrawlerOverviewLogic.actions.getLatestCrawlRequests).toHaveBeenCalled(); + }); + }); + + describe('on failure', () => { + it('flashes an error message', async () => { + jest.spyOn(CrawlerOverviewLogic.actions, 'getLatestCrawlRequests'); + http.post.mockReturnValueOnce(Promise.reject('error')); + + CrawlerOverviewLogic.actions.stopCrawl(); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledWith('error'); + }); + }); + }); + + describe('createNewTimeoutForCrawlRequests', () => { + it('saves the timeout ID in the logic', () => { + jest.spyOn(CrawlerOverviewLogic.actions, 'onCreateNewTimeout'); + jest.spyOn(CrawlerOverviewLogic.actions, 'getLatestCrawlRequests'); + + CrawlerOverviewLogic.actions.createNewTimeoutForCrawlRequests(2000); + + expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 2000); + expect(CrawlerOverviewLogic.actions.onCreateNewTimeout).toHaveBeenCalled(); + + jest.runAllTimers(); + + expect(CrawlerOverviewLogic.actions.getLatestCrawlRequests).toHaveBeenCalled(); + }); + + it('clears a timeout if one already exists', () => { + const timeoutId = setTimeout(() => {}, 1); + mount({ + timeoutId, + }); + + CrawlerOverviewLogic.actions.createNewTimeoutForCrawlRequests(2000); + + expect(clearTimeout).toHaveBeenCalledWith(timeoutId); + }); + }); + + describe('getLatestCrawlRequests', () => { + describe('on success', () => { + [ + CrawlerStatus.Pending, + CrawlerStatus.Starting, + CrawlerStatus.Running, + CrawlerStatus.Canceling, + ].forEach((status) => { + it(`creates a new timeout for status ${status}`, async () => { + jest.spyOn(CrawlerOverviewLogic.actions, 'createNewTimeoutForCrawlRequests'); + http.get.mockReturnValueOnce(Promise.resolve([{ status }])); + + CrawlerOverviewLogic.actions.getLatestCrawlRequests(); + await nextTick(); + + expect( + CrawlerOverviewLogic.actions.createNewTimeoutForCrawlRequests + ).toHaveBeenCalled(); + }); + }); + + [CrawlerStatus.Success, CrawlerStatus.Failed, CrawlerStatus.Canceled].forEach((status) => { + it(`clears the timeout and fetches data for status ${status}`, async () => { + jest.spyOn(CrawlerOverviewLogic.actions, 'clearTimeoutId'); + jest.spyOn(CrawlerOverviewLogic.actions, 'fetchCrawlerData'); + http.get.mockReturnValueOnce(Promise.resolve([{ status }])); + + CrawlerOverviewLogic.actions.getLatestCrawlRequests(); + await nextTick(); + + expect(CrawlerOverviewLogic.actions.clearTimeoutId).toHaveBeenCalled(); + expect(CrawlerOverviewLogic.actions.fetchCrawlerData).toHaveBeenCalled(); + }); + + it(`optionally supresses fetching data for status ${status}`, async () => { + jest.spyOn(CrawlerOverviewLogic.actions, 'clearTimeoutId'); + jest.spyOn(CrawlerOverviewLogic.actions, 'fetchCrawlerData'); + http.get.mockReturnValueOnce(Promise.resolve([{ status }])); + + CrawlerOverviewLogic.actions.getLatestCrawlRequests(false); + await nextTick(); + + expect(CrawlerOverviewLogic.actions.clearTimeoutId).toHaveBeenCalled(); + expect(CrawlerOverviewLogic.actions.fetchCrawlerData).toHaveBeenCalledTimes(0); + }); + }); + }); + + describe('on failure', () => { + it('creates a new timeout', async () => { + jest.spyOn(CrawlerOverviewLogic.actions, 'createNewTimeoutForCrawlRequests'); + http.get.mockReturnValueOnce(Promise.reject()); + + CrawlerOverviewLogic.actions.getLatestCrawlRequests(); + await nextTick(); + + expect(CrawlerOverviewLogic.actions.createNewTimeoutForCrawlRequests).toHaveBeenCalled(); + }); + }); + }); + }); + + describe('selectors', () => { + describe('mostRecentCrawlRequestStatus', () => { + it('is Success when there are no crawl requests', () => { + mount({ + crawlRequests: [], + }); + + expect(CrawlerOverviewLogic.values.mostRecentCrawlRequestStatus).toEqual( + CrawlerStatus.Success + ); + }); + + it('is Success when there are only crawl requests', () => { + mount({ + crawlRequests: [ + { + id: '2', + status: CrawlerStatus.Skipped, + createdAt: 'Mon, 31 Aug 2020 17:00:00 +0000', + beganAt: null, + completedAt: null, + }, + { + id: '1', + status: CrawlerStatus.Skipped, + createdAt: 'Mon, 30 Aug 2020 17:00:00 +0000', + beganAt: null, + completedAt: null, + }, + ], + }); + + expect(CrawlerOverviewLogic.values.mostRecentCrawlRequestStatus).toEqual( + CrawlerStatus.Success + ); + }); + + it('is the first non-skipped crawl request status', () => { + mount({ + crawlRequests: [ + { + id: '3', + status: CrawlerStatus.Skipped, + createdAt: 'Mon, 31 Aug 2020 17:00:00 +0000', + beganAt: null, + completedAt: null, + }, + { + id: '2', + status: CrawlerStatus.Failed, + createdAt: 'Mon, 30 Aug 2020 17:00:00 +0000', + beganAt: null, + completedAt: null, + }, + { + id: '1', + status: CrawlerStatus.Success, + createdAt: 'Mon, 29 Aug 2020 17:00:00 +0000', + beganAt: null, + completedAt: null, + }, + ], + }); + + expect(CrawlerOverviewLogic.values.mostRecentCrawlRequestStatus).toEqual( + CrawlerStatus.Failed + ); + }); + }); + }); + + describe('events', () => { + describe('beforeUnmount', () => { + it('clears the timeout if there is one', () => { + jest.spyOn(global, 'setTimeout'); + + mount({ + timeoutId: setTimeout(() => {}, 1), + }); + unmount(); + + expect(setTimeout).toHaveBeenCalled(); + }); + + it('does not crash if no timeout exists', () => { + mount({ timeoutId: null }); + unmount(); + }); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview_logic.ts index 5d6d1db2bd8a3..35dd866c0453e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_overview_logic.ts @@ -14,7 +14,13 @@ import { flashAPIErrors, flashSuccessToast } from '../../../shared/flash_message import { HttpLogic } from '../../../shared/http'; import { EngineLogic } from '../engine'; -import { CrawlerData, CrawlerDomain, CrawlRequest, CrawlRequestFromServer } from './types'; +import { + CrawlerData, + CrawlerDomain, + CrawlRequest, + CrawlRequestFromServer, + CrawlerStatus, +} from './types'; import { crawlerDataServerToClient, crawlRequestServerToClient } from './utils'; export const DELETE_DOMAIN_MESSAGE = (domainUrl: string) => @@ -28,17 +34,28 @@ export const DELETE_DOMAIN_MESSAGE = (domainUrl: string) => } ); +const POLLING_DURATION = 1000; +const POLLING_DURATION_ON_FAILURE = 5000; + export interface CrawlerOverviewValues { crawlRequests: CrawlRequest[]; dataLoading: boolean; domains: CrawlerDomain[]; + mostRecentCrawlRequestStatus: CrawlerStatus; + timeoutId: NodeJS.Timeout | null; } interface CrawlerOverviewActions { + clearTimeoutId(): void; + createNewTimeoutForCrawlRequests(duration: number): { duration: number }; deleteDomain(domain: CrawlerDomain): { domain: CrawlerDomain }; fetchCrawlerData(): void; + getLatestCrawlRequests(refreshData?: boolean): { refreshData?: boolean }; + onCreateNewTimeout(timeoutId: NodeJS.Timeout): { timeoutId: NodeJS.Timeout }; onReceiveCrawlerData(data: CrawlerData): { data: CrawlerData }; onReceiveCrawlRequests(crawlRequests: CrawlRequest[]): { crawlRequests: CrawlRequest[] }; + startCrawl(): void; + stopCrawl(): void; } export const CrawlerOverviewLogic = kea< @@ -46,10 +63,16 @@ export const CrawlerOverviewLogic = kea< >({ path: ['enterprise_search', 'app_search', 'crawler', 'crawler_overview'], actions: { + clearTimeoutId: true, + createNewTimeoutForCrawlRequests: (duration) => ({ duration }), deleteDomain: (domain) => ({ domain }), fetchCrawlerData: true, + getLatestCrawlRequests: (refreshData) => ({ refreshData }), + onCreateNewTimeout: (timeoutId) => ({ timeoutId }), onReceiveCrawlerData: (data) => ({ data }), onReceiveCrawlRequests: (crawlRequests) => ({ crawlRequests }), + startCrawl: () => null, + stopCrawl: () => null, }, reducers: { dataLoading: [ @@ -70,24 +93,33 @@ export const CrawlerOverviewLogic = kea< onReceiveCrawlRequests: (_, { crawlRequests }) => crawlRequests, }, ], + timeoutId: [ + null, + { + clearTimeoutId: () => null, + onCreateNewTimeout: (_, { timeoutId }) => timeoutId, + }, + ], }, - listeners: ({ actions }) => ({ + selectors: ({ selectors }) => ({ + mostRecentCrawlRequestStatus: [ + () => [selectors.crawlRequests], + (crawlRequests: CrawlerOverviewValues['crawlRequests']) => { + const eligibleCrawlRequests = crawlRequests.filter( + (req) => req.status !== CrawlerStatus.Skipped + ); + if (eligibleCrawlRequests.length === 0) { + return CrawlerStatus.Success; + } + return eligibleCrawlRequests[0].status; + }, + ], + }), + listeners: ({ actions, values }) => ({ fetchCrawlerData: async () => { const { http } = HttpLogic.values; const { engineName } = EngineLogic.values; - // TODO Remove fetching crawl requests here once Crawl Request Polling is implemented - try { - const crawlResultsResponse: CrawlRequestFromServer[] = await http.get( - `/api/app_search/engines/${engineName}/crawler/crawl_requests` - ); - - const crawlRequests = crawlResultsResponse.map(crawlRequestServerToClient); - actions.onReceiveCrawlRequests(crawlRequests); - } catch (e) { - flashAPIErrors(e); - } - try { const response = await http.get(`/api/app_search/engines/${engineName}/crawler`); @@ -118,5 +150,78 @@ export const CrawlerOverviewLogic = kea< flashAPIErrors(e); } }, + startCrawl: async () => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + + try { + await http.post(`/api/app_search/engines/${engineName}/crawler/crawl_requests`); + actions.getLatestCrawlRequests(); + } catch (e) { + flashAPIErrors(e); + } + }, + stopCrawl: async () => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + + try { + await http.post(`/api/app_search/engines/${engineName}/crawler/crawl_requests/cancel`); + actions.getLatestCrawlRequests(); + } catch (e) { + flashAPIErrors(e); + } + }, + createNewTimeoutForCrawlRequests: ({ duration }) => { + if (values.timeoutId) { + clearTimeout(values.timeoutId); + } + + const timeoutIdId = setTimeout(() => { + actions.getLatestCrawlRequests(); + }, duration); + + actions.onCreateNewTimeout(timeoutIdId); + }, + getLatestCrawlRequests: async ({ refreshData = true }) => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + + try { + const crawlRequestsFromServer: CrawlRequestFromServer[] = await http.get( + `/api/app_search/engines/${engineName}/crawler/crawl_requests` + ); + const crawlRequests = crawlRequestsFromServer.map(crawlRequestServerToClient); + actions.onReceiveCrawlRequests(crawlRequests); + if ( + [ + CrawlerStatus.Pending, + CrawlerStatus.Starting, + CrawlerStatus.Running, + CrawlerStatus.Canceling, + ].includes(crawlRequests[0]?.status) + ) { + actions.createNewTimeoutForCrawlRequests(POLLING_DURATION); + } else if ( + [CrawlerStatus.Success, CrawlerStatus.Failed, CrawlerStatus.Canceled].includes( + crawlRequests[0]?.status + ) + ) { + actions.clearTimeoutId(); + if (refreshData) { + actions.fetchCrawlerData(); + } + } + } catch (e) { + actions.createNewTimeoutForCrawlRequests(POLLING_DURATION_ON_FAILURE); + } + }, + }), + events: ({ values }) => ({ + beforeUnmount: () => { + if (values.timeoutId) { + clearTimeout(values.timeoutId); + } + }, }), }); 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 587ba61ce27e9..3fa0153861319 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 c5dd3907c9019..3919740b0c6cb 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 0000000000000..e2b230041c810 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain.test.tsx @@ -0,0 +1,79 @@ +/* + * 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 { getPageHeaderActions } from '../../../test_helpers'; + +import { CrawlerStatusBanner } from './components/crawler_status_banner'; +import { CrawlerStatusIndicator } from './components/crawler_status_indicator/crawler_status_indicator'; +import { CrawlerOverview } from './crawler_overview'; +import { CrawlerSingleDomain } from './crawler_single_domain'; + +const MOCK_VALUES = { + // CrawlerSingleDomainLogic + dataLoading: false, + domain: { + url: 'https://elastic.co', + }, + // CrawlerOverviewLogic + domains: [], + crawlRequests: [], +}; + +const MOCK_ACTIONS = { + fetchCrawlerData: jest.fn(), + fetchDomainData: jest.fn(), + getLatestCrawlRequests: 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').pageTitle).toEqual('https://elastic.co'); + }); + + it('uses a placeholder for the page title and page chrome if a domain has not been set', () => { + setMockValues({ + ...MOCK_VALUES, + domain: null, + }); + + const wrapper = shallow(); + + expect(wrapper.prop('pageHeader').pageTitle).toEqual('Loading...'); + }); + + it('contains a crawler status banner', () => { + const wrapper = shallow(); + + expect(wrapper.find(CrawlerStatusBanner)).toHaveLength(1); + }); + + it('contains a crawler status indicator', () => { + const wrapper = shallow(); + + expect(getPageHeaderActions(wrapper).find(CrawlerStatusIndicator)).toHaveLength(1); + }); +}); 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 0000000000000..9f9ec288a690c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/crawler_single_domain.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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, EuiSpacer } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { getEngineBreadcrumbs } from '../engine'; +import { AppSearchPageTemplate } from '../layout'; + +import { CrawlerStatusBanner } from './components/crawler_status_banner'; +import { CrawlerStatusIndicator } from './components/crawler_status_indicator/crawler_status_indicator'; +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 ( + ] }} + isLoading={dataLoading} + > + + + {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 0000000000000..bb478a30ee5ef --- /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 0000000000000..bccd67a4921d1 --- /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 f54771e2bef9a..3b790bf2696cb 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 @@ -76,6 +76,72 @@ describe('crawler routes', () => { }); }); + describe('POST /api/app_search/engines/{name}/crawler/crawl_requests', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'post', + path: '/api/app_search/engines/{name}/crawler/crawl_requests', + }); + + registerCrawlerRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request to enterprise search', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/api/as/v0/engines/:name/crawler/crawl_requests', + }); + }); + + it('validates correctly with name', () => { + const request = { params: { name: 'some-engine' } }; + mockRouter.shouldValidate(request); + }); + + it('fails validation without name', () => { + const request = { params: {} }; + mockRouter.shouldThrow(request); + }); + }); + + describe('POST /api/app_search/engines/{name}/crawler/crawl_requests/cancel', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'post', + path: '/api/app_search/engines/{name}/crawler/crawl_requests/cancel', + }); + + registerCrawlerRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request to enterprise search', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/api/as/v0/engines/:name/crawler/crawl_requests/active/cancel', + }); + }); + + it('validates correctly with name', () => { + const request = { params: { name: 'some-engine' } }; + mockRouter.shouldValidate(request); + }); + + it('fails validation without name', () => { + const request = { params: {} }; + mockRouter.shouldThrow(request); + }); + }); + describe('POST /api/app_search/engines/{name}/crawler/domains', () => { let mockRouter: MockRouter; @@ -178,6 +244,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 5404a9a00bdac..26a755a6d2b6c 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 @@ -41,6 +41,34 @@ export function registerCrawlerRoutes({ }) ); + router.post( + { + path: '/api/app_search/engines/{name}/crawler/crawl_requests', + validate: { + params: schema.object({ + name: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/api/as/v0/engines/:name/crawler/crawl_requests', + }) + ); + + router.post( + { + path: '/api/app_search/engines/{name}/crawler/crawl_requests/cancel', + validate: { + params: schema.object({ + name: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/api/as/v0/engines/:name/crawler/crawl_requests/active/cancel', + }) + ); + router.post( { path: '/api/app_search/engines/{name}/crawler/domains', @@ -66,6 +94,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 eed94de97113d..398421278b723 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 9bc1bc977b786..3d3a4dda60632 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/fleet/server/services/preconfiguration.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts index 5ea4b896e7fa1..d374553e1db83 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts @@ -14,6 +14,8 @@ import type { AgentPolicy, NewPackagePolicy, Output } from '../types'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../constants'; +import * as agentPolicy from './agent_policy'; + import { ensurePreconfiguredPackagesAndPolicies, comparePreconfiguredPolicyToCurrent, @@ -128,6 +130,7 @@ jest.mock('./epm/packages/get', () => ({ jest.mock('./package_policy', () => ({ ...jest.requireActual('./package_policy'), packagePolicyService: { + getByIDs: jest.fn().mockReturnValue([]), create(soClient: any, esClient: any, newPackagePolicy: NewPackagePolicy) { return { id: 'mocked', @@ -152,10 +155,13 @@ jest.mock('./app_context', () => ({ }, })); +const spyAgentPolicyServiceUpdate = jest.spyOn(agentPolicy.agentPolicyService, 'update'); + describe('policy preconfiguration', () => { beforeEach(() => { mockInstalledPackages.clear(); mockConfiguredPolicies.clear(); + spyAgentPolicyServiceUpdate.mockClear(); }); it('should perform a no-op when passed no policies or packages', async () => { @@ -331,6 +337,84 @@ describe('policy preconfiguration', () => { expect(policiesB[0].updated_at).toEqual(policiesA[0].updated_at); expect(nonFatalErrorsB.length).toBe(0); }); + + it('should update a managed policy if top level fields are changed', async () => { + const soClient = getPutPreconfiguredPackagesMock(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + + mockConfiguredPolicies.set('test-id', { + name: 'Test policy', + description: 'Test policy description', + unenroll_timeout: 120, + namespace: 'default', + id: 'test-id', + package_policies: [], + is_managed: true, + } as PreconfiguredAgentPolicy); + + const { + policies, + nonFatalErrors: nonFatalErrorsB, + } = await ensurePreconfiguredPackagesAndPolicies( + soClient, + esClient, + [ + { + name: 'Renamed Test policy', + description: 'Renamed Test policy description', + unenroll_timeout: 999, + namespace: 'default', + id: 'test-id', + is_managed: true, + package_policies: [], + }, + ] as PreconfiguredAgentPolicy[], + [], + mockDefaultOutput + ); + expect(spyAgentPolicyServiceUpdate).toBeCalled(); + expect(spyAgentPolicyServiceUpdate).toBeCalledWith( + expect.anything(), // soClient + expect.anything(), // esClient + 'test-id', + expect.objectContaining({ + name: 'Renamed Test policy', + description: 'Renamed Test policy description', + unenroll_timeout: 999, + }) + ); + expect(policies.length).toEqual(1); + expect(policies[0].id).toBe('test-id'); + expect(nonFatalErrorsB.length).toBe(0); + }); + + it('should not update a managed policy if a top level field has not changed', async () => { + const soClient = getPutPreconfiguredPackagesMock(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + const policy: PreconfiguredAgentPolicy = { + name: 'Test policy', + namespace: 'default', + id: 'test-id', + package_policies: [], + is_managed: true, + }; + mockConfiguredPolicies.set('test-id', policy); + + const { + policies, + nonFatalErrors: nonFatalErrorsB, + } = await ensurePreconfiguredPackagesAndPolicies( + soClient, + esClient, + [policy], + [], + mockDefaultOutput + ); + expect(spyAgentPolicyServiceUpdate).not.toBeCalled(); + expect(policies.length).toEqual(1); + expect(policies[0].id).toBe('test-id'); + expect(nonFatalErrorsB.length).toBe(0); + }); }); describe('comparePreconfiguredPolicyToCurrent', () => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.tsx.snap b/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.tsx.snap index 9595009347259..aa978d27f9e23 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.tsx.snap +++ b/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.tsx.snap @@ -326,6 +326,7 @@ exports[`extend index management ilm summary extension should return extension w >
    illegal_argument_exception : diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx index 8835a7cd55ce8..be0ecbb1dab65 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx @@ -17,6 +17,8 @@ import { EuiToolTip, EuiIcon, EuiFieldSearch, + EuiAccordion, + EuiPanel, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -259,6 +261,11 @@ export const Expressions: React.FC = (props) => { return alertParams.groupBy; }, [alertParams.groupBy]); + const areAllAggsRate = useMemo( + () => alertParams.criteria?.every((c) => c.aggType === Aggregators.RATE), + [alertParams.criteria] + ); + return ( <> @@ -323,27 +330,60 @@ export const Expressions: React.FC = (props) => {
    - - {i18n.translate('xpack.infra.metrics.alertFlyout.alertOnNoData', { - defaultMessage: "Alert me if there's no data", - })}{' '} - - - - - } - checked={alertParams.alertOnNoData} - onChange={(e) => setAlertParams('alertOnNoData', e.target.checked)} - /> - + + + + {i18n.translate('xpack.infra.metrics.alertFlyout.alertOnNoData', { + defaultMessage: "Alert me if there's no data", + })}{' '} + + + + + } + checked={alertParams.alertOnNoData} + onChange={(e) => setAlertParams('alertOnNoData', e.target.checked)} + /> + + {i18n.translate('xpack.infra.metrics.alertFlyout.shouldDropPartialBuckets', { + defaultMessage: 'Drop partial buckets when evaluating data', + })}{' '} + + + + + } + checked={areAllAggsRate || alertParams.shouldDropPartialBuckets} + disabled={areAllAggsRate} + onChange={(e) => setAlertParams('shouldDropPartialBuckets', e.target.checked)} + /> + + = (props) => { alertThrottle={alertThrottle} alertNotifyWhen={alertNotifyWhen} alertType={METRIC_THRESHOLD_ALERT_TYPE_ID} - alertParams={pick(alertParams, 'criteria', 'groupBy', 'filterQuery', 'sourceId')} + alertParams={pick( + alertParams, + 'criteria', + 'groupBy', + 'filterQuery', + 'sourceId', + 'shouldDropPartialBuckets' + )} showNoDataResults={alertParams.alertOnNoData} validate={validateMetricThreshold} groupByDisplayName={groupByPreviewDisplayName} diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts index fca4160199030..a679579e57235 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts @@ -61,4 +61,5 @@ export interface AlertParams { sourceId: string; filterQueryText?: string; alertOnNoData?: boolean; + shouldDropPartialBuckets?: boolean; } diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts index d3fa983ff9e84..6d99b6e2f5fe3 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts @@ -45,6 +45,7 @@ export interface EvaluatedAlertParams { criteria: MetricExpressionParams[]; groupBy: string | undefined | string[]; filterQuery: string | undefined; + shouldDropPartialBuckets?: boolean; } export const evaluateAlert = ( @@ -53,7 +54,7 @@ export const evaluateAlert = { - const { criteria, groupBy, filterQuery } = params; + const { criteria, groupBy, filterQuery, shouldDropPartialBuckets } = params; return Promise.all( criteria.map(async (criterion) => { const currentValues = await getMetric( @@ -63,7 +64,8 @@ export const evaluateAlert = Promise> = async function ( esClient, params, @@ -113,7 +114,8 @@ const getMetric: ( timefield, groupBy, filterQuery, - timeframe + timeframe, + shouldDropPartialBuckets ) { const { aggType, timeSize, timeUnit } = params; const hasGroupBy = groupBy && groupBy.length; @@ -127,10 +129,10 @@ const getMetric: ( .startOf(timeUnit) .valueOf(); - // We need enough data for 5 buckets worth of data. We also need - // to convert the intervalAsSeconds to milliseconds. - // TODO: We only need to get 5 buckets for the rate query, so this logic should move there. - const minimumFrom = to - intervalAsMS * MINIMUM_BUCKETS; + // Rate aggregations need 5 buckets worth of data + const minimumBuckets = aggType === Aggregators.RATE ? 5 : 1; + + const minimumFrom = to - intervalAsMS * minimumBuckets; const from = roundTimestamp( timeframe && timeframe.start <= minimumFrom ? timeframe.start : minimumFrom, @@ -145,6 +147,16 @@ const getMetric: ( filterQuery ); + const dropPartialBucketsOptions = + // Rate aggs always drop partial buckets; guard against this boolean being passed as false + shouldDropPartialBuckets || aggType === Aggregators.RATE + ? { + from, + to, + bucketSizeInMillis: intervalAsMS, + } + : null; + try { if (hasGroupBy) { const bucketSelector = ( @@ -166,11 +178,7 @@ const getMetric: ( ...result, [Object.values(bucket.key) .map((value) => value) - .join(', ')]: getValuesFromAggregations(bucket, aggType, { - from, - to, - bucketSizeInMillis: intervalAsMS, - }), + .join(', ')]: getValuesFromAggregations(bucket, aggType, dropPartialBucketsOptions), }), {} ); @@ -184,7 +192,7 @@ const getMetric: ( [UNGROUPED_FACTORY_KEY]: getValuesFromAggregations( (result.aggregations! as unknown) as Aggregation, aggType, - { from, to, bucketSizeInMillis: intervalAsMS } + dropPartialBucketsOptions ), }; } catch (e) { @@ -224,47 +232,46 @@ const dropPartialBuckets = ({ from, to, bucketSizeInMillis }: DropPartialBucketO const getValuesFromAggregations = ( aggregations: Aggregation, aggType: MetricExpressionParams['aggType'], - dropPartialBucketsOptions: DropPartialBucketOptions + dropPartialBucketsOptions: DropPartialBucketOptions | null ) => { try { const { buckets } = aggregations.aggregatedIntervals; if (!buckets.length) return null; // No Data state + let mappedBuckets; + if (aggType === Aggregators.COUNT) { - return buckets.map((bucket) => ({ + mappedBuckets = buckets.map((bucket) => ({ key: bucket.from_as_string, value: bucket.doc_count, })); - } - if (aggType === Aggregators.P95 || aggType === Aggregators.P99) { - return buckets.map((bucket) => { + } else if (aggType === Aggregators.P95 || aggType === Aggregators.P99) { + mappedBuckets = buckets.map((bucket) => { const values = bucket.aggregatedValue?.values || []; const firstValue = first(values); if (!firstValue) return null; return { key: bucket.from_as_string, value: firstValue.value }; }); - } - - if (aggType === Aggregators.AVERAGE) { - return buckets.map((bucket) => ({ + } else if (aggType === Aggregators.AVERAGE) { + mappedBuckets = buckets.map((bucket) => ({ + key: bucket.key_as_string ?? bucket.from_as_string, + value: bucket.aggregatedValue?.value ?? null, + })); + } else if (aggType === Aggregators.RATE) { + mappedBuckets = buckets.map((bucket) => ({ + key: bucket.key_as_string ?? bucket.from_as_string, + value: bucket.aggregatedValue?.value ?? null, + })); + } else { + mappedBuckets = buckets.map((bucket) => ({ key: bucket.key_as_string ?? bucket.from_as_string, value: bucket.aggregatedValue?.value ?? null, })); } - - if (aggType === Aggregators.RATE) { - return buckets - .map((bucket) => ({ - key: bucket.key_as_string ?? bucket.from_as_string, - value: bucket.aggregatedValue?.value ?? null, - })) - .filter(dropPartialBuckets(dropPartialBucketsOptions)); + if (dropPartialBucketsOptions) { + return mappedBuckets.filter(dropPartialBuckets(dropPartialBucketsOptions)); } - - return buckets.map((bucket) => ({ - key: bucket.key_as_string ?? bucket.from_as_string, - value: bucket.aggregatedValue?.value ?? null, - })); + return mappedBuckets; } catch (e) { return NaN; // Error state } diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts index 2ba8365d6b4a9..dd29437275243 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts @@ -64,4 +64,30 @@ describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { ); }); }); + + describe('when passed a timeframe of 1 hour', () => { + const testTimeframe = { + start: moment().subtract(1, 'hour').valueOf(), + end: moment().valueOf(), + }; + const searchBodyWithoutGroupBy = getElasticsearchMetricQuery( + expressionParams, + timefield, + testTimeframe + ); + const searchBodyWithGroupBy = getElasticsearchMetricQuery( + expressionParams, + timefield, + testTimeframe, + groupBy + ); + test("generates 1 hour's worth of buckets", () => { + // @ts-ignore + expect(searchBodyWithoutGroupBy.aggs.aggregatedIntervals.date_range.ranges.length).toBe(60); + expect( + // @ts-ignore + searchBodyWithGroupBy.aggs.groupings.aggs.aggregatedIntervals.date_range.ranges.length + ).toBe(60); + }); + }); }); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.ts index cde84b217be95..66e112640c357 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.ts @@ -76,12 +76,13 @@ export const getElasticsearchMetricQuery = ( aggregatedIntervals: { date_range: { field: timefield, - ranges: [ - { - from: to - intervalAsMS - deliveryDelay, - to: to - deliveryDelay, - }, - ], + // Generate an array of buckets, starting at `from` and ending at `to` + // This is usually only necessary for alert previews or rate aggs. Most alert evaluations + // will generate only one bucket from this logic. + ranges: Array.from(Array(Math.floor((to - from) / intervalAsMS)), (_, i) => ({ + from: from + intervalAsMS * i - deliveryDelay, + to: from + intervalAsMS * (i + 1) - deliveryDelay, + })), }, aggregations, }, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/preview_metric_threshold_alert.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/preview_metric_threshold_alert.ts index a4c207f4006d5..931b830875cdf 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/preview_metric_threshold_alert.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/preview_metric_threshold_alert.ts @@ -26,6 +26,7 @@ interface PreviewMetricThresholdAlertParams { criteria: MetricExpressionParams[]; groupBy: string | undefined | string[]; filterQuery: string | undefined; + shouldDropPartialBuckets?: boolean; }; config: InfraSource['configuration']; lookback: Unit; 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 f5d457db65234..b5ff0a7e6915b 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 13ca811b0b082..30060c10ea37e 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 9f8f56cc92768..61e74e0ef3bc7 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 aceade2a3a513..76c607ca4c4ee 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 7ac6b3d987c84..6adb8b59474db 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 06798413c8f40..e2d928fda24ed 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 97fa8c067331e..00ae005c38b14 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 a60061a3aa054..06fa31b87ce64 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 fd394aea90d66..6a3e0f40c48f4 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 fb9cb992fcf47..96413444d60c4 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 bb678a361e174..a0d137b90e84c 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 4b4d2275d0dec..163971c4ba9fb 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 43f5527e42d4b..84bcb48f95234 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 3389c723b4daf..69460c649c3e1 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 21c68a9fe1d82..10575f37dba6e 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/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index 83d86eb410b19..95c9140624e63 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/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index 134f0b4185b84..6c4d3631a12f3 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/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap index ea973df9aad76..f81ec6a73d140 100644 --- a/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap +++ b/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap @@ -1408,6 +1408,7 @@ exports[`UploadLicense should display an error when ES says license is expired 1 >