Embeddable telemetry and reference extraction/injection (#74352)

This commit is contained in:
Peter Pisljar 2020-09-18 17:43:00 +02:00 committed by GitHub
parent ef55756b8c
commit e361035650
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
91 changed files with 1314 additions and 505 deletions

View file

@ -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;

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [Filter](./kibana-plugin-plugins-data-public.filter.md) &gt; [$state](./kibana-plugin-plugins-data-public.filter._state.md)
## Filter.$state property
<b>Signature:</b>
```typescript
$state?: FilterState;
```

View file

@ -2,19 +2,14 @@
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [Filter](./kibana-plugin-plugins-data-public.filter.md)
## Filter interface
## Filter type
<b>Signature:</b>
```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) | <code>FilterState</code> | |
| [meta](./kibana-plugin-plugins-data-public.filter.meta.md) | <code>FilterMeta</code> | |
| [query](./kibana-plugin-plugins-data-public.filter.query.md) | <code>any</code> | |

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [Filter](./kibana-plugin-plugins-data-public.filter.md) &gt; [meta](./kibana-plugin-plugins-data-public.filter.meta.md)
## Filter.meta property
<b>Signature:</b>
```typescript
meta: FilterMeta;
```

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [Filter](./kibana-plugin-plugins-data-public.filter.md) &gt; [query](./kibana-plugin-plugins-data-public.filter.query.md)
## Filter.query property
<b>Signature:</b>
```typescript
query?: any;
```

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [Query](./kibana-plugin-plugins-data-public.query.md) &gt; [language](./kibana-plugin-plugins-data-public.query.language.md)
## Query.language property
<b>Signature:</b>
```typescript
language: string;
```

View file

@ -2,18 +2,15 @@
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [Query](./kibana-plugin-plugins-data-public.query.md)
## Query interface
## Query type
<b>Signature:</b>
```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) | <code>string</code> | |
| [query](./kibana-plugin-plugins-data-public.query.query.md) | <code>string &#124; {</code><br/><code> [key: string]: any;</code><br/><code> }</code> | |

View file

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [Query](./kibana-plugin-plugins-data-public.query.md) &gt; [query](./kibana-plugin-plugins-data-public.query.query.md)
## Query.query property
<b>Signature:</b>
```typescript
query: string | {
[key: string]: any;
};
```

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) &gt; [from](./kibana-plugin-plugins-data-public.timerange.from.md)
## TimeRange.from property
<b>Signature:</b>
```typescript
from: string;
```

View file

@ -2,19 +2,14 @@
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [TimeRange](./kibana-plugin-plugins-data-public.timerange.md)
## TimeRange interface
## TimeRange type
<b>Signature:</b>
```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) | <code>string</code> | |
| [mode](./kibana-plugin-plugins-data-public.timerange.mode.md) | <code>'absolute' &#124; 'relative'</code> | |
| [to](./kibana-plugin-plugins-data-public.timerange.to.md) | <code>string</code> | |

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) &gt; [mode](./kibana-plugin-plugins-data-public.timerange.mode.md)
## TimeRange.mode property
<b>Signature:</b>
```typescript
mode?: 'absolute' | 'relative';
```

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) &gt; [to](./kibana-plugin-plugins-data-public.timerange.to.md)
## TimeRange.to property
<b>Signature:</b>
```typescript
to: string;
```

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [Filter](./kibana-plugin-plugins-data-server.filter.md) &gt; [$state](./kibana-plugin-plugins-data-server.filter._state.md)
## Filter.$state property
<b>Signature:</b>
```typescript
$state?: FilterState;
```

View file

@ -2,19 +2,14 @@
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [Filter](./kibana-plugin-plugins-data-server.filter.md)
## Filter interface
## Filter type
<b>Signature:</b>
```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) | <code>FilterState</code> | |
| [meta](./kibana-plugin-plugins-data-server.filter.meta.md) | <code>FilterMeta</code> | |
| [query](./kibana-plugin-plugins-data-server.filter.query.md) | <code>any</code> | |

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [Filter](./kibana-plugin-plugins-data-server.filter.md) &gt; [meta](./kibana-plugin-plugins-data-server.filter.meta.md)
## Filter.meta property
<b>Signature:</b>
```typescript
meta: FilterMeta;
```

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [Filter](./kibana-plugin-plugins-data-server.filter.md) &gt; [query](./kibana-plugin-plugins-data-server.filter.query.md)
## Filter.query property
<b>Signature:</b>
```typescript
query?: any;
```

