From 84c7886d559ed72af0222806dcdd05f340e75564 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Sat, 19 Sep 2020 05:02:35 +0200 Subject: [PATCH] Embeddable telemetry and reference extraction/injection (#74352) (#77911) --- ...na-plugin-plugins-data-public.esfilters.md | 2 +- ...lugin-plugins-data-public.filter._state.md | 11 -- ...ibana-plugin-plugins-data-public.filter.md | 17 +- ...-plugin-plugins-data-public.filter.meta.md | 11 -- ...plugin-plugins-data-public.filter.query.md | 11 -- ...ugin-plugins-data-public.query.language.md | 11 -- ...kibana-plugin-plugins-data-public.query.md | 17 +- ...-plugin-plugins-data-public.query.query.md | 13 -- ...ugin-plugins-data-public.timerange.from.md | 11 -- ...na-plugin-plugins-data-public.timerange.md | 17 +- ...ugin-plugins-data-public.timerange.mode.md | 11 -- ...plugin-plugins-data-public.timerange.to.md | 11 -- ...lugin-plugins-data-server.filter._state.md | 11 -- ...ibana-plugin-plugins-data-server.filter.md | 17 +- ...-plugin-plugins-data-server.filter.meta.md | 11 -- ...plugin-plugins-data-server.filter.query.md | 11 -- .../kibana-plugin-plugins-data-server.md | 6 +- ...ugin-plugins-data-server.query.language.md | 11 -- ...kibana-plugin-plugins-data-server.query.md | 17 +- ...-plugin-plugins-data-server.query.query.md | 13 -- ...ugin-plugins-data-server.timerange.from.md | 11 -- ...na-plugin-plugins-data-server.timerange.md | 17 +- ...ugin-plugins-data-server.timerange.mode.md | 11 -- ...plugin-plugins-data-server.timerange.to.md | 11 -- .../es_query/filters/get_display_value.ts | 2 +- .../common/es_query/filters/meta_filter.ts | 17 +- .../data/common/query/timefilter/types.ts | 5 +- src/plugins/data/common/query/types.ts | 5 +- src/plugins/data/public/public.api.md | 32 ++- .../lib/map_and_flatten_filters.test.ts | 4 +- .../filter_manager/lib/map_filter.test.ts | 4 +- src/plugins/data/server/server.api.md | 30 +-- src/plugins/embeddable/.eslintrc.json | 5 + .../common/lib/migrate_base_input.ts | 39 ++++ src/plugins/embeddable/common/types.ts | 70 +++++++ src/plugins/embeddable/kibana.json | 2 +- src/plugins/embeddable/public/index.ts | 2 + .../default_embeddable_factory_provider.ts | 4 + .../public/lib/embeddables/embeddable.tsx | 5 +- .../lib/embeddables/embeddable_factory.ts | 3 +- .../embeddable_factory_definition.ts | 3 + .../public/lib/embeddables/i_embeddable.ts | 46 +---- .../embeddables/filterable_container.tsx | 1 - .../embeddables/hello_world_container.tsx | 1 - src/plugins/embeddable/public/lib/types.ts | 7 +- src/plugins/embeddable/public/mocks.tsx | 4 + src/plugins/embeddable/public/plugin.test.ts | 102 ++++++++-- src/plugins/embeddable/public/plugin.tsx | 127 +++++++++++- src/plugins/embeddable/public/types.ts | 16 ++ src/plugins/embeddable/server/index.ts | 26 +++ src/plugins/embeddable/server/plugin.ts | 186 ++++++++++++++++++ src/plugins/embeddable/server/types.ts | 48 +++++ src/plugins/kibana_utils/common/index.ts | 1 + .../common/persistable_state/index.ts | 54 +++++ .../kibana_utils/public/ui/configurable.ts | 11 +- .../.eslintrc.json | 5 + .../dashboard_hello_world_drilldown/index.tsx | 4 +- .../index.tsx | 4 +- .../dashboard_to_discover_drilldown/types.ts | 4 +- .../dashboard_to_dashboard_drilldown/types.ts | 5 +- .../embeddables/embeddable_action_storage.ts | 7 +- .../ui_actions_enhanced/.eslintrc.json | 5 + .../ui_actions_enhanced/common/types.ts | 28 +++ .../plugins/ui_actions_enhanced/kibana.json | 2 +- .../action_wizard/action_wizard.tsx | 10 +- .../components/action_wizard/test_data.tsx | 19 +- ...nnected_flyout_manage_drilldowns.story.tsx | 5 +- ...onnected_flyout_manage_drilldowns.test.tsx | 6 +- .../connected_flyout_manage_drilldowns.tsx | 3 +- .../test_data.ts | 2 +- .../flyout_drilldown_wizard.story.tsx | 29 +-- .../flyout_drilldown_wizard.tsx | 24 ++- .../form_drilldown_wizard.tsx | 10 +- .../public/drilldowns/drilldown_definition.ts | 12 +- .../public/drilldowns/url_drilldown/types.ts | 4 +- .../public/dynamic_actions/action_factory.ts | 28 ++- .../action_factory_definition.ts | 13 +- .../dynamic_action_enhancement.ts | 28 +++ .../dynamic_action_manager.test.ts | 32 +-- .../dynamic_actions/dynamic_action_manager.ts | 9 +- .../public/dynamic_actions/types.ts | 16 +- .../ui_actions_enhanced/public/index.ts | 3 + .../ui_actions_enhanced/public/mocks.ts | 3 + .../ui_actions_enhanced/public/plugin.ts | 9 +- .../ui_actions_service_enhancements.test.ts | 60 ++++++ .../ui_actions_service_enhancements.ts | 60 +++++- .../test_helpers/time_range_container.ts | 1 - .../server/dynamic_action_enhancement.ts | 55 ++++++ .../ui_actions_enhanced/server/index.ts | 29 +++ .../ui_actions_enhanced/server/plugin.ts | 107 ++++++++++ .../ui_actions_enhanced/server/types.ts | 26 +++ 91 files changed, 1314 insertions(+), 505 deletions(-) delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter._state.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.meta.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.query.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.language.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.query.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.from.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.mode.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.to.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter._state.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.meta.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.query.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.language.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.query.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.from.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.mode.md delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.to.md create mode 100644 src/plugins/embeddable/.eslintrc.json create mode 100644 src/plugins/embeddable/common/lib/migrate_base_input.ts create mode 100644 src/plugins/embeddable/common/types.ts create mode 100644 src/plugins/embeddable/server/index.ts create mode 100644 src/plugins/embeddable/server/plugin.ts create mode 100644 src/plugins/embeddable/server/types.ts create mode 100644 src/plugins/kibana_utils/common/persistable_state/index.ts create mode 100644 x-pack/examples/ui_actions_enhanced_examples/.eslintrc.json create mode 100644 x-pack/plugins/ui_actions_enhanced/.eslintrc.json create mode 100644 x-pack/plugins/ui_actions_enhanced/common/types.ts create mode 100644 x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_enhancement.ts create mode 100644 x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts create mode 100644 x-pack/plugins/ui_actions_enhanced/server/index.ts create mode 100644 x-pack/plugins/ui_actions_enhanced/server/plugin.ts create mode 100644 x-pack/plugins/ui_actions_enhanced/server/types.ts 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 bc34d4113f847..4422b755faa77 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 @@ -35,7 +35,7 @@ esFilters: { type?: string | undefined; key?: string | undefined; params?: any; - value?: string | ((formatter?: import("../common").FilterValueFormatter | undefined) => string) | undefined; + value?: string | undefined; }; $state?: import("../common").FilterState | undefined; query?: any; diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter._state.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter._state.md deleted file mode 100644 index bfb5dff71e70d..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter._state.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Filter](./kibana-plugin-plugins-data-public.filter.md) > [$state](./kibana-plugin-plugins-data-public.filter._state.md) - -## Filter.$state property - -Signature: - -```typescript -$state?: FilterState; -``` 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 f993721ee96ad..9212b757e07df 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 @@ -2,19 +2,14 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Filter](./kibana-plugin-plugins-data-public.filter.md) -## Filter interface +## Filter type Signature: ```typescript -export interface Filter +export declare type Filter = { + $state?: FilterState; + meta: FilterMeta; + query?: any; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [$state](./kibana-plugin-plugins-data-public.filter._state.md) | FilterState | | -| [meta](./kibana-plugin-plugins-data-public.filter.meta.md) | FilterMeta | | -| [query](./kibana-plugin-plugins-data-public.filter.query.md) | any | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.meta.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.meta.md deleted file mode 100644 index 3385a3773a2aa..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.meta.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Filter](./kibana-plugin-plugins-data-public.filter.md) > [meta](./kibana-plugin-plugins-data-public.filter.meta.md) - -## Filter.meta property - -Signature: - -```typescript -meta: FilterMeta; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.query.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.query.md deleted file mode 100644 index 083b544493e80..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.query.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Filter](./kibana-plugin-plugins-data-public.filter.md) > [query](./kibana-plugin-plugins-data-public.filter.query.md) - -## Filter.query property - -Signature: - -```typescript -query?: any; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.language.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.language.md deleted file mode 100644 index 127ee9210799e..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.language.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Query](./kibana-plugin-plugins-data-public.query.md) > [language](./kibana-plugin-plugins-data-public.query.language.md) - -## Query.language property - -Signature: - -```typescript -language: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.md index a1dffe5ff5fa4..e15b04236a0b5 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.md @@ -2,18 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Query](./kibana-plugin-plugins-data-public.query.md) -## Query interface +## Query type Signature: ```typescript -export interface Query +export declare type Query = { + query: string | { + [key: string]: any; + }; + language: string; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [language](./kibana-plugin-plugins-data-public.query.language.md) | string | | -| [query](./kibana-plugin-plugins-data-public.query.query.md) | string | {
[key: string]: any;
} | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.query.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.query.md deleted file mode 100644 index 9fcd0310af0fe..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.query.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Query](./kibana-plugin-plugins-data-public.query.md) > [query](./kibana-plugin-plugins-data-public.query.query.md) - -## Query.query property - -Signature: - -```typescript -query: string | { - [key: string]: any; - }; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.from.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.from.md deleted file mode 100644 index b428bd9cd90ca..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.from.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) > [from](./kibana-plugin-plugins-data-public.timerange.from.md) - -## TimeRange.from property - -Signature: - -```typescript -from: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.md index 69078ca40d20d..482501e494c7a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.md @@ -2,19 +2,14 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) -## TimeRange interface +## TimeRange type Signature: ```typescript -export interface TimeRange +export declare type TimeRange = { + from: string; + to: string; + mode?: 'absolute' | 'relative'; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [from](./kibana-plugin-plugins-data-public.timerange.from.md) | string | | -| [mode](./kibana-plugin-plugins-data-public.timerange.mode.md) | 'absolute' | 'relative' | | -| [to](./kibana-plugin-plugins-data-public.timerange.to.md) | string | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.mode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.mode.md deleted file mode 100644 index fb9ebd3c9165f..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.mode.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) > [mode](./kibana-plugin-plugins-data-public.timerange.mode.md) - -## TimeRange.mode property - -Signature: - -```typescript -mode?: 'absolute' | 'relative'; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.to.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.to.md deleted file mode 100644 index 342acd5e049f1..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timerange.to.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) > [to](./kibana-plugin-plugins-data-public.timerange.to.md) - -## TimeRange.to property - -Signature: - -```typescript -to: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter._state.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter._state.md deleted file mode 100644 index 079f352609a70..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter._state.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [Filter](./kibana-plugin-plugins-data-server.filter.md) > [$state](./kibana-plugin-plugins-data-server.filter._state.md) - -## Filter.$state property - -Signature: - -```typescript -$state?: FilterState; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.md index 4e4c49b222f01..519bbaf8f9416 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.md @@ -2,19 +2,14 @@ [Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [Filter](./kibana-plugin-plugins-data-server.filter.md) -## Filter interface +## Filter type Signature: ```typescript -export interface Filter +export declare type Filter = { + $state?: FilterState; + meta: FilterMeta; + query?: any; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [$state](./kibana-plugin-plugins-data-server.filter._state.md) | FilterState | | -| [meta](./kibana-plugin-plugins-data-server.filter.meta.md) | FilterMeta | | -| [query](./kibana-plugin-plugins-data-server.filter.query.md) | any | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.meta.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.meta.md deleted file mode 100644 index 6d11804704d82..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.meta.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [Filter](./kibana-plugin-plugins-data-server.filter.md) > [meta](./kibana-plugin-plugins-data-server.filter.meta.md) - -## Filter.meta property - -Signature: - -```typescript -meta: FilterMeta; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.query.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.query.md deleted file mode 100644 index 942c7930f449d..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.query.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [Filter](./kibana-plugin-plugins-data-server.filter.md) > [query](./kibana-plugin-plugins-data-server.filter.query.md) - -## Filter.query property - -Signature: - -```typescript -query?: any; -``` 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 f5b587d86b349..3c477e17503f4 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 @@ -42,7 +42,6 @@ | [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) | | | [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | | -| [Filter](./kibana-plugin-plugins-data-server.filter.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) | | | [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) | | | [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | | @@ -58,12 +57,10 @@ | [OptionedValueProp](./kibana-plugin-plugins-data-server.optionedvalueprop.md) | | | [PluginSetup](./kibana-plugin-plugins-data-server.pluginsetup.md) | | | [PluginStart](./kibana-plugin-plugins-data-server.pluginstart.md) | | -| [Query](./kibana-plugin-plugins-data-server.query.md) | | | [RefreshInterval](./kibana-plugin-plugins-data-server.refreshinterval.md) | | | [SearchUsage](./kibana-plugin-plugins-data-server.searchusage.md) | | | [TabbedAggColumn](./kibana-plugin-plugins-data-server.tabbedaggcolumn.md) | \* | | [TabbedTable](./kibana-plugin-plugins-data-server.tabbedtable.md) | \* | -| [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) | | ## Variables @@ -91,11 +88,14 @@ | [AggParam](./kibana-plugin-plugins-data-server.aggparam.md) | | | [EsaggsExpressionFunctionDefinition](./kibana-plugin-plugins-data-server.esaggsexpressionfunctiondefinition.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) | | | [IFieldFormatsRegistry](./kibana-plugin-plugins-data-server.ifieldformatsregistry.md) | | | [IFieldParamType](./kibana-plugin-plugins-data-server.ifieldparamtype.md) | | | [IMetricAggType](./kibana-plugin-plugins-data-server.imetricaggtype.md) | | | [ParsedInterval](./kibana-plugin-plugins-data-server.parsedinterval.md) | | +| [Query](./kibana-plugin-plugins-data-server.query.md) | | | [TabbedAggRow](./kibana-plugin-plugins-data-server.tabbedaggrow.md) | \* | +| [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.language.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.language.md deleted file mode 100644 index 384fc77d801c0..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.language.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [Query](./kibana-plugin-plugins-data-server.query.md) > [language](./kibana-plugin-plugins-data-server.query.language.md) - -## Query.language property - -Signature: - -```typescript -language: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.md index 5d61c75bc5e99..6a7bdfe51f1c0 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.md @@ -2,18 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [Query](./kibana-plugin-plugins-data-server.query.md) -## Query interface +## Query type Signature: ```typescript -export interface Query +export declare type Query = { + query: string | { + [key: string]: any; + }; + language: string; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [language](./kibana-plugin-plugins-data-server.query.language.md) | string | | -| [query](./kibana-plugin-plugins-data-server.query.query.md) | string | {
[key: string]: any;
} | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.query.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.query.md deleted file mode 100644 index 5c2aa700bc603..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.query.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [Query](./kibana-plugin-plugins-data-server.query.md) > [query](./kibana-plugin-plugins-data-server.query.query.md) - -## Query.query property - -Signature: - -```typescript -query: string | { - [key: string]: any; - }; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.from.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.from.md deleted file mode 100644 index b6f40cc2e4203..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.from.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) > [from](./kibana-plugin-plugins-data-server.timerange.from.md) - -## TimeRange.from property - -Signature: - -```typescript -from: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.md index 8280d924eb609..1ac59343220fd 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.md @@ -2,19 +2,14 @@ [Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) -## TimeRange interface +## TimeRange type Signature: ```typescript -export interface TimeRange +export declare type TimeRange = { + from: string; + to: string; + mode?: 'absolute' | 'relative'; +}; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [from](./kibana-plugin-plugins-data-server.timerange.from.md) | string | | -| [mode](./kibana-plugin-plugins-data-server.timerange.mode.md) | 'absolute' | 'relative' | | -| [to](./kibana-plugin-plugins-data-server.timerange.to.md) | string | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.mode.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.mode.md deleted file mode 100644 index 1408fb43cbf39..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.mode.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) > [mode](./kibana-plugin-plugins-data-server.timerange.mode.md) - -## TimeRange.mode property - -Signature: - -```typescript -mode?: 'absolute' | 'relative'; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.to.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.to.md deleted file mode 100644 index 98aca5474d350..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.timerange.to.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) > [to](./kibana-plugin-plugins-data-server.timerange.to.md) - -## TimeRange.to property - -Signature: - -```typescript -to: string; -``` diff --git a/src/plugins/data/common/es_query/filters/get_display_value.ts b/src/plugins/data/common/es_query/filters/get_display_value.ts index 28ba0ab629e8f..317d0f0140293 100644 --- a/src/plugins/data/common/es_query/filters/get_display_value.ts +++ b/src/plugins/data/common/es_query/filters/get_display_value.ts @@ -43,7 +43,7 @@ export function getDisplayValueFromFilter(filter: Filter, indexPatterns: IIndexP if (typeof filter.meta.value === 'function') { const indexPattern = getIndexPatternFromFilter(filter, indexPatterns); const valueFormatter: any = getValueFormatter(indexPattern, filter.meta.key); - return filter.meta.value(valueFormatter); + return (filter.meta.value as any)(valueFormatter); } else { return filter.meta.value || ''; } diff --git a/src/plugins/data/common/es_query/filters/meta_filter.ts b/src/plugins/data/common/es_query/filters/meta_filter.ts index e3099ae6a4026..1e892d452f401 100644 --- a/src/plugins/data/common/es_query/filters/meta_filter.ts +++ b/src/plugins/data/common/es_query/filters/meta_filter.ts @@ -22,9 +22,10 @@ export enum FilterStateStore { GLOBAL_STATE = 'globalState', } -export interface FilterState { +// eslint-disable-next-line +export type FilterState = { store: FilterStateStore; -} +}; type FilterFormatterFunction = (value: any) => string; export interface FilterValueFormatter { @@ -32,7 +33,8 @@ export interface FilterValueFormatter { getConverterFor: (type: string) => FilterFormatterFunction; } -export interface FilterMeta { +// eslint-disable-next-line +export type FilterMeta = { alias: string | null; disabled: boolean; negate: boolean; @@ -43,14 +45,15 @@ export interface FilterMeta { type?: string; key?: string; params?: any; - value?: string | ((formatter?: FilterValueFormatter) => string); -} + value?: string; +}; -export interface Filter { +// eslint-disable-next-line +export type Filter = { $state?: FilterState; meta: FilterMeta; query?: any; -} +}; export interface LatLon { lat: number; diff --git a/src/plugins/data/common/query/timefilter/types.ts b/src/plugins/data/common/query/timefilter/types.ts index 60008ce6054e1..82b1ae69cc73b 100644 --- a/src/plugins/data/common/query/timefilter/types.ts +++ b/src/plugins/data/common/query/timefilter/types.ts @@ -24,11 +24,12 @@ export interface RefreshInterval { value: number; } -export interface TimeRange { +// eslint-disable-next-line +export type TimeRange = { from: string; to: string; mode?: 'absolute' | 'relative'; -} +}; export interface TimeRangeBounds { min: Moment | undefined; diff --git a/src/plugins/data/common/query/types.ts b/src/plugins/data/common/query/types.ts index 6b34a1baf293b..c1a98eac5350e 100644 --- a/src/plugins/data/common/query/types.ts +++ b/src/plugins/data/common/query/types.ts @@ -19,7 +19,8 @@ export * from './timefilter/types'; -export interface Query { +// eslint-disable-next-line +export type Query = { query: string | { [key: string]: any }; language: string; -} +}; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 3cc8e8bba887d..82b21dd9a451a 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -566,7 +566,7 @@ export const esFilters: { type?: string | undefined; key?: string | undefined; params?: any; - value?: string | ((formatter?: import("../common").FilterValueFormatter | undefined) => string) | undefined; + value?: string | undefined; }; $state?: import("../common").FilterState | undefined; query?: any; @@ -791,18 +791,11 @@ export interface FieldMappingSpec { // Warning: (ae-missing-release-tag) "Filter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface Filter { - // Warning: (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts - // - // (undocumented) +export type Filter = { $state?: FilterState; - // Warning: (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts - // - // (undocumented) meta: FilterMeta; - // (undocumented) query?: any; -} +}; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "FilterBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1653,14 +1646,12 @@ export function plugin(initializerContext: PluginInitializerContext; // Warning: (ae-missing-release-tag) "TimeRange" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface TimeRange { - // (undocumented) +export type TimeRange = { from: string; - // (undocumented) - mode?: 'absolute' | 'relative'; - // (undocumented) to: string; -} + mode?: 'absolute' | 'relative'; +}; // Warning: (ae-missing-release-tag) "UI_SETTINGS" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2205,6 +2193,8 @@ export const UI_SETTINGS: { // src/plugins/data/common/es_query/filters/exists_filter.ts:30:3 - (ae-forgotten-export) The symbol "ExistsFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/exists_filter.ts:31:3 - (ae-forgotten-export) The symbol "FilterExistsProperty" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/match_all_filter.ts:28:3 - (ae-forgotten-export) The symbol "MatchAllFilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:53:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:54:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrase_filter.ts:33:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrases_filter.ts:31:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/aggs/types.ts:98:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts index 1b2d476570902..996a7aaa27c31 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts @@ -25,7 +25,9 @@ describe('filter manager utilities', () => { let filters: unknown; function getDisplayName(filter: Filter) { - return typeof filter.meta.value === 'function' ? filter.meta.value() : filter.meta.value; + return typeof filter.meta.value === 'function' + ? (filter.meta.value as any)() + : filter.meta.value; } beforeEach(() => { diff --git a/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts b/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts index 35d2f2b7b294e..7b303ca4d5314 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts @@ -22,7 +22,9 @@ import { Filter } from '../../../../common'; describe('filter manager utilities', () => { function getDisplayName(filter: Filter) { - return typeof filter.meta.value === 'function' ? filter.meta.value() : filter.meta.value; + return typeof filter.meta.value === 'function' + ? (filter.meta.value as any)() + : filter.meta.value; } describe('mapFilter()', () => { diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index bc626b62b6409..727d23f70ad91 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -440,18 +440,11 @@ 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 (undocumented) -export interface Filter { - // Warning: (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts - // - // (undocumented) +export type Filter = { $state?: FilterState; - // Warning: (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts - // - // (undocumented) meta: FilterMeta; - // (undocumented) query?: any; -} +}; // Warning: (ae-forgotten-export) The symbol "IUiSettingsClient" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "getDefaultSearchParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -943,14 +936,12 @@ export interface PluginStart { // Warning: (ae-missing-release-tag) "Query" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface Query { - // (undocumented) - language: string; - // (undocumented) +export type Query = { query: string | { [key: string]: any; }; -} + language: string; +}; // Warning: (ae-missing-release-tag) "RefreshInterval" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1065,14 +1056,11 @@ export interface TabbedTable { // Warning: (ae-missing-release-tag) "TimeRange" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface TimeRange { - // (undocumented) +export type TimeRange = { from: string; - // (undocumented) - mode?: 'absolute' | 'relative'; - // (undocumented) to: string; -} + mode?: 'absolute' | 'relative'; +}; // Warning: (ae-missing-release-tag) "toSnakeCase" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1122,6 +1110,8 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // Warnings were encountered during analysis: // +// src/plugins/data/common/es_query/filters/meta_filter.ts:53:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/es_query/filters/meta_filter.ts:54:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/fields/types.ts:41:25 - (ae-forgotten-export) The symbol "IndexPattern" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:40:23 - (ae-forgotten-export) The symbol "buildCustomFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:40:23 - (ae-forgotten-export) The symbol "buildFilter" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/embeddable/.eslintrc.json b/src/plugins/embeddable/.eslintrc.json new file mode 100644 index 0000000000000..2aab6c2d9093b --- /dev/null +++ b/src/plugins/embeddable/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "@typescript-eslint/consistent-type-definitions": 0 + } +} diff --git a/src/plugins/embeddable/common/lib/migrate_base_input.ts b/src/plugins/embeddable/common/lib/migrate_base_input.ts new file mode 100644 index 0000000000000..0d5dc508e20ad --- /dev/null +++ b/src/plugins/embeddable/common/lib/migrate_base_input.ts @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectReference } from '../../../../core/types'; +import { EmbeddableInput } from '../types'; + +export const telemetryBaseEmbeddableInput = ( + state: EmbeddableInput, + telemetryData: Record +) => { + return telemetryData; +}; + +export const extractBaseEmbeddableInput = (state: EmbeddableInput) => { + return { state, references: [] as SavedObjectReference[] }; +}; + +export const injectBaseEmbeddableInput = ( + state: EmbeddableInput, + references: SavedObjectReference[] +) => { + return state; +}; diff --git a/src/plugins/embeddable/common/types.ts b/src/plugins/embeddable/common/types.ts new file mode 100644 index 0000000000000..68b842c934de8 --- /dev/null +++ b/src/plugins/embeddable/common/types.ts @@ -0,0 +1,70 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SerializableState } from '../../kibana_utils/common'; +import { Query, TimeRange } from '../../data/common/query'; +import { Filter } from '../../data/common/es_query/filters'; + +export enum ViewMode { + EDIT = 'edit', + VIEW = 'view', +} + +export type EmbeddableInput = { + viewMode?: ViewMode; + title?: string; + /** + * Note this is not a saved object id. It is used to uniquely identify this + * Embeddable instance from others (e.g. inside a container). It's possible to + * have two Embeddables where everything else is the same but the id. + */ + id: string; + lastReloadRequestTime?: number; + hidePanelTitles?: boolean; + + /** + * Reserved key for enhancements added by other plugins. + */ + enhancements?: SerializableState; + + /** + * List of action IDs that this embeddable should not render. + */ + disabledActions?: string[]; + + /** + * Whether this embeddable should not execute triggers. + */ + disableTriggers?: boolean; + + /** + * Time range of the chart. + */ + timeRange?: TimeRange; + + /** + * Visualization query string used to narrow down results. + */ + query?: Query; + + /** + * Visualization filters used to narrow down results. + */ + filters?: Filter[]; +}; diff --git a/src/plugins/embeddable/kibana.json b/src/plugins/embeddable/kibana.json index d597920ca7f0d..cc579b8050782 100644 --- a/src/plugins/embeddable/kibana.json +++ b/src/plugins/embeddable/kibana.json @@ -1,7 +1,7 @@ { "id": "embeddable", "version": "kibana", - "server": false, + "server": true, "ui": true, "requiredPlugins": [ "inspector", diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index 57253c1f741ab..c5d8853ada5e8 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -78,6 +78,8 @@ export { EmbeddableRendererProps, } from './lib'; +export { EnhancementRegistryDefinition } from './types'; + export function plugin(initializerContext: PluginInitializerContext) { return new EmbeddablePublicPlugin(initializerContext); } diff --git a/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts b/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts index b22f16c94aff8..e2047dca1f770 100644 --- a/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts +++ b/src/plugins/embeddable/public/lib/embeddables/default_embeddable_factory_provider.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ + import { SavedObjectAttributes } from 'kibana/public'; import { EmbeddableFactoryDefinition } from './embeddable_factory_definition'; import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable'; @@ -47,6 +48,9 @@ export const defaultEmbeddableFactoryProvider = < isEditable: def.isEditable.bind(def), getDisplayName: def.getDisplayName.bind(def), savedObjectMetaData: def.savedObjectMetaData, + telemetry: def.telemetry || (() => ({})), + inject: def.inject || ((state: EmbeddableInput) => state), + extract: def.extract || ((state: EmbeddableInput) => ({ state, references: [] })), }; return factory; }; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx index ffe8a5bf6e7dc..9267d600360cf 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx @@ -21,10 +21,11 @@ import { cloneDeep, isEqual } from 'lodash'; import * as Rx from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; import { RenderCompleteDispatcher } from '../../../../kibana_utils/public'; -import { Adapters, ViewMode } from '../types'; +import { Adapters } from '../types'; import { IContainer } from '../containers'; -import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable'; +import { EmbeddableOutput, IEmbeddable } from './i_embeddable'; import { TriggerContextMapping } from '../ui_actions'; +import { EmbeddableInput, ViewMode } from '../../../common/types'; function getPanelTitle(input: EmbeddableInput, output: EmbeddableOutput) { return input.hidePanelTitles ? '' : input.title === undefined ? output.defaultTitle : input.title; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts index 7949b6fb8ba27..a6fa46fbc4e3e 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory.ts @@ -23,6 +23,7 @@ import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable'; import { ErrorEmbeddable } from './error_embeddable'; import { IContainer } from '../containers/i_container'; import { PropertySpec } from '../types'; +import { PersistableState } from '../../../../kibana_utils/common'; export interface EmbeddableInstanceConfiguration { id: string; @@ -44,7 +45,7 @@ export interface EmbeddableFactory< TEmbeddableOutput >, TSavedObjectAttributes extends SavedObjectAttributes = SavedObjectAttributes -> { +> extends PersistableState { // A unique identified for this factory, which will be used to map an embeddable spec to // a factory that can generate an instance of it. readonly type: string; diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts index b8985f7311ea9..224a11a201b88 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_factory_definition.ts @@ -40,5 +40,8 @@ export type EmbeddableFactoryDefinition< | 'savedObjectMetaData' | 'canCreateNew' | 'getDefaultInput' + | 'telemetry' + | 'extract' + | 'inject' > >; diff --git a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts index e8aecdba0abc4..3843950c164c9 100644 --- a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts +++ b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts @@ -20,57 +20,15 @@ import { Observable } from 'rxjs'; import { Adapters } from '../types'; import { IContainer } from '../containers/i_container'; -import { ViewMode } from '../types'; import { TriggerContextMapping } from '../../../../ui_actions/public'; -import type { TimeRange, Query, Filter } from '../../../../data/common'; +import { EmbeddableInput } from '../../../common/types'; export interface EmbeddableError { name: string; message: string; } -export interface EmbeddableInput { - viewMode?: ViewMode; - title?: string; - /** - * Note this is not a saved object id. It is used to uniquely identify this - * Embeddable instance from others (e.g. inside a container). It's possible to - * have two Embeddables where everything else is the same but the id. - */ - id: string; - lastReloadRequestTime?: number; - hidePanelTitles?: boolean; - - /** - * Reserved key for enhancements added by other plugins. - */ - enhancements?: unknown; - - /** - * List of action IDs that this embeddable should not render. - */ - disabledActions?: string[]; - - /** - * Whether this embeddable should not execute triggers. - */ - disableTriggers?: boolean; - - /** - * Time range of the chart. - */ - timeRange?: TimeRange; - - /** - * Visualization query string used to narrow down results. - */ - query?: Query; - - /** - * Visualization filters used to narrow down results. - */ - filters?: Filter[]; -} +export { EmbeddableInput }; export interface EmbeddableOutput { // Whether the embeddable is actively loading. diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx index ceaa74218904d..db71b94ac855f 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/filterable_container.tsx @@ -32,7 +32,6 @@ export interface FilterableContainerInput extends ContainerInput { * https://github.com/microsoft/TypeScript/issues/15300 is fixed so we use a type * here instead */ -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type InheritedChildrenInput = { filters: Filter[]; id?: string; diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx index 913c3a0b30826..d47979b9419f3 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/hello_world_container.tsx @@ -30,7 +30,6 @@ export const HELLO_WORLD_CONTAINER = 'HELLO_WORLD_CONTAINER'; * https://github.com/microsoft/TypeScript/issues/15300 is fixed so we use a type * here instead */ -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions type InheritedInput = { id: string; viewMode: ViewMode; diff --git a/src/plugins/embeddable/public/lib/types.ts b/src/plugins/embeddable/public/lib/types.ts index 1cfff7baca186..7fe189dea2381 100644 --- a/src/plugins/embeddable/public/lib/types.ts +++ b/src/plugins/embeddable/public/lib/types.ts @@ -32,10 +32,5 @@ export interface PropertySpec { description: string; value?: string; } - -export enum ViewMode { - EDIT = 'edit', - VIEW = 'view', -} - +export { ViewMode } from '../../common/types'; export { Adapters }; diff --git a/src/plugins/embeddable/public/mocks.tsx b/src/plugins/embeddable/public/mocks.tsx index 2064236e9ae7f..26c10121adb3d 100644 --- a/src/plugins/embeddable/public/mocks.tsx +++ b/src/plugins/embeddable/public/mocks.tsx @@ -109,6 +109,7 @@ export const mockRefOrValEmbeddable = < const createSetupContract = (): Setup => { const setupContract: Setup = { registerEmbeddableFactory: jest.fn(), + registerEnhancement: jest.fn(), setCustomEmbeddableFactoryProvider: jest.fn(), }; return setupContract; @@ -118,6 +119,9 @@ const createStartContract = (): Start => { const startContract: Start = { getEmbeddableFactories: jest.fn(), getEmbeddableFactory: jest.fn(), + telemetry: jest.fn(), + extract: jest.fn(), + inject: jest.fn(), EmbeddablePanel: jest.fn(), getEmbeddablePanel: jest.fn(), getStateTransfer: jest.fn(() => createEmbeddableStateTransferMock() as EmbeddableStateTransfer), diff --git a/src/plugins/embeddable/public/plugin.test.ts b/src/plugins/embeddable/public/plugin.test.ts index e37d602ad8cac..5fd3bcdd61318 100644 --- a/src/plugins/embeddable/public/plugin.test.ts +++ b/src/plugins/embeddable/public/plugin.test.ts @@ -22,21 +22,6 @@ import { EmbeddableFactoryProvider } from './types'; import { defaultEmbeddableFactoryProvider } from './lib'; import { HelloWorldEmbeddable } from '../../../../examples/embeddable_examples/public'; -test('cannot register embeddable factory with the same ID', async () => { - const coreSetup = coreMock.createSetup(); - const coreStart = coreMock.createStart(); - const { setup } = testPlugin(coreSetup, coreStart); - const embeddableFactoryId = 'ID'; - const embeddableFactory = {} as any; - - setup.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory); - expect(() => - setup.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory) - ).toThrowError( - 'Embeddable factory [embeddableFactoryId = ID] already registered in Embeddables API.' - ); -}); - test('can set custom embeddable factory provider', async () => { const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); @@ -108,3 +93,90 @@ test('custom embeddable factory provider test for intercepting embeddable creati await new Promise((resolve) => process.nextTick(resolve)); expect(updateCount).toEqual(0); }); + +describe('embeddable factory', () => { + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createStart(); + const { setup, doStart } = testPlugin(coreSetup, coreStart); + const start = doStart(); + const embeddableFactoryId = 'ID'; + const embeddableFactory = { + type: embeddableFactoryId, + create: jest.fn(), + getDisplayName: () => 'Test', + isEditable: () => Promise.resolve(true), + extract: jest.fn().mockImplementation((state) => ({ state, references: [] })), + inject: jest.fn().mockImplementation((state) => state), + telemetry: jest.fn().mockResolvedValue({}), + } as any; + const embeddableState = { + id: embeddableFactoryId, + my: 'state', + } as any; + + test('cannot register embeddable factory with the same ID', async () => { + setup.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory); + expect(() => + setup.registerEmbeddableFactory(embeddableFactoryId, embeddableFactory) + ).toThrowError( + 'Embeddable factory [embeddableFactoryId = ID] already registered in Embeddables API.' + ); + }); + + test('embeddableFactory extract function gets called when calling embeddable extract', () => { + start.extract(embeddableState); + expect(embeddableFactory.extract).toBeCalledWith(embeddableState); + }); + + test('embeddableFactory inject function gets called when calling embeddable inject', () => { + start.inject(embeddableState, []); + expect(embeddableFactory.extract).toBeCalledWith(embeddableState); + }); + + test('embeddableFactory telemetry function gets called when calling embeddable telemetry', () => { + start.telemetry(embeddableState, {}); + expect(embeddableFactory.telemetry).toBeCalledWith(embeddableState, {}); + }); +}); + +describe('embeddable enhancements', () => { + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createStart(); + const { setup, doStart } = testPlugin(coreSetup, coreStart); + const start = doStart(); + const embeddableEnhancement = { + id: 'test', + extract: jest.fn().mockImplementation((state) => ({ state, references: [] })), + inject: jest.fn().mockImplementation((state) => state), + telemetry: jest.fn().mockResolvedValue({}), + } as any; + const embeddableState = { + enhancements: { + test: { + my: 'state', + }, + }, + } as any; + + test('cannot register embeddable enhancement with the same ID', async () => { + setup.registerEnhancement(embeddableEnhancement); + expect(() => setup.registerEnhancement(embeddableEnhancement)).toThrowError( + 'enhancement with id test already exists in the registry' + ); + }); + + test('enhancement extract function gets called when calling embeddable extract', () => { + start.extract(embeddableState); + expect(embeddableEnhancement.extract).toBeCalledWith(embeddableState.enhancements.test); + }); + + test('enhancement inject function gets called when calling embeddable inject', () => { + start.inject(embeddableState, []); + expect(embeddableEnhancement.extract).toBeCalledWith(embeddableState.enhancements.test); + }); + + test('enhancement telemetry function gets called when calling embeddable telemetry', () => { + start.telemetry(embeddableState, {}); + expect(embeddableEnhancement.telemetry).toBeCalledWith(embeddableState.enhancements.test, {}); + }); +}); diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index 2ca31994b722d..00eb923c26662 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -18,6 +18,7 @@ */ import React from 'react'; import { Subscription } from 'rxjs'; +import { identity } from 'lodash'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../data/public'; import { getSavedObjectFinder } from '../../saved_objects/public'; import { UiActionsSetup, UiActionsStart } from '../../ui_actions/public'; @@ -29,8 +30,15 @@ import { Plugin, ScopedHistory, PublicAppInfo, + SavedObjectReference, } from '../../../core/public'; -import { EmbeddableFactoryRegistry, EmbeddableFactoryProvider } from './types'; +import { + EmbeddableFactoryRegistry, + EmbeddableFactoryProvider, + EnhancementsRegistry, + EnhancementRegistryDefinition, + EnhancementRegistryItem, +} from './types'; import { bootstrap } from './bootstrap'; import { EmbeddableFactory, @@ -42,6 +50,12 @@ import { } from './lib'; import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition'; import { EmbeddableStateTransfer } from './lib/state_transfer'; +import { + extractBaseEmbeddableInput, + injectBaseEmbeddableInput, + telemetryBaseEmbeddableInput, +} from '../common/lib/migrate_base_input'; +import { PersistableState, SerializableState } from '../../kibana_utils/common'; export interface EmbeddableSetupDependencies { data: DataPublicPluginSetup; @@ -63,10 +77,11 @@ export interface EmbeddableSetup { id: string, factory: EmbeddableFactoryDefinition ) => () => EmbeddableFactory; + registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void; setCustomEmbeddableFactoryProvider: (customProvider: EmbeddableFactoryProvider) => void; } -export interface EmbeddableStart { +export interface EmbeddableStart extends PersistableState { getEmbeddableFactory: < I extends EmbeddableInput = EmbeddableInput, O extends EmbeddableOutput = EmbeddableOutput, @@ -88,6 +103,7 @@ export class EmbeddablePublicPlugin implements Plugin = new Map(); private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map(); + private readonly enhancements: EnhancementsRegistry = new Map(); private customEmbeddableFactoryProvider?: EmbeddableFactoryProvider; private outgoingOnlyStateTransfer: EmbeddableStateTransfer = {} as EmbeddableStateTransfer; private isRegistryReady = false; @@ -101,6 +117,7 @@ export class EmbeddablePublicPlugin implements Plugin { if (this.customEmbeddableFactoryProvider) { throw new Error( @@ -168,6 +185,9 @@ export class EmbeddablePublicPlugin implements Plugin = {}) => { + const enhancements: Record = state.enhancements || {}; + const factory = this.getEmbeddableFactory(state.id); + + let telemetry = telemetryBaseEmbeddableInput(state, telemetryData); + if (factory) { + telemetry = factory.telemetry(state, telemetry); + } + Object.keys(enhancements).map((key) => { + if (!enhancements[key]) return; + telemetry = this.getEnhancement(key).telemetry(enhancements[key], telemetry); + }); + + return telemetry; + }; + + private extract = (state: EmbeddableInput) => { + const enhancements = state.enhancements || {}; + const factory = this.getEmbeddableFactory(state.id); + + const baseResponse = extractBaseEmbeddableInput(state); + let updatedInput = baseResponse.state; + const refs = baseResponse.references; + + if (factory) { + const factoryResponse = factory.extract(state); + updatedInput = factoryResponse.state; + refs.push(...factoryResponse.references); + } + + updatedInput.enhancements = {}; + Object.keys(enhancements).forEach((key) => { + if (!enhancements[key]) return; + const enhancementResult = this.getEnhancement(key).extract( + enhancements[key] as SerializableState + ); + refs.push(...enhancementResult.references); + updatedInput.enhancements![key] = enhancementResult.state; + }); + + return { + state: updatedInput, + references: refs, + }; + }; + + private inject = (state: EmbeddableInput, references: SavedObjectReference[]) => { + const enhancements = state.enhancements || {}; + const factory = this.getEmbeddableFactory(state.id); + + let updatedInput = injectBaseEmbeddableInput(state, references); + + if (factory) { + updatedInput = factory.inject(updatedInput, references); + } + + updatedInput.enhancements = {}; + Object.keys(enhancements).forEach((key) => { + if (!enhancements[key]) return; + updatedInput.enhancements![key] = this.getEnhancement(key).inject( + enhancements[key] as SerializableState, + references + ); + }); + + return updatedInput; + }; + + private registerEnhancement = (enhancement: EnhancementRegistryDefinition) => { + if (this.enhancements.has(enhancement.id)) { + throw new Error(`enhancement with id ${enhancement.id} already exists in the registry`); + } + this.enhancements.set(enhancement.id, { + id: enhancement.id, + telemetry: enhancement.telemetry || (() => ({})), + inject: enhancement.inject || identity, + extract: + enhancement.extract || + ((state: SerializableState) => { + return { state, references: [] }; + }), + }); + }; + + private getEnhancement = (id: string): EnhancementRegistryItem => { + return ( + this.enhancements.get(id) || { + id: 'unknown', + telemetry: () => ({}), + inject: identity, + extract: (state: SerializableState) => { + return { state, references: [] }; + }, + } + ); + }; + private getEmbeddableFactories = () => { this.ensureFactoriesExist(); return this.embeddableFactories.values(); @@ -215,12 +332,6 @@ export class EmbeddablePublicPlugin implements Plugin; }; diff --git a/src/plugins/embeddable/public/types.ts b/src/plugins/embeddable/public/types.ts index 2d112b2359818..c5148bbaefb6b 100644 --- a/src/plugins/embeddable/public/types.ts +++ b/src/plugins/embeddable/public/types.ts @@ -25,8 +25,24 @@ import { IEmbeddable, EmbeddableFactoryDefinition, } from './lib/embeddables'; +import { + PersistableState, + PersistableStateDefinition, + SerializableState, +} from '../../kibana_utils/common'; export type EmbeddableFactoryRegistry = Map; +export type EnhancementsRegistry = Map; + +export interface EnhancementRegistryDefinition