View file

@ -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) | |

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [Query](./kibana-plugin-plugins-data-server.query.md) &gt; [language](./kibana-plugin-plugins-data-server.query.language.md)
## Query.language property
<b>Signature:</b>
```typescript
language: string;
```

View file

@ -2,18 +2,15 @@
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [Query](./kibana-plugin-plugins-data-server.query.md)
## Query interface
## Query type
<b>Signature:</b>
```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) | <code>string</code> | |
| [query](./kibana-plugin-plugins-data-server.query.query.md) | <code>string &#124; {</code><br/><code> [key: string]: any;</code><br/><code> }</code> | |

View file

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [Query](./kibana-plugin-plugins-data-server.query.md) &gt; [query](./kibana-plugin-plugins-data-server.query.query.md)
## Query.query property
<b>Signature:</b>
```typescript
query: string | {
[key: string]: any;
};
```

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) &gt; [from](./kibana-plugin-plugins-data-server.timerange.from.md)
## TimeRange.from property
<b>Signature:</b>
```typescript
from: string;
```

View file

@ -2,19 +2,14 @@
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [TimeRange](./kibana-plugin-plugins-data-server.timerange.md)
## TimeRange interface
## TimeRange type
<b>Signature:</b>
```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) | <code>string</code> | |
| [mode](./kibana-plugin-plugins-data-server.timerange.mode.md) | <code>'absolute' &#124; 'relative'</code> | |
| [to](./kibana-plugin-plugins-data-server.timerange.to.md) | <code>string</code> | |

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) &gt; [mode](./kibana-plugin-plugins-data-server.timerange.mode.md)
## TimeRange.mode property
<b>Signature:</b>
```typescript
mode?: 'absolute' | 'relative';
```

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) &gt; [to](./kibana-plugin-plugins-data-server.timerange.to.md)
## TimeRange.to property
<b>Signature:</b>
```typescript
to: string;
```

View file

@ -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 || '';
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}
};

View file

@ -565,7 +565,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)
@ -1645,14 +1638,12 @@ export function plugin(initializerContext: PluginInitializerContext<ConfigSchema
// 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-forgotten-export) The symbol "QueryService" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "QueryStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@ -2147,14 +2138,11 @@ export type TimeHistoryContract = PublicMethodsOf<TimeHistory>;
// 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)
//
@ -2197,6 +2185,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

View file

@ -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(() => {

View file

@ -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()', () => {

View file

@ -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)
@ -941,14 +934,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)
//
@ -1063,14 +1054,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)
//
@ -1120,6 +1108,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

View file

@ -0,0 +1,5 @@
{
"rules": {
"@typescript-eslint/consistent-type-definitions": 0
}
}

View file

@ -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<string, any>
) => {
return telemetryData;
};
export const extractBaseEmbeddableInput = (state: EmbeddableInput) => {
return { state, references: [] as SavedObjectReference[] };
};
export const injectBaseEmbeddableInput = (
state: EmbeddableInput,
references: SavedObjectReference[]
) => {
return state;
};

View file

@ -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[];
};

View file

@ -1,7 +1,7 @@
{
"id": "embeddable",
"version": "kibana",
"server": false,
"server": true,
"ui": true,
"requiredPlugins": [
"inspector",

View file

@ -78,6 +78,8 @@ export {
EmbeddableRendererProps,
} from './lib';
export { EnhancementRegistryDefinition } from './types';
export function plugin(initializerContext: PluginInitializerContext) {
return new EmbeddablePublicPlugin(initializerContext);
}

View file

@ -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;
};

View file

@ -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;

View file

@ -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<EmbeddableInput> {
// 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;

View file

@ -40,5 +40,8 @@ export type EmbeddableFactoryDefinition<
| 'savedObjectMetaData'
| 'canCreateNew'
| 'getDefaultInput'
| 'telemetry'
| 'extract'
| 'inject'
>
>;

View file

@ -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.

View file

@ -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;

View file

@ -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;

View file

@ -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 };

View file

@ -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),

View file

@ -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, {});
});
});

View file

@ -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<I, O, E>
) => () => EmbeddableFactory<I, O, E>;
registerEnhancement: (enhancement: EnhancementRegistryDefinition) => void;
setCustomEmbeddableFactoryProvider: (customProvider: EmbeddableFactoryProvider) => void;
}
export interface EmbeddableStart {
export interface EmbeddableStart extends PersistableState<EmbeddableInput> {
getEmbeddableFactory: <
I extends EmbeddableInput = EmbeddableInput,
O extends EmbeddableOutput = EmbeddableOutput,
@ -88,6 +103,7 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
EmbeddableFactoryDefinition
> = 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<EmbeddableSetup, Embeddabl
return {
registerEmbeddableFactory: this.registerEmbeddableFactory,
registerEnhancement: this.registerEnhancement,
setCustomEmbeddableFactoryProvider: (provider: EmbeddableFactoryProvider) => {
if (this.customEmbeddableFactoryProvider) {
throw new Error(
@ -168,6 +185,9 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
},
EmbeddablePanel: getEmbeddablePanelHoc(),
getEmbeddablePanel: getEmbeddablePanelHoc,
telemetry: this.telemetry,
extract: this.extract,
inject: this.inject,
};
}
@ -177,6 +197,103 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
}
}
private telemetry = (state: EmbeddableInput, telemetryData: Record<string, any> = {}) => {
const enhancements: Record<string, any> = 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<EmbeddableSetup, Embeddabl
this.ensureFactoryExists(embeddableFactoryId);
const factory = this.embeddableFactories.get(embeddableFactoryId);
if (!factory) {
throw new Error(
`Embeddable factory [embeddableFactoryId = ${embeddableFactoryId}] does not exist.`
);
}
return factory as EmbeddableFactory<I, O, E>;
};

View file

@ -25,8 +25,24 @@ import {
IEmbeddable,
EmbeddableFactoryDefinition,
} from './lib/embeddables';
import {
PersistableState,
PersistableStateDefinition,
SerializableState,
} from '../../kibana_utils/common';
export type EmbeddableFactoryRegistry = Map<string, EmbeddableFactory>;
export type EnhancementsRegistry = Map<string, EnhancementRegistryItem>;
export interface EnhancementRegistryDefinition<P extends SerializableState = SerializableState>
extends PersistableStateDefinition<P> {
id: string;
}
export interface EnhancementRegistryItem<P extends SerializableState = SerializableState>
extends PersistableState<P> {
id: string;
}
export type EmbeddableFactoryProvider = <
I extends EmbeddableInput = EmbeddableInput,

View file

@ -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();

View file

@ -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<object, object> {
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<string, any> = {}) => {
const enhancements: Record<string, any> = 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: [] };
},
}
);
};
}

View file

@ -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<string, EmbeddableRegistryItem>;
export type EnhancementsRegistry = Map<string, EnhancementRegistryItem>;
export interface EnhancementRegistryDefinition<P extends SerializableState = SerializableState>
extends PersistableStateDefinition<P> {
id: string;
}
export interface EnhancementRegistryItem<P extends SerializableState = SerializableState>
extends PersistableState<P> {
id: string;
}
export interface EmbeddableRegistryDefinition<P extends EmbeddableInput = EmbeddableInput>
extends PersistableStateDefinition<P> {
id: string;
}
export interface EmbeddableRegistryItem<P extends EmbeddableInput = EmbeddableInput>
extends PersistableState<P> {
id: string;
}

View file

@ -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';

View file