+ extends PersistableStateDefinition

{ + id: string; +} + +export interface EnhancementRegistryItem

+ extends PersistableState

{ + id: string; +} export type EmbeddableFactoryProvider = < I extends EmbeddableInput = EmbeddableInput, diff --git a/src/plugins/embeddable/server/index.ts b/src/plugins/embeddable/server/index.ts new file mode 100644 index 0000000000000..1138478bff4b7 --- /dev/null +++ b/src/plugins/embeddable/server/index.ts @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { EmbeddableServerPlugin, EmbeddableSetup } from './plugin'; + +export { EmbeddableSetup }; + +export { EnhancementRegistryDefinition, EmbeddableRegistryDefinition } from './types'; + +export const plugin = () => new EmbeddableServerPlugin(); diff --git a/src/plugins/embeddable/server/plugin.ts b/src/plugins/embeddable/server/plugin.ts new file mode 100644 index 0000000000000..f79c4b7620110 --- /dev/null +++ b/src/plugins/embeddable/server/plugin.ts @@ -0,0 +1,186 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreSetup, CoreStart, Plugin, SavedObjectReference } from 'kibana/server'; +import { identity } from 'lodash'; +import { + EmbeddableFactoryRegistry, + EnhancementsRegistry, + EnhancementRegistryDefinition, + EnhancementRegistryItem, + EmbeddableRegistryDefinition, +} from './types'; +import { + extractBaseEmbeddableInput, + injectBaseEmbeddableInput, + telemetryBaseEmbeddableInput, +} from '../common/lib/migrate_base_input'; +import { SerializableState } from '../../kibana_utils/common'; +import { EmbeddableInput } from '../common/types'; + +export interface EmbeddableSetup { + registerEmbeddableFactory: (factory: EmbeddableRegistryDefinition) => void; + registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void; +} + +export class EmbeddableServerPlugin implements Plugin { + private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map(); + private readonly enhancements: EnhancementsRegistry = new Map(); + + public setup(core: CoreSetup) { + return { + registerEmbeddableFactory: this.registerEmbeddableFactory, + registerEnhancement: this.registerEnhancement, + }; + } + + public start(core: CoreStart) { + return { + telemetry: this.telemetry, + extract: this.extract, + inject: this.inject, + }; + } + + public stop() {} + + private telemetry = (state: EmbeddableInput, telemetryData: Record = {}) => { + const enhancements: Record = state.enhancements || {}; + const factory = this.getEmbeddableFactory(state.id); + + let telemetry = telemetryBaseEmbeddableInput(state, telemetryData); + if (factory) { + telemetry = factory.telemetry(state, telemetry); + } + Object.keys(enhancements).map((key) => { + if (!enhancements[key]) return; + telemetry = this.getEnhancement(key).telemetry(enhancements[key], telemetry); + }); + + return telemetry; + }; + + private extract = (state: EmbeddableInput) => { + const enhancements = state.enhancements || {}; + const factory = this.getEmbeddableFactory(state.id); + + const baseResponse = extractBaseEmbeddableInput(state); + let updatedInput = baseResponse.state; + const refs = baseResponse.references; + + if (factory) { + const factoryResponse = factory.extract(state); + updatedInput = factoryResponse.state; + refs.push(...factoryResponse.references); + } + + updatedInput.enhancements = {}; + Object.keys(enhancements).forEach((key) => { + if (!enhancements[key]) return; + const enhancementResult = this.getEnhancement(key).extract( + enhancements[key] as SerializableState + ); + refs.push(...enhancementResult.references); + updatedInput.enhancements![key] = enhancementResult.state; + }); + + return { + state: updatedInput, + references: refs, + }; + }; + + private inject = (state: EmbeddableInput, references: SavedObjectReference[]) => { + const enhancements = state.enhancements || {}; + const factory = this.getEmbeddableFactory(state.id); + + let updatedInput = injectBaseEmbeddableInput(state, references); + + if (factory) { + updatedInput = factory.inject(updatedInput, references); + } + + updatedInput.enhancements = {}; + Object.keys(enhancements).forEach((key) => { + if (!enhancements[key]) return; + updatedInput.enhancements![key] = this.getEnhancement(key).inject( + enhancements[key] as SerializableState, + references + ); + }); + + return updatedInput; + }; + + private registerEnhancement = (enhancement: EnhancementRegistryDefinition) => { + if (this.enhancements.has(enhancement.id)) { + throw new Error(`enhancement with id ${enhancement.id} already exists in the registry`); + } + this.enhancements.set(enhancement.id, { + id: enhancement.id, + telemetry: enhancement.telemetry || (() => ({})), + inject: enhancement.inject || identity, + extract: + enhancement.extract || + ((state: SerializableState) => { + return { state, references: [] }; + }), + }); + }; + + private getEnhancement = (id: string): EnhancementRegistryItem => { + return ( + this.enhancements.get(id) || { + id: 'unknown', + telemetry: () => ({}), + inject: identity, + extract: (state: SerializableState) => { + return { state, references: [] }; + }, + } + ); + }; + + private registerEmbeddableFactory = (factory: EmbeddableRegistryDefinition) => { + if (this.embeddableFactories.has(factory.id)) { + throw new Error( + `Embeddable factory [embeddableFactoryId = ${factory.id}] already registered in Embeddables API.` + ); + } + this.embeddableFactories.set(factory.id, { + id: factory.id, + telemetry: factory.telemetry || (() => ({})), + inject: factory.inject || identity, + extract: factory.extract || ((state: EmbeddableInput) => ({ state, references: [] })), + }); + }; + + private getEmbeddableFactory = (embeddableFactoryId: string) => { + return ( + this.embeddableFactories.get(embeddableFactoryId) || { + id: 'unknown', + telemetry: () => ({}), + inject: (state: EmbeddableInput) => state, + extract: (state: EmbeddableInput) => { + return { state, references: [] }; + }, + } + ); + }; +} diff --git a/src/plugins/embeddable/server/types.ts b/src/plugins/embeddable/server/types.ts new file mode 100644 index 0000000000000..64f9325dad3cb --- /dev/null +++ b/src/plugins/embeddable/server/types.ts @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + PersistableState, + PersistableStateDefinition, + SerializableState, +} from '../../kibana_utils/common'; +import { EmbeddableInput } from '../common/types'; + +export type EmbeddableFactoryRegistry = Map; +export type EnhancementsRegistry = Map; + +export interface EnhancementRegistryDefinition