@ -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<P extends SerializableState = SerializableState> {
/**
* function to extract telemetry information
* @param state
* @param collector
*/
telemetry: (state: P, collector: Record<string, any>) => Record<string, any>;
/**
* 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<P extends SerializableState = SerializableState> = Partial<
PersistableState<P>
>;

View file

@ -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<Config extends object = object, Context = object> {
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<Config extends object = object, Context = object>
/**
* Props provided to `CollectConfig` component on every re-render.
*/
export interface CollectConfigProps<Config extends object = object, Context = object> {
export interface CollectConfigProps<
Config extends SerializableState = SerializableState,
Context = object
> {
/**
* Current (latest) config of the item.
*/

View file

@ -0,0 +1,5 @@
{
"rules": {
"@typescript-eslint/consistent-type-definitions": 0
}
}

View file

@ -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';

View file

@ -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';

View file

@ -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<Config>;

View file

@ -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<typeof APPLY_FILTER_TRIGGER>;

View file

@ -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;
};
}

View file

@ -0,0 +1,5 @@
{
"rules": {
"@typescript-eslint/consistent-type-definitions": 0
}
}

View file

@ -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<Config extends BaseActionConfig = BaseActionConfig> = {
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[];
};

View file

@ -7,7 +7,7 @@
"uiActions",
"licensing"
],
"server": false,
"server": true,
"ui": true,
"requiredBundles": [
"kibanaUtils",

View file

@ -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[];

View file

@ -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<DashboardDrilldownConfig>) {
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<UrlDrilldownConfig>) {
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<any>
>) 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<ActionFactory<any>> }) {
const [state, setState] = useState<{
currentActionFactory?: ActionFactory;
config?: ActionBaseConfig;
config?: BaseActionConfig;
selectedTriggers?: TriggerId[];
}>({});

View file

@ -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[]) => {

View file

@ -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,

View file

@ -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[],
};

View file