+ extends PersistableStateDefinition

{ + id: string; +} + +export interface EnhancementRegistryItem

+ extends PersistableState

{ + id: string; +} + +export interface EmbeddableRegistryDefinition

+ extends PersistableStateDefinition

{ + id: string; +} + +export interface EmbeddableRegistryItem

+ extends PersistableState

{ + id: string; +} diff --git a/src/plugins/kibana_utils/common/index.ts b/src/plugins/kibana_utils/common/index.ts index 1ec5737c5a38b..e09290c811c7b 100644 --- a/src/plugins/kibana_utils/common/index.ts +++ b/src/plugins/kibana_utils/common/index.ts @@ -29,3 +29,4 @@ export { distinctUntilChangedWithInitialValue } from './distinct_until_changed_w export { url } from './url'; export { now } from './now'; export { calculateObjectHash } from './calculate_object_hash'; +export * from './persistable_state'; diff --git a/src/plugins/kibana_utils/common/persistable_state/index.ts b/src/plugins/kibana_utils/common/persistable_state/index.ts new file mode 100644 index 0000000000000..ae5e3d514554c --- /dev/null +++ b/src/plugins/kibana_utils/common/persistable_state/index.ts @@ -0,0 +1,54 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectReference } from '../../../../core/types'; + +export type SerializableValue = string | number | boolean | null | undefined | SerializableState; +export type Serializable = SerializableValue | SerializableValue[]; + +// eslint-disable-next-line +export type SerializableState = { + [key: string]: Serializable; +}; + +export interface PersistableState