@ -60,7 +60,7 @@ class MockDynamicActionManager implements PublicMethodsOf<DynamicActionManager>
async updateEvent(
eventId: string,
action: UiActionsEnhancedSerializedAction<unknown>,
action: UiActionsEnhancedSerializedAction,
triggers: Array<keyof TriggerContextMapping>
) {
const state = this.state.get();

View file

@ -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 (
<FlyoutDrilldownWizard
drilldownActionFactories={[urlFactory as ActionFactory, dashboardFactory as ActionFactory]}
{...otherProps}
/>
);
return <FlyoutDrilldownWizard drilldownActionFactories={mockActionFactories} {...otherProps} />;
})
.add('open in flyout - create', () => {
return (
<EuiFlyout onClose={() => {}}>
<FlyoutDrilldownWizard
drilldownActionFactories={[
urlFactory as ActionFactory,
dashboardFactory as ActionFactory,
]}
{...otherProps}
/>
<FlyoutDrilldownWizard drilldownActionFactories={mockActionFactories} {...otherProps} />
</EuiFlyout>
);
})
@ -48,13 +36,10 @@ storiesOf('components/FlyoutDrilldownWizard', module)
return (
<EuiFlyout onClose={() => {}}>
<FlyoutDrilldownWizard
drilldownActionFactories={[
urlFactory as ActionFactory,
dashboardFactory as ActionFactory,
]}
drilldownActionFactories={mockActionFactories}
initialDrilldownWizardConfig={{
name: 'My fancy drilldown',
actionFactory: urlFactory as any,
actionFactory: mockActionFactories[1],
actionConfig: {
url: 'https://elastic.co',
openInNewTab: true,
@ -70,10 +55,10 @@ storiesOf('components/FlyoutDrilldownWizard', module)
return (
<EuiFlyout onClose={() => {}}>
<FlyoutDrilldownWizard
drilldownActionFactories={[dashboardFactory as ActionFactory]}
drilldownActionFactories={[mockActionFactories[1]]}
initialDrilldownWizardConfig={{
name: 'My fancy drilldown',
actionFactory: urlFactory as any,
actionFactory: mockActionFactories[1],
actionConfig: {
url: 'https://elastic.co',
openInNewTab: true,

View file

@ -16,11 +16,15 @@ import {
txtEditDrilldownTitle,
} from './i18n';
import { DrilldownHelloBar } from '../drilldown_hello_bar';
import { ActionFactory, BaseActionFactoryContext } from '../../../dynamic_actions';
import {
ActionFactory,
BaseActionConfig,
BaseActionFactoryContext,
} from '../../../dynamic_actions';
import { Trigger, TriggerId } from '../../../../../../../src/plugins/ui_actions/public';
import { ActionFactoryPlaceContext } from '../types';
export interface DrilldownWizardConfig<ActionConfig extends object = object> {
export interface DrilldownWizardConfig<ActionConfig extends BaseActionConfig = BaseActionConfig> {
name: string;
actionFactory?: ActionFactory;
actionConfig?: ActionConfig;
@ -28,7 +32,7 @@ export interface DrilldownWizardConfig<ActionConfig extends object = object> {
}
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<CurrentActionConfig extends object = object>({
export function FlyoutDrilldownWizard<
CurrentActionConfig extends BaseActionConfig = BaseActionConfig
>({
onClose,
onBack,
onSubmit = () => {},

View file

@ -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[];

View file

@ -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<SupportedTriggers> = {
triggers: SupportedTriggers[];
},
ExecutionContext extends TriggerContextMapping[SupportedTriggers] = TriggerContextMapping[SupportedTriggers]
> {
> extends PersistableStateDefinition<SerializedEvent> {
/**
* Globally unique identifier for this drilldown.
*/

View file

@ -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

View file

@ -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<SupportedTriggers> = {
triggers: SupportedTriggers[];
},
ActionContext extends TriggerContextMapping[SupportedTriggers] = TriggerContextMapping[SupportedTriggers]
> implements Omit<Presentable<FactoryContext>, 'getHref'>, Configurable<Config, FactoryContext> {
> implements
Omit<Presentable<FactoryContext>, 'getHref'>,
Configurable<Config, FactoryContext>,
PersistableState<SerializedEvent> {
constructor(
protected readonly def: ActionFactoryDefinition<
Config,
@ -121,4 +131,16 @@ export class ActionFactory<
);
});
}
public telemetry(state: SerializedEvent, telemetryData: Record<string, any>) {
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;
}
}

View file

@ -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<SupportedTriggers> = {
triggers: SupportedTriggers[];
},
ActionContext extends TriggerContextMapping[SupportedTriggers] = TriggerContextMapping[SupportedTriggers]
> extends Partial<Omit<Presentable<FactoryContext>, 'getHref'>>,
Configurable<Config, FactoryContext> {
Configurable<Config, FactoryContext>,
PersistableStateDefinition<SerializedEvent> {
/**
* 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

View file

@ -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<string, any>) => {
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<SerializableState>;
};

View file

@ -250,7 +250,7 @@ describe('DynamicActionManager', () => {
uiActions.registerActionFactory(actionFactoryDefinition1);
await manager.start();
const action: SerializedAction<unknown> = {
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<unknown> = {
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<unknown> = {
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<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition1.id,
name: 'foo',
config: {},
@ -348,7 +348,7 @@ describe('DynamicActionManager', () => {
uiActions.registerActionFactory(actionFactoryDefinition1);
await manager.start();
const action: SerializedAction<unknown> = {
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<unknown> = {
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<unknown> = {
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<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition1.id,
name: 'foo',
config: {},
@ -432,7 +432,7 @@ describe('DynamicActionManager', () => {
uiActions.registerActionFactory(actionFactoryDefinition1);
await manager.start();
const action: SerializedAction<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition1.id,
name: 'foo',
config: {},
@ -457,7 +457,7 @@ describe('DynamicActionManager', () => {
expect(registeredAction1.getDisplayName()).toBe('Action 3');
const action: SerializedAction<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition2.id,
name: 'foo',
config: {},
@ -479,7 +479,7 @@ describe('DynamicActionManager', () => {
uiActions.registerActionFactory(actionFactoryDefinition2);
await manager.start();
const action: SerializedAction<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition2.id,
name: 'foo',
config: {},
@ -505,7 +505,7 @@ describe('DynamicActionManager', () => {
uiActions.registerActionFactory(actionFactoryDefinition2);
await manager.start();
const action: SerializedAction<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition2.id,
name: 'foo',
config: {},
@ -524,7 +524,7 @@ describe('DynamicActionManager', () => {
uiActions.registerActionFactory(actionFactoryDefinition2);
await manager.start();
const action: SerializedAction<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition2.id,
name: 'foo',
config: {},
@ -552,7 +552,7 @@ describe('DynamicActionManager', () => {
uiActions.registerActionFactory(actionFactoryDefinition2);
await manager.start();
const action: SerializedAction<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition2.id,
name: 'foo',
config: {},
@ -580,7 +580,7 @@ describe('DynamicActionManager', () => {
expect(registeredAction1.getDisplayName()).toBe('Action 3');
const action: SerializedAction<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition2.id,
name: 'foo',
config: {},
@ -604,7 +604,7 @@ describe('DynamicActionManager', () => {
uiActions.registerActionFactory(actionFactoryDefinition2);
await manager.start();
const action: SerializedAction<unknown> = {
const action: SerializedAction = {
factoryId: actionFactoryDefinition2.id,
name: 'foo',
config: {},

View file

@ -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<object>);
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<unknown>,
triggers: Array<keyof TriggerContextMapping>
) {
public async createEvent(action: SerializedAction, triggers: Array<keyof TriggerContextMapping>) {
const event: SerializedEvent = {
eventId: uuidv4(),
triggers,
@ -231,7 +228,7 @@ export class DynamicActionManager {
*/
public async updateEvent(
eventId: string,
action: SerializedAction<unknown>,
action: SerializedAction,
triggers: Array<keyof TriggerContextMapping>
) {
const event: SerializedEvent = {

View file

@ -5,21 +5,9 @@
*/
import { TriggerId } from '../../../../../src/plugins/ui_actions/public';
import { SerializedAction, SerializedEvent, BaseActionConfig } from '../../common/types';
export interface SerializedAction<Config = unknown> {
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

View file

@ -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';

View file

@ -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;

View file

@ -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<UiActionsServiceEnhancements, 'getActionFactory' | 'getActionFactories'> {
Pick<
UiActionsServiceEnhancements,
'getActionFactory' | 'getActionFactories' | 'telemetry' | 'extract' | 'inject'
> {
FlyoutManageDrilldowns: ReturnType<typeof createFlyoutManageDrilldowns>;
}
@ -87,7 +91,7 @@ export class AdvancedUiActionsPublicPlugin
public setup(
core: CoreSetup<StartDependencies>,
{ 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,

View file

@ -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(() => {

View file

@ -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<DynamicActionsState> {
protected readonly actionFactories: ActionFactoryRegistry;
protected readonly deps: Omit<UiActionsServiceEnhancementsParams, 'actionFactories'>;
@ -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<SupportedTriggers> = {
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<SupportedTriggers> = {
triggers: SupportedTriggers[];
@ -102,6 +111,9 @@ export class UiActionsServiceEnhancements {
licenseFeatureName,
supportedTriggers,
isCompatible,
telemetry,
extract,
inject,
}: DrilldownDefinition<Config, SupportedTriggers, FactoryContext, ExecutionContext>): 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<string, any> = {}) => {
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;
}),
};
};
}

View file

@ -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;

View file

@ -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<string, any>) => {
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;
};

View file

@ -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';

View file

@ -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<SetupContract, StartContract, SetupDependencies> {
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<string, any> = {}) => {
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;
}),
};
};
}

View file

@ -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<string, ActionFactory>;
export interface ActionFactoryDefinition<P extends SerializedEvent = SerializedEvent>
extends PersistableStateDefinition<P> {
id: string;
}
export interface ActionFactory<P extends SerializedEvent = SerializedEvent>
extends PersistableState<P> {
id: string;
}
export { SerializedEvent, SerializedAction, DynamicActionsState };