{ + /** + * function to extract telemetry information + * @param state + * @param collector + */ + telemetry: (state: P, collector: Record) => Record; + /** + * inject function receives state and a list of references and should return state with references injected + * default is identity function + * @param state + * @param references + */ + inject: (state: P, references: SavedObjectReference[]) => P; + /** + * extract function receives state and should return state with references extracted and array of references + * default returns same state with empty reference array + * @param state + */ + extract: (state: P) => { state: P; references: SavedObjectReference[] }; +} + +export type PersistableStateDefinition

= Partial< + PersistableState

+>; diff --git a/src/plugins/kibana_utils/public/ui/configurable.ts b/src/plugins/kibana_utils/public/ui/configurable.ts index 3fa5cdc8b5e47..89bce5ae423ee 100644 --- a/src/plugins/kibana_utils/public/ui/configurable.ts +++ b/src/plugins/kibana_utils/public/ui/configurable.ts @@ -18,11 +18,15 @@ */ import { UiComponent } from '../../common/ui/ui_component'; +import { SerializableState } from '../../common'; /** * Represents something that can be configured by user using UI. */ -export interface Configurable { +export interface Configurable< + Config extends SerializableState = SerializableState, + Context = object +> { /** * Create default config for this item, used when item is created for the first time. */ @@ -42,7 +46,10 @@ export interface Configurable /** * Props provided to `CollectConfig` component on every re-render. */ -export interface CollectConfigProps { +export interface CollectConfigProps< + Config extends SerializableState = SerializableState, + Context = object +> { /** * Current (latest) config of the item. */ diff --git a/x-pack/examples/ui_actions_enhanced_examples/.eslintrc.json b/x-pack/examples/ui_actions_enhanced_examples/.eslintrc.json new file mode 100644 index 0000000000000..2aab6c2d9093b --- /dev/null +++ b/x-pack/examples/ui_actions_enhanced_examples/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "@typescript-eslint/consistent-type-definitions": 0 + } +} diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_drilldown/index.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_drilldown/index.tsx index fd782f5468c85..cac5f0b29dc6e 100644 --- a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_drilldown/index.tsx +++ b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_drilldown/index.tsx @@ -17,9 +17,9 @@ import { export type ActionContext = ChartActionContext; -export interface Config { +export type Config = { name: string; -} +}; const SAMPLE_DASHBOARD_HELLO_WORLD_DRILLDOWN = 'SAMPLE_DASHBOARD_HELLO_WORLD_DRILLDOWN'; diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_only_range_select_drilldown/index.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_only_range_select_drilldown/index.tsx index 7394690a61eae..fa2f0825f9335 100644 --- a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_only_range_select_drilldown/index.tsx +++ b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_only_range_select_drilldown/index.tsx @@ -13,9 +13,9 @@ import { CollectConfigProps } from '../../../../../src/plugins/kibana_utils/publ import { SELECT_RANGE_TRIGGER } from '../../../../../src/plugins/ui_actions/public'; import { BaseActionFactoryContext } from '../../../../plugins/ui_actions_enhanced/public/dynamic_actions'; -export interface Config { +export type Config = { name: string; -} +}; const SAMPLE_DASHBOARD_HELLO_WORLD_DRILLDOWN_ONLY_RANGE_SELECT = 'SAMPLE_DASHBOARD_HELLO_WORLD_DRILLDOWN_ONLY_RANGE_SELECT'; diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_to_discover_drilldown/types.ts b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_to_discover_drilldown/types.ts index a10e8ad707e97..692de571e8a00 100644 --- a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_to_discover_drilldown/types.ts +++ b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_to_discover_drilldown/types.ts @@ -9,7 +9,7 @@ import { ApplyGlobalFilterActionContext } from '../../../../../src/plugins/data/ export type ActionContext = ApplyGlobalFilterActionContext; -export interface Config { +export type Config = { /** * Whether to use a user selected index pattern, stored in `indexPatternId` field. */ @@ -30,6 +30,6 @@ export interface Config { * Whether to carry over source dashboard time range. */ carryTimeRange: boolean; -} +}; export type CollectConfigProps = CollectConfigPropsBase; diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts index c21109f8a596a..330a501a78d39 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts @@ -7,10 +7,11 @@ import { UiActionsEnhancedBaseActionFactoryContext } from '../../../../../ui_actions_enhanced/public'; import { APPLY_FILTER_TRIGGER } from '../../../../../../../src/plugins/ui_actions/public'; -export interface Config { +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type Config = { dashboardId?: string; useCurrentFilters: boolean; useCurrentDateRange: boolean; -} +}; export type FactoryContext = UiActionsEnhancedBaseActionFactoryContext; diff --git a/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts b/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts index 8881b2063c8db..e0960b83b23f9 100644 --- a/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts +++ b/x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts @@ -5,6 +5,7 @@ */ import { + DynamicActionsState, UiActionsEnhancedAbstractActionStorage as AbstractActionStorage, UiActionsEnhancedSerializedEvent as SerializedEvent, } from '../../../ui_actions_enhanced/public'; @@ -13,12 +14,12 @@ import { EmbeddableOutput, IEmbeddable, } from '../../../../../src/plugins/embeddable/public'; +import { SerializableState } from '../../../../../src/plugins/kibana_utils/common'; export interface EmbeddableWithDynamicActionsInput extends EmbeddableInput { enhancements?: { - dynamicActions?: { - events: SerializedEvent[]; - }; + dynamicActions: DynamicActionsState; + [key: string]: SerializableState; }; } diff --git a/x-pack/plugins/ui_actions_enhanced/.eslintrc.json b/x-pack/plugins/ui_actions_enhanced/.eslintrc.json new file mode 100644 index 0000000000000..2aab6c2d9093b --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "rules": { + "@typescript-eslint/consistent-type-definitions": 0 + } +} diff --git a/x-pack/plugins/ui_actions_enhanced/common/types.ts b/x-pack/plugins/ui_actions_enhanced/common/types.ts new file mode 100644 index 0000000000000..1150f4f823e8e --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/common/types.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SerializableState } from '../../../../src/plugins/kibana_utils/common'; + +export type BaseActionConfig = SerializableState; + +export type SerializedAction = { + readonly factoryId: string; + readonly name: string; + readonly config: Config; +}; + +/** + * Serialized representation of a triggers-action pair, used to persist in storage. + */ +export type SerializedEvent = { + eventId: string; + triggers: string[]; + action: SerializedAction; +}; + +export type DynamicActionsState = { + events: SerializedEvent[]; +}; diff --git a/x-pack/plugins/ui_actions_enhanced/kibana.json b/x-pack/plugins/ui_actions_enhanced/kibana.json index 108c66505f25c..5435019f216f2 100644 --- a/x-pack/plugins/ui_actions_enhanced/kibana.json +++ b/x-pack/plugins/ui_actions_enhanced/kibana.json @@ -7,7 +7,7 @@ "uiActions", "licensing" ], - "server": false, + "server": true, "ui": true, "requiredBundles": [ "kibanaUtils", diff --git a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/action_wizard.tsx index a3f6cac3ba1b4..ca7f6af4f7a37 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/action_wizard.tsx @@ -31,7 +31,7 @@ import { txtTriggerPickerHelpTooltip, } from './i18n'; import './action_wizard.scss'; -import { ActionFactory, BaseActionFactoryContext } from '../../dynamic_actions'; +import { ActionFactory, BaseActionConfig, BaseActionFactoryContext } from '../../dynamic_actions'; import { Trigger, TriggerId } from '../../../../../../src/plugins/ui_actions/public'; export interface ActionWizardProps< @@ -57,12 +57,12 @@ export interface ActionWizardProps< /** * current config for currently selected action factory */ - config?: object; + config?: BaseActionConfig; /** * config changed */ - onConfigChange: (config: object) => void; + onConfigChange: (config: BaseActionConfig) => void; /** * Context will be passed into ActionFactory's methods @@ -219,9 +219,9 @@ interface SelectedActionFactoryProps< ActionFactoryContext extends BaseActionFactoryContext = BaseActionFactoryContext > { actionFactory: ActionFactory; - config: object; + config: BaseActionConfig; context: ActionFactoryContext; - onConfigChange: (config: object) => void; + onConfigChange: (config: BaseActionConfig) => void; showDeselect: boolean; onDeselect: () => void; allTriggers: TriggerId[]; diff --git a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx index 71286e9a59c06..af930bfba6b8b 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx @@ -8,7 +8,7 @@ import React, { useState } from 'react'; import { EuiFieldText, EuiFormRow, EuiSelect, EuiSwitch } from '@elastic/eui'; import { reactToUiComponent } from '../../../../../../src/plugins/kibana_react/public'; import { ActionWizard } from './action_wizard'; -import { ActionFactory, ActionFactoryDefinition } from '../../dynamic_actions'; +import { ActionFactory, ActionFactoryDefinition, BaseActionConfig } from '../../dynamic_actions'; import { CollectConfigProps } from '../../../../../../src/plugins/kibana_utils/public'; import { licensingMock } from '../../../../licensing/public/mocks'; import { @@ -19,18 +19,16 @@ import { VALUE_CLICK_TRIGGER, } from '../../../../../../src/plugins/ui_actions/public'; -type ActionBaseConfig = object; - export const dashboards = [ { id: 'dashboard1', title: 'Dashboard 1' }, { id: 'dashboard2', title: 'Dashboard 2' }, ]; -interface DashboardDrilldownConfig { +type DashboardDrilldownConfig = { dashboardId?: string; useCurrentFilters: boolean; useCurrentDateRange: boolean; -} +}; function DashboardDrilldownCollectConfig(props: CollectConfigProps) { const config = props.config ?? { @@ -121,10 +119,11 @@ export const dashboardFactory = new ActionFactory(dashboardDrilldownActionFactor getFeatureUsageStart: () => licensingMock.createStart().featureUsage, }); -interface UrlDrilldownConfig { +type UrlDrilldownConfig = { url: string; openInNewTab: boolean; -} +}; + function UrlDrilldownCollectConfig(props: CollectConfigProps) { const config = props.config ?? { url: '', @@ -182,6 +181,10 @@ export const urlFactory = new ActionFactory(urlDrilldownActionFactory, { getFeatureUsageStart: () => licensingMock.createStart().featureUsage, }); +export const mockActionFactories: ActionFactory[] = ([dashboardFactory, urlFactory] as Array< + ActionFactory +>) as ActionFactory[]; + export const mockSupportedTriggers: TriggerId[] = [ VALUE_CLICK_TRIGGER, SELECT_RANGE_TRIGGER, @@ -210,7 +213,7 @@ export const mockGetTriggerInfo = (triggerId: TriggerId): Trigger => { export function Demo({ actionFactories }: { actionFactories: Array> }) { const [state, setState] = useState<{ currentActionFactory?: ActionFactory; - config?: ActionBaseConfig; + config?: BaseActionConfig; selectedTriggers?: TriggerId[]; }>({}); diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.story.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.story.tsx index f7284539ab2fe..daa56354289cf 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.story.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.story.tsx @@ -8,14 +8,13 @@ import * as React from 'react'; import { EuiFlyout } from '@elastic/eui'; import { storiesOf } from '@storybook/react'; import { createFlyoutManageDrilldowns } from './connected_flyout_manage_drilldowns'; -import { dashboardFactory, urlFactory } from '../../../components/action_wizard/test_data'; +import { mockActionFactories } from '../../../components/action_wizard/test_data'; import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; import { StubBrowserStorage } from '../../../../../../../src/test_utils/public/stub_browser_storage'; import { mockDynamicActionManager } from './test_data'; -import { ActionFactory } from '../../../dynamic_actions'; const FlyoutManageDrilldowns = createFlyoutManageDrilldowns({ - actionFactories: [dashboardFactory as ActionFactory, urlFactory as ActionFactory], + actionFactories: mockActionFactories, storage: new Storage(new StubBrowserStorage()), toastService: { addError: (...args: any[]) => { diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx index 2412cdd51748c..c4b07fa05c3c1 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx @@ -8,10 +8,9 @@ import React from 'react'; import { cleanup, fireEvent, render, wait } from '@testing-library/react/pure'; import { createFlyoutManageDrilldowns } from './connected_flyout_manage_drilldowns'; import { - dashboardFactory, mockGetTriggerInfo, mockSupportedTriggers, - urlFactory, + mockActionFactories, } from '../../../components/action_wizard/test_data'; import { StubBrowserStorage } from '../../../../../../../src/test_utils/public/stub_browser_storage'; import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; @@ -21,12 +20,11 @@ import { WELCOME_MESSAGE_TEST_SUBJ } from '../drilldown_hello_bar'; import { coreMock } from '../../../../../../../src/core/public/mocks'; import { NotificationsStart } from 'kibana/public'; import { toastDrilldownsCRUDError } from './i18n'; -import { ActionFactory } from '../../../dynamic_actions'; const storage = new Storage(new StubBrowserStorage()); const toasts = coreMock.createStart().notifications.toasts; const FlyoutManageDrilldowns = createFlyoutManageDrilldowns({ - actionFactories: [dashboardFactory as ActionFactory, urlFactory as ActionFactory], + actionFactories: mockActionFactories, storage: new Storage(new StubBrowserStorage()), toastService: toasts, getTrigger: mockGetTriggerInfo, diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx index 6f9eccde8bdb0..28a0990cf7526 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.tsx @@ -25,6 +25,7 @@ import { } from './i18n'; import { ActionFactory, + BaseActionConfig, BaseActionFactoryContext, DynamicActionManager, SerializedAction, @@ -127,7 +128,7 @@ export function createFlyoutManageDrilldowns({ return { actionFactory: allActionFactoriesById[drilldownToEdit.action.factoryId], - actionConfig: drilldownToEdit.action.config as object, + actionConfig: drilldownToEdit.action.config as BaseActionConfig, name: drilldownToEdit.action.name, selectedTriggers: (drilldownToEdit.triggers ?? []) as TriggerId[], }; diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/test_data.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/test_data.ts index 58c36e36481b8..78eec05eb2d0b 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/test_data.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/test_data.ts @@ -60,7 +60,7 @@ class MockDynamicActionManager implements PublicMethodsOf async updateEvent( eventId: string, - action: UiActionsEnhancedSerializedAction, + action: UiActionsEnhancedSerializedAction, triggers: Array ) { const state = this.state.get(); diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx index 8f73c2b3b3cc9..2f5f7760d40bd 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx @@ -8,8 +8,7 @@ import * as React from 'react'; import { EuiFlyout } from '@elastic/eui'; import { storiesOf } from '@storybook/react'; import { FlyoutDrilldownWizard } from './index'; -import { dashboardFactory, urlFactory } from '../../../components/action_wizard/test_data'; -import { ActionFactory } from '../../../dynamic_actions'; +import { mockActionFactories } from '../../../components/action_wizard/test_data'; import { Trigger, TriggerId } from '../../../../../../../src/plugins/ui_actions/public'; const otherProps = { @@ -24,23 +23,12 @@ const otherProps = { storiesOf('components/FlyoutDrilldownWizard', module) .add('default', () => { - return ( - - ); + return ; }) .add('open in flyout - create', () => { return ( {}}> - + ); }) @@ -48,13 +36,10 @@ storiesOf('components/FlyoutDrilldownWizard', module) return ( {}}> {}}> { +export interface DrilldownWizardConfig { name: string; actionFactory?: ActionFactory; actionConfig?: ActionConfig; @@ -28,7 +32,7 @@ export interface DrilldownWizardConfig { } export interface FlyoutDrilldownWizardProps< - CurrentActionConfig extends object = object, + CurrentActionConfig extends BaseActionConfig = BaseActionConfig, ActionFactoryContext extends BaseActionFactoryContext = BaseActionFactoryContext > { drilldownActionFactories: ActionFactory[]; @@ -71,7 +75,7 @@ function useWizardConfigState( DrilldownWizardConfig, { setName: (name: string) => void; - setActionConfig: (actionConfig: object) => void; + setActionConfig: (actionConfig: BaseActionConfig) => void; setActionFactory: (actionFactory?: ActionFactory) => void; setSelectedTriggers: (triggers?: TriggerId[]) => void; } @@ -100,7 +104,7 @@ function useWizardConfigState( name, }); }, - setActionConfig: (actionConfig: object) => { + setActionConfig: (actionConfig: BaseActionConfig) => { setWizardConfig({ ...wizardConfig, actionConfig, @@ -108,12 +112,12 @@ function useWizardConfigState( }, setActionFactory: (actionFactory?: ActionFactory) => { if (actionFactory) { + const actionConfig = (actionConfigCache[actionFactory.id] ?? + actionFactory.createConfig(actionFactoryContext)) as BaseActionConfig; setWizardConfig({ ...wizardConfig, actionFactory, - actionConfig: - actionConfigCache[actionFactory.id] ?? - actionFactory.createConfig(actionFactoryContext), + actionConfig, selectedTriggers: [], }); } else { @@ -141,7 +145,9 @@ function useWizardConfigState( ]; } -export function FlyoutDrilldownWizard({ +export function FlyoutDrilldownWizard< + CurrentActionConfig extends BaseActionConfig = BaseActionConfig +>({ onClose, onBack, onSubmit = () => {}, diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/form_drilldown_wizard/form_drilldown_wizard.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/form_drilldown_wizard/form_drilldown_wizard.tsx index d7f94a52088b7..45655c2634fe7 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/form_drilldown_wizard/form_drilldown_wizard.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/form_drilldown_wizard/form_drilldown_wizard.tsx @@ -8,7 +8,11 @@ import React from 'react'; import { EuiFieldText, EuiForm, EuiFormRow, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { txtDrilldownAction, txtNameOfDrilldown, txtUntitledDrilldown } from './i18n'; -import { ActionFactory, BaseActionFactoryContext } from '../../../dynamic_actions'; +import { + ActionFactory, + BaseActionConfig, + BaseActionFactoryContext, +} from '../../../dynamic_actions'; import { ActionWizard } from '../../../components/action_wizard'; import { Trigger, TriggerId } from '../../../../../../../src/plugins/ui_actions/public'; @@ -26,8 +30,8 @@ export interface FormDrilldownWizardProps< onActionFactoryChange?: (actionFactory?: ActionFactory) => void; actionFactoryContext: ActionFactoryContext; - actionConfig?: object; - onActionConfigChange?: (config: object) => void; + actionConfig?: BaseActionConfig; + onActionConfigChange?: (config: BaseActionConfig) => void; actionFactories?: ActionFactory[]; diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts index f5e565d4090ff..b55b4b87ebccd 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts @@ -4,10 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ActionFactoryDefinition, BaseActionFactoryContext } from '../dynamic_actions'; +import { + ActionFactoryDefinition, + BaseActionConfig, + BaseActionFactoryContext, + SerializedEvent, +} from '../dynamic_actions'; import { LicenseType } from '../../../licensing/public'; import { TriggerContextMapping, TriggerId } from '../../../../../src/plugins/ui_actions/public'; import { ActionExecutionContext } from '../../../../../src/plugins/ui_actions/public'; +import { PersistableStateDefinition } from '../../../../../src/plugins/kibana_utils/common'; /** * This is a convenience interface to register a drilldown. Drilldown has @@ -24,13 +30,13 @@ import { ActionExecutionContext } from '../../../../../src/plugins/ui_actions/pu */ export interface DrilldownDefinition< - Config extends object = object, + Config extends BaseActionConfig = BaseActionConfig, SupportedTriggers extends TriggerId = TriggerId, FactoryContext extends BaseActionFactoryContext = { triggers: SupportedTriggers[]; }, ExecutionContext extends TriggerContextMapping[SupportedTriggers] = TriggerContextMapping[SupportedTriggers] -> { +> extends PersistableStateDefinition { /** * Globally unique identifier for this drilldown. */ diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/types.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/types.ts index 31c7481c9d63e..fb7d96aaf8325 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/types.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/types.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface UrlDrilldownConfig { +export type UrlDrilldownConfig = { url: { format?: 'handlebars_v1'; template: string }; openInNewTab: boolean; -} +}; /** * URL drilldown has 3 sources for variables: global, context and event variables diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts index 3ad6d4ee39749..57c8733ed44fc 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts @@ -12,9 +12,16 @@ import { } from '../../../../../src/plugins/ui_actions/public'; import { ActionFactoryDefinition } from './action_factory_definition'; import { Configurable } from '../../../../../src/plugins/kibana_utils/public'; -import { BaseActionFactoryContext, SerializedAction } from './types'; +import { + BaseActionConfig, + BaseActionFactoryContext, + SerializedAction, + SerializedEvent, +} from './types'; import { ILicense, LicensingPluginStart } from '../../../licensing/public'; import { UiActionsActionDefinition as ActionDefinition } from '../../../../../src/plugins/ui_actions/public'; +import { SavedObjectReference } from '../../../../../src/core/types'; +import { PersistableState } from '../../../../../src/plugins/kibana_utils/common'; export interface ActionFactoryDeps { readonly getLicense: () => ILicense; @@ -22,13 +29,16 @@ export interface ActionFactoryDeps { } export class ActionFactory< - Config extends object = object, + Config extends BaseActionConfig = BaseActionConfig, SupportedTriggers extends TriggerId = TriggerId, FactoryContext extends BaseActionFactoryContext = { triggers: SupportedTriggers[]; }, ActionContext extends TriggerContextMapping[SupportedTriggers] = TriggerContextMapping[SupportedTriggers] -> implements Omit, 'getHref'>, Configurable { +> implements + Omit, 'getHref'>, + Configurable, + PersistableState { constructor( protected readonly def: ActionFactoryDefinition< Config, @@ -121,4 +131,16 @@ export class ActionFactory< ); }); } + + public telemetry(state: SerializedEvent, telemetryData: Record) { + return this.def.telemetry ? this.def.telemetry(state, telemetryData) : {}; + } + + public extract(state: SerializedEvent) { + return this.def.extract ? this.def.extract(state) : { state, references: [] }; + } + + public inject(state: SerializedEvent, references: SavedObjectReference[]) { + return this.def.inject ? this.def.inject(state, references) : state; + } } diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory_definition.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory_definition.ts index 7ec6b21485747..b4df1f827a2a3 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory_definition.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory_definition.ts @@ -5,7 +5,12 @@ */ import { Configurable } from '../../../../../src/plugins/kibana_utils/public'; -import { BaseActionFactoryContext, SerializedAction } from './types'; +import { + BaseActionConfig, + BaseActionFactoryContext, + SerializedAction, + SerializedEvent, +} from './types'; import { LicenseType } from '../../../licensing/public'; import { TriggerContextMapping, @@ -13,19 +18,21 @@ import { UiActionsActionDefinition as ActionDefinition, UiActionsPresentable as Presentable, } from '../../../../../src/plugins/ui_actions/public'; +import { PersistableStateDefinition } from '../../../../../src/plugins/kibana_utils/common'; /** * This is a convenience interface for registering new action factories. */ export interface ActionFactoryDefinition< - Config extends object = object, + Config extends BaseActionConfig = BaseActionConfig, SupportedTriggers extends TriggerId = TriggerId, FactoryContext extends BaseActionFactoryContext = { triggers: SupportedTriggers[]; }, ActionContext extends TriggerContextMapping[SupportedTriggers] = TriggerContextMapping[SupportedTriggers] > extends Partial, 'getHref'>>, - Configurable { + Configurable, + PersistableStateDefinition { /** * Unique ID of the action factory. This ID is used to identify this action * factory in the registry as well as to construct actions of this type and diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_enhancement.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_enhancement.ts new file mode 100644 index 0000000000000..7cac49624bfdd --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_enhancement.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EnhancementRegistryDefinition } from '../../../../../src/plugins/embeddable/public'; +import { SavedObjectReference } from '../../../../../src/core/types'; +import { SerializableState } from '../../../../../src/plugins/kibana_utils/common'; +import { DynamicActionsState } from '../../../ui_actions_enhanced/public'; +import { UiActionsServiceEnhancements } from '../services'; + +export const dynamicActionEnhancement = ( + uiActionsEnhanced: UiActionsServiceEnhancements +): EnhancementRegistryDefinition => { + return { + id: 'dynamicActions', + telemetry: (state: SerializableState, telemetryData: Record) => { + return uiActionsEnhanced.telemetry(state as DynamicActionsState, telemetryData); + }, + extract: (state: SerializableState) => { + return uiActionsEnhanced.extract(state as DynamicActionsState); + }, + inject: (state: SerializableState, references: SavedObjectReference[]) => { + return uiActionsEnhanced.inject(state as DynamicActionsState, references); + }, + } as EnhancementRegistryDefinition; +}; diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.test.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.test.ts index 39d9dfeca2fd6..83232bbce1ba7 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.test.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.test.ts @@ -250,7 +250,7 @@ describe('DynamicActionManager', () => { uiActions.registerActionFactory(actionFactoryDefinition1); await manager.start(); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition1.id, name: 'foo', config: {}, @@ -277,7 +277,7 @@ describe('DynamicActionManager', () => { test('adds event to UI state', async () => { const { manager, uiActions } = setup([]); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition1.id, name: 'foo', config: {}, @@ -296,7 +296,7 @@ describe('DynamicActionManager', () => { test('optimistically adds event to UI state', async () => { const { manager, uiActions } = setup([]); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition1.id, name: 'foo', config: {}, @@ -319,7 +319,7 @@ describe('DynamicActionManager', () => { test('instantiates event in actions service', async () => { const { manager, uiActions, actions } = setup([]); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition1.id, name: 'foo', config: {}, @@ -348,7 +348,7 @@ describe('DynamicActionManager', () => { uiActions.registerActionFactory(actionFactoryDefinition1); await manager.start(); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition1.id, name: 'foo', config: {}, @@ -361,7 +361,7 @@ describe('DynamicActionManager', () => { test('does not add even to UI state', async () => { const { manager, storage, uiActions } = setup([]); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition1.id, name: 'foo', config: {}, @@ -380,7 +380,7 @@ describe('DynamicActionManager', () => { test('optimistically adds event to UI state and then removes it', async () => { const { manager, storage, uiActions } = setup([]); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition1.id, name: 'foo', config: {}, @@ -406,7 +406,7 @@ describe('DynamicActionManager', () => { test('does not instantiate event in actions service', async () => { const { manager, storage, uiActions, actions } = setup([]); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition1.id, name: 'foo', config: {}, @@ -432,7 +432,7 @@ describe('DynamicActionManager', () => { uiActions.registerActionFactory(actionFactoryDefinition1); await manager.start(); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition1.id, name: 'foo', config: {}, @@ -457,7 +457,7 @@ describe('DynamicActionManager', () => { expect(registeredAction1.getDisplayName()).toBe('Action 3'); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition2.id, name: 'foo', config: {}, @@ -479,7 +479,7 @@ describe('DynamicActionManager', () => { uiActions.registerActionFactory(actionFactoryDefinition2); await manager.start(); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition2.id, name: 'foo', config: {}, @@ -505,7 +505,7 @@ describe('DynamicActionManager', () => { uiActions.registerActionFactory(actionFactoryDefinition2); await manager.start(); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition2.id, name: 'foo', config: {}, @@ -524,7 +524,7 @@ describe('DynamicActionManager', () => { uiActions.registerActionFactory(actionFactoryDefinition2); await manager.start(); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition2.id, name: 'foo', config: {}, @@ -552,7 +552,7 @@ describe('DynamicActionManager', () => { uiActions.registerActionFactory(actionFactoryDefinition2); await manager.start(); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition2.id, name: 'foo', config: {}, @@ -580,7 +580,7 @@ describe('DynamicActionManager', () => { expect(registeredAction1.getDisplayName()).toBe('Action 3'); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition2.id, name: 'foo', config: {}, @@ -604,7 +604,7 @@ describe('DynamicActionManager', () => { uiActions.registerActionFactory(actionFactoryDefinition2); await manager.start(); - const action: SerializedAction = { + const action: SerializedAction = { factoryId: actionFactoryDefinition2.id, name: 'foo', config: {}, diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.ts index 6ca388281ad76..471b929fdbc06 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/dynamic_action_manager.ts @@ -74,7 +74,7 @@ export class DynamicActionManager { const actionId = this.generateActionId(eventId); const factory = uiActions.getActionFactory(event.action.factoryId); - const actionDefinition: ActionDefinition = factory.create(action as SerializedAction); + const actionDefinition: ActionDefinition = factory.create(action as SerializedAction); uiActions.registerAction({ ...actionDefinition, id: actionId, @@ -195,10 +195,7 @@ export class DynamicActionManager { * @param action Dynamic action for which to create an event. * @param triggers List of triggers to which action should react. */ - public async createEvent( - action: SerializedAction, - triggers: Array - ) { + public async createEvent(action: SerializedAction, triggers: Array) { const event: SerializedEvent = { eventId: uuidv4(), triggers, @@ -231,7 +228,7 @@ export class DynamicActionManager { */ public async updateEvent( eventId: string, - action: SerializedAction, + action: SerializedAction, triggers: Array ) { const event: SerializedEvent = { diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/types.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/types.ts index d00db0d9acb7a..28d104093f64f 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/types.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/types.ts @@ -5,21 +5,9 @@ */ import { TriggerId } from '../../../../../src/plugins/ui_actions/public'; +import { SerializedAction, SerializedEvent, BaseActionConfig } from '../../common/types'; -export interface SerializedAction { - readonly factoryId: string; - readonly name: string; - readonly config: Config; -} - -/** - * Serialized representation of a triggers-action pair, used to persist in storage. - */ -export interface SerializedEvent { - eventId: string; - triggers: string[]; - action: SerializedAction; -} +export { SerializedAction, SerializedEvent, BaseActionConfig }; /** * Action factory context passed into ActionFactories' CollectConfig, getDisplayName, getIconType diff --git a/x-pack/plugins/ui_actions_enhanced/public/index.ts b/x-pack/plugins/ui_actions_enhanced/public/index.ts index 4a899b24852a9..ae720598ec759 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/index.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/index.ts @@ -29,7 +29,10 @@ export { DynamicActionManagerState as UiActionsEnhancedDynamicActionManagerState, MemoryActionStorage as UiActionsEnhancedMemoryActionStorage, BaseActionFactoryContext as UiActionsEnhancedBaseActionFactoryContext, + BaseActionConfig as UiActionsEnhancedBaseActionConfig, } from './dynamic_actions'; +export { DynamicActionsState } from './services/ui_actions_service_enhancements'; + export { DrilldownDefinition as UiActionsEnhancedDrilldownDefinition } from './drilldowns'; export * from './drilldowns/url_drilldown'; diff --git a/x-pack/plugins/ui_actions_enhanced/public/mocks.ts b/x-pack/plugins/ui_actions_enhanced/public/mocks.ts index 17a6fc1b955df..9eb0a06b6dbaf 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/mocks.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/mocks.ts @@ -30,6 +30,9 @@ const createStartContract = (): Start => { getActionFactories: jest.fn(), getActionFactory: jest.fn(), FlyoutManageDrilldowns: jest.fn(), + telemetry: jest.fn(), + extract: jest.fn(), + inject: jest.fn(), }; return startContract; diff --git a/x-pack/plugins/ui_actions_enhanced/public/plugin.ts b/x-pack/plugins/ui_actions_enhanced/public/plugin.ts index b38bc44abe2b0..b05c08c4c77d0 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/plugin.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/plugin.ts @@ -39,6 +39,7 @@ import { UiActionsServiceEnhancements } from './services'; import { ILicense, LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public'; import { createFlyoutManageDrilldowns } from './drilldowns'; import { createStartServicesGetter, Storage } from '../../../../src/plugins/kibana_utils/public'; +import { dynamicActionEnhancement } from './dynamic_actions/dynamic_action_enhancement'; interface SetupDependencies { embeddable: EmbeddableSetup; // Embeddable are needed because they register basic triggers/actions. @@ -58,7 +59,10 @@ export interface SetupContract export interface StartContract extends UiActionsStart, - Pick { + Pick< + UiActionsServiceEnhancements, + 'getActionFactory' | 'getActionFactories' | 'telemetry' | 'extract' | 'inject' + > { FlyoutManageDrilldowns: ReturnType; } @@ -87,7 +91,7 @@ export class AdvancedUiActionsPublicPlugin public setup( core: CoreSetup, - { uiActions, licensing }: SetupDependencies + { embeddable, uiActions, licensing }: SetupDependencies ): SetupContract { const startServices = createStartServicesGetter(core.getStartServices); this.enhancements = new UiActionsServiceEnhancements({ @@ -95,6 +99,7 @@ export class AdvancedUiActionsPublicPlugin featureUsageSetup: licensing.featureUsage, getFeatureUsageStart: () => startServices().plugins.licensing.featureUsage, }); + embeddable.registerEnhancement(dynamicActionEnhancement(this.enhancements)); return { ...uiActions, ...this.enhancements, diff --git a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.test.ts b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.test.ts index 3a0b65d2ed844..6c71868222b24 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.test.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.test.ts @@ -96,6 +96,66 @@ describe('UiActionsService', () => { ).resolves.toBe(false); }); + test('action factory extract function gets called when calling uiactions extract', () => { + const service = new UiActionsServiceEnhancements(deps); + const actionState = { + events: [ + { + eventId: 'test', + triggers: [], + action: { factoryId: factoryDefinition1.id, name: 'test', config: {} }, + }, + ], + }; + const extract = jest.fn().mockImplementation((state) => ({ state, references: [] })); + service.registerActionFactory({ + ...factoryDefinition1, + extract, + }); + service.extract(actionState); + expect(extract).toBeCalledWith(actionState.events[0]); + }); + + test('action factory inject function gets called when calling uiactions inject', () => { + const service = new UiActionsServiceEnhancements(deps); + const actionState = { + events: [ + { + eventId: 'test', + triggers: [], + action: { factoryId: factoryDefinition1.id, name: 'test', config: {} }, + }, + ], + }; + const inject = jest.fn().mockImplementation((state) => state); + service.registerActionFactory({ + ...factoryDefinition1, + inject, + }); + service.inject(actionState, []); + expect(inject).toBeCalledWith(actionState.events[0], []); + }); + + test('action factory telemetry function gets called when calling uiactions telemetry', () => { + const service = new UiActionsServiceEnhancements(deps); + const actionState = { + events: [ + { + eventId: 'test', + triggers: [], + action: { factoryId: factoryDefinition1.id, name: 'test', config: {} }, + }, + ], + }; + const telemetry = jest.fn().mockImplementation((state) => ({})); + service.registerActionFactory({ + ...factoryDefinition1, + telemetry, + }); + service.telemetry(actionState); + expect(telemetry).toBeCalledWith(actionState.events[0], {}); + }); + describe('registerFeature for licensing', () => { const spy = jest.spyOn(deps.featureUsageSetup, 'register'); beforeEach(() => { diff --git a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts index ab0aa1200f5a7..5e40d803962de 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts @@ -8,12 +8,20 @@ import { ActionFactoryRegistry } from '../types'; import { ActionFactory, ActionFactoryDefinition, + BaseActionConfig, BaseActionFactoryContext, + SerializedEvent, } from '../dynamic_actions'; import { DrilldownDefinition } from '../drilldowns'; import { ILicense } from '../../../licensing/common/types'; import { TriggerContextMapping, TriggerId } from '../../../../../src/plugins/ui_actions/public'; import { LicensingPluginSetup, LicensingPluginStart } from '../../../licensing/public'; +import { SavedObjectReference } from '../../../../../src/core/types'; +import { PersistableStateDefinition } from '../../../../../src/plugins/kibana_utils/common'; + +import { DynamicActionsState } from '../../common/types'; + +export { DynamicActionsState }; export interface UiActionsServiceEnhancementsParams { readonly actionFactories?: ActionFactoryRegistry; @@ -22,7 +30,8 @@ export interface UiActionsServiceEnhancementsParams { readonly getFeatureUsageStart: () => LicensingPluginStart['featureUsage']; } -export class UiActionsServiceEnhancements { +export class UiActionsServiceEnhancements + implements PersistableStateDefinition { protected readonly actionFactories: ActionFactoryRegistry; protected readonly deps: Omit; @@ -36,7 +45,7 @@ export class UiActionsServiceEnhancements { * serialize/deserialize dynamic actions. */ public readonly registerActionFactory = < - Config extends object = object, + Config extends BaseActionConfig = BaseActionConfig, SupportedTriggers extends TriggerId = TriggerId, FactoryContext extends BaseActionFactoryContext = { triggers: SupportedTriggers[]; @@ -81,7 +90,7 @@ export class UiActionsServiceEnhancements { * Convenience method to register a {@link DrilldownDefinition | drilldown}. */ public readonly registerDrilldown = < - Config extends object = object, + Config extends BaseActionConfig = BaseActionConfig, SupportedTriggers extends TriggerId = TriggerId, FactoryContext extends BaseActionFactoryContext = { triggers: SupportedTriggers[]; @@ -102,6 +111,9 @@ export class UiActionsServiceEnhancements { licenseFeatureName, supportedTriggers, isCompatible, + telemetry, + extract, + inject, }: DrilldownDefinition): void => { const actionFactory: ActionFactoryDefinition< Config, @@ -119,6 +131,9 @@ export class UiActionsServiceEnhancements { isConfigValid, getDisplayName, supportedTriggers, + telemetry, + extract, + inject, getIconType: () => euiIcon, isCompatible: async () => true, create: (serializedAction) => ({ @@ -151,4 +166,43 @@ export class UiActionsServiceEnhancements { ); }); }; + + public readonly telemetry = (state: DynamicActionsState, telemetry: Record = {}) => { + let telemetryData = telemetry; + state.events.forEach((event: SerializedEvent) => { + if (this.actionFactories.has(event.action.factoryId)) { + telemetryData = this.actionFactories + .get(event.action.factoryId)! + .telemetry(event, telemetryData); + } + }); + return telemetryData; + }; + + public readonly extract = (state: DynamicActionsState) => { + const references: SavedObjectReference[] = []; + const newState = { + events: state.events.map((event: SerializedEvent) => { + const result = this.actionFactories.has(event.action.factoryId) + ? this.actionFactories.get(event.action.factoryId)!.extract(event) + : { + state: event, + references: [], + }; + references.push(...result.references); + return result.state; + }), + }; + return { state: newState, references }; + }; + + public readonly inject = (state: DynamicActionsState, references: SavedObjectReference[]) => { + return { + events: state.events.map((event: SerializedEvent) => { + return this.actionFactories.has(event.action.factoryId) + ? this.actionFactories.get(event.action.factoryId)!.inject(event, references) + : event; + }), + }; + }; } diff --git a/x-pack/plugins/ui_actions_enhanced/public/test_helpers/time_range_container.ts b/x-pack/plugins/ui_actions_enhanced/public/test_helpers/time_range_container.ts index 3d143b0cacd06..9a529f192158d 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/test_helpers/time_range_container.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/test_helpers/time_range_container.ts @@ -17,7 +17,6 @@ import { TimeRange } from '../../../../../src/plugins/data/public'; * https://github.com/microsoft/TypeScript/issues/15300 is fixed so we use a type * here instead */ -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type InheritedChildrenInput = { timeRange: TimeRange; id?: string; diff --git a/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts b/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts new file mode 100644 index 0000000000000..b366436200914 --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EnhancementRegistryDefinition } from '../../../../src/plugins/embeddable/server'; +import { SavedObjectReference } from '../../../../src/core/types'; +import { DynamicActionsState, SerializedEvent } from './types'; +import { AdvancedUiActionsPublicPlugin } from './plugin'; +import { SerializableState } from '../../../../src/plugins/kibana_utils/common'; + +export const dynamicActionEnhancement = ( + uiActionsEnhanced: AdvancedUiActionsPublicPlugin +): EnhancementRegistryDefinition => { + return { + id: 'dynamicActions', + telemetry: (state: SerializableState, telemetry: Record) => { + let telemetryData = telemetry; + (state as DynamicActionsState).events.forEach((event: SerializedEvent) => { + if (uiActionsEnhanced.getActionFactory(event.action.factoryId)) { + telemetryData = uiActionsEnhanced + .getActionFactory(event.action.factoryId)! + .telemetry(event, telemetryData); + } + }); + return telemetryData; + }, + extract: (state: SerializableState) => { + const references: SavedObjectReference[] = []; + const newState: DynamicActionsState = { + events: (state as DynamicActionsState).events.map((event: SerializedEvent) => { + const result = uiActionsEnhanced.getActionFactory(event.action.factoryId) + ? uiActionsEnhanced.getActionFactory(event.action.factoryId)!.extract(event) + : { + state: event, + references: [], + }; + result.references.forEach((r) => references.push(r)); + return result.state; + }), + }; + return { state: newState, references }; + }, + inject: (state: SerializableState, references: SavedObjectReference[]) => { + return { + events: (state as DynamicActionsState).events.map((event: SerializedEvent) => { + return uiActionsEnhanced.getActionFactory(event.action.factoryId) + ? uiActionsEnhanced.getActionFactory(event.action.factoryId)!.inject(event, references) + : event; + }), + } as DynamicActionsState; + }, + } as EnhancementRegistryDefinition; +}; diff --git a/x-pack/plugins/ui_actions_enhanced/server/index.ts b/x-pack/plugins/ui_actions_enhanced/server/index.ts new file mode 100644 index 0000000000000..5419c4135796d --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/server/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AdvancedUiActionsPublicPlugin } from './plugin'; + +export function plugin() { + return new AdvancedUiActionsPublicPlugin(); +} + +export { AdvancedUiActionsPublicPlugin as Plugin }; +export { + SetupContract as AdvancedUiActionsSetup, + StartContract as AdvancedUiActionsStart, +} from './plugin'; + +export { + ActionFactoryDefinition as UiActionsEnhancedActionFactoryDefinition, + ActionFactory as UiActionsEnhancedActionFactory, +} from './types'; + +export { + DynamicActionsState, + BaseActionConfig as UiActionsEnhancedBaseActionConfig, + SerializedAction as UiActionsEnhancedSerializedAction, + SerializedEvent as UiActionsEnhancedSerializedEvent, +} from '../common/types'; diff --git a/x-pack/plugins/ui_actions_enhanced/server/plugin.ts b/x-pack/plugins/ui_actions_enhanced/server/plugin.ts new file mode 100644 index 0000000000000..0a61c917a2c5c --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/server/plugin.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { identity } from 'lodash'; +import { CoreSetup, Plugin, SavedObjectReference } from '../../../../src/core/server'; +import { EmbeddableSetup } from '../../../../src/plugins/embeddable/server'; +import { dynamicActionEnhancement } from './dynamic_action_enhancement'; +import { + ActionFactoryRegistry, + SerializedEvent, + ActionFactoryDefinition, + DynamicActionsState, +} from './types'; + +export interface SetupContract { + registerActionFactory: any; +} + +export type StartContract = void; + +interface SetupDependencies { + embeddable: EmbeddableSetup; // Embeddable are needed because they register basic triggers/actions. +} + +export class AdvancedUiActionsPublicPlugin + implements Plugin { + protected readonly actionFactories: ActionFactoryRegistry = new Map(); + + constructor() {} + + public setup(core: CoreSetup, { embeddable }: SetupDependencies) { + embeddable.registerEnhancement(dynamicActionEnhancement(this)); + + return { + registerActionFactory: this.registerActionFactory, + }; + } + + public start() {} + + public stop() {} + + /** + * Register an action factory. Action factories are used to configure and + * serialize/deserialize dynamic actions. + */ + public readonly registerActionFactory = (definition: ActionFactoryDefinition) => { + if (this.actionFactories.has(definition.id)) { + throw new Error(`ActionFactory [actionFactory.id = ${definition.id}] already registered.`); + } + + this.actionFactories.set(definition.id, { + id: definition.id, + telemetry: definition.telemetry || (() => ({})), + inject: definition.inject || identity, + extract: + definition.extract || + ((state: SerializedEvent) => { + return { state, references: [] }; + }), + }); + }; + + public readonly getActionFactory = (actionFactoryId: string) => { + const actionFactory = this.actionFactories.get(actionFactoryId); + return actionFactory; + }; + + public readonly telemetry = (state: DynamicActionsState, telemetry: Record = {}) => { + state.events.forEach((event: SerializedEvent) => { + if (this.actionFactories.has(event.action.factoryId)) { + this.actionFactories.get(event.action.factoryId)!.telemetry(event, telemetry); + } + }); + return telemetry; + }; + + public readonly extract = (state: DynamicActionsState) => { + const references: SavedObjectReference[] = []; + const newState = { + events: state.events.map((event: SerializedEvent) => { + const result = this.actionFactories.has(event.action.factoryId) + ? this.actionFactories.get(event.action.factoryId)!.extract(event) + : { + state: event, + references: [], + }; + result.references.forEach((r) => references.push(r)); + return result.state; + }), + }; + return { state: newState, references }; + }; + + public readonly inject = (state: DynamicActionsState, references: SavedObjectReference[]) => { + return { + events: state.events.map((event: SerializedEvent) => { + return this.actionFactories.has(event.action.factoryId) + ? this.actionFactories.get(event.action.factoryId)!.inject(event, references) + : event; + }), + }; + }; +} diff --git a/x-pack/plugins/ui_actions_enhanced/server/types.ts b/x-pack/plugins/ui_actions_enhanced/server/types.ts new file mode 100644 index 0000000000000..4859be6728344 --- /dev/null +++ b/x-pack/plugins/ui_actions_enhanced/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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + PersistableState, + PersistableStateDefinition, +} from '../../../../src/plugins/kibana_utils/common'; + +import { SerializedAction, SerializedEvent, DynamicActionsState } from '../common/types'; + +export type ActionFactoryRegistry = Map; + +export interface ActionFactoryDefinition

+ extends PersistableStateDefinition

{ + id: string; +} + +export interface ActionFactory

+ extends PersistableState

{ + id: string; +} + +export { SerializedEvent, SerializedAction, DynamicActionsState };