Skip to content

Commit 0e896cc

Browse files
committed
feat: 🎸 add first version of dynamic action manager
1 parent dd60726 commit 0e896cc

File tree

11 files changed

+120
-44
lines changed

11 files changed

+120
-44
lines changed

‎src/plugins/ui_actions/public/actions/action_factory.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@
1919

2020
import { uiToReactComponent } from '../../../kibana_react/public';
2121
import { Presentable } from '../util/presentable';
22-
import { ActionDefinition } from './action_definition';
22+
import { ActionDefinition } from './action';
2323
import {
2424
AnyActionFactoryDefinition,
2525
AFDConfig as Config,
2626
AFDFactoryContext as FactoryContext,
2727
AFDActionContext as ActionContext,
2828
} from './action_factory_definition';
2929
import { Configurable } from '../util';
30+
import { SerializedAction } from './types';
3031

3132
export class ActionFactory<D extends AnyActionFactoryDefinition>
3233
implements Presentable<FactoryContext<D>>, Configurable<Config<D>> {
@@ -62,8 +63,10 @@ export class ActionFactory<D extends AnyActionFactoryDefinition>
6263
return this.definition.getHref(context);
6364
}
6465

65-
public create(config: Config<D>): ActionDefinition<ActionContext<D>> {
66-
return this.definition.create(config);
66+
public create(
67+
serializedAction: Omit<SerializedAction<Config<D>>, 'factoryId'>
68+
): ActionDefinition<ActionContext<D>> {
69+
return this.definition.create(serializedAction);
6770
}
6871
}
6972

‎src/plugins/ui_actions/public/actions/action_factory_definition.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
* under the License.
1818
*/
1919

20-
import { ActionDefinition } from './action_definition';
20+
import { ActionDefinition } from './action';
2121
import { Presentable, Configurable } from '../util';
22+
import { SerializedAction } from './types';
2223

2324
/**
2425
* This is a convenience interface for registering new action factories.
@@ -39,7 +40,9 @@ export interface ActionFactoryDefinition<
3940
* This method should return a definition of a new action, normally used to
4041
* register it in `ui_actions` registry.
4142
*/
42-
create(config: Config): ActionDefinition<any>; // TODO: FIX THIS....
43+
create(
44+
serializedAction: Omit<SerializedAction<Config>, 'factoryId'>
45+
): ActionDefinition<ActionContext>;
4346
}
4447

4548
export type AnyActionFactoryDefinition = ActionFactoryDefinition<any, any, any>;

‎src/plugins/ui_actions/public/actions/action_internal.ts‎

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { Action, ActionContext as Context, AnyActionDefinition } from './action'
2121
import { Presentable } from '../util/presentable';
2222
import { uiToReactComponent } from '../../../kibana_react/public';
2323
import { ActionType } from '../types';
24+
import { SerializedAction } from './types';
2425

2526
export class ActionInternal<A extends AnyActionDefinition>
2627
implements Action<Context<A>>, Presentable<Context<A>> {
@@ -45,7 +46,7 @@ export class ActionInternal<A extends AnyActionDefinition>
4546
}
4647

4748
public getDisplayName(context: Context<A>): string {
48-
if (!this.definition.getDisplayName) return '';
49+
if (!this.definition.getDisplayName) return `Action: ${this.id}`;
4950
return this.definition.getDisplayName(context);
5051
}
5152

@@ -64,8 +65,7 @@ export class ActionInternal<A extends AnyActionDefinition>
6465
throw new Error('Action does not have a config.');
6566
}
6667

67-
const serialized: SerializedAction = {
68-
id: this.id,
68+
const serialized: SerializedAction<unknown> = {
6969
factoryId: this.type,
7070
name: this.name,
7171
config: this.config,
@@ -74,17 +74,10 @@ export class ActionInternal<A extends AnyActionDefinition>
7474
return serialized;
7575
}
7676

77-
public deserialize({ name, config }: SerializedAction) {
77+
public deserialize({ name, config }: SerializedAction<unknown>) {
7878
this.name = name;
79-
this.config = config;
79+
this.config = config as object;
8080
}
8181
}
8282

8383
export type AnyActionInternal = ActionInternal<any>;
84-
85-
export interface SerializedAction<Config extends object = object> {
86-
readonly id: string;
87-
readonly factoryId: string;
88-
readonly name: string;
89-
readonly config: Config;
90-
}

‎src/plugins/ui_actions/public/actions/create_action.ts‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
* under the License.
1818
*/
1919

20-
import { ActionByType } from './action';
21-
import { ActionType } from '../types';
22-
import { ActionDefinition } from './action_definition';
20+
import { Ensure } from '@kbn/utility-types';
21+
import { Action } from './action';
22+
import { TriggerContextMapping } from '../types';
23+
import { ActionDefinition } from './action';
2324

24-
export function createAction<T extends ActionType>(action: ActionDefinition<T>): ActionByType<T> {
25+
export function createAction<T extends keyof TriggerContextMapping>(
26+
action: ActionDefinition<Ensure<TriggerContextMapping[T], object>>
27+
): Action<TriggerContextMapping[T], T> {
2528
return {
2629
getIconType: () => undefined,
2730
order: 0,
@@ -30,5 +33,5 @@ export function createAction<T extends ActionType>(action: ActionDefinition<T>):
3033
getDisplayName: () => '',
3134
getHref: () => undefined,
3235
...action,
33-
} as ActionByType<T>;
36+
} as Action<TriggerContextMapping[T], T>;
3437
}

‎src/plugins/ui_actions/public/actions/dynamic_action_manager.ts‎

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,77 @@
1717
* under the License.
1818
*/
1919

20-
import { ActionStorage } from './dynamic_action_storage';
20+
import { v4 as uuidv4 } from 'uuid';
21+
import { ActionStorage, SerializedEvent } from './dynamic_action_storage';
2122
import { UiActionsService } from '../service';
23+
import { SerializedAction } from './types';
24+
import { ActionDefinition } from './action';
2225

2326
export interface DynamicActionManagerParams {
2427
storage: ActionStorage;
25-
uiActions: UiActionsService;
28+
uiActions: Pick<UiActionsService, 'registerAction' | 'attachAction' | 'getActionFactory'>;
29+
isCompatible: <C = unknown>(context: C) => Promise<boolean>;
2630
}
2731

2832
export class DynamicActionManager {
33+
static idPrefixCounter = 0;
34+
35+
private readonly idPrefix = 'DYN_ACTION_' + DynamicActionManager.idPrefixCounter++;
36+
2937
constructor(protected readonly params: DynamicActionManagerParams) {}
38+
39+
protected generateActionId(eventId: string): string {
40+
return this.idPrefix + eventId;
41+
}
42+
43+
public async start() {
44+
const events = await this.params.storage.list();
45+
46+
for (const event of events) {
47+
this.reviveAction(event);
48+
}
49+
}
50+
51+
public async stop() {
52+
/*
53+
const { storage, uiActions } = this.params;
54+
const events = await storage.list();
55+
56+
for (const event of events) {
57+
uiActions.detachAction(event.triggerId, event.action.id);
58+
uiActions.unregisterAction(event.action.id);
59+
}
60+
*/
61+
}
62+
63+
public async createEvent(action: SerializedAction<unknown>, triggerId = 'VALUE_CLICK_TRIGGER') {
64+
const event: SerializedEvent = {
65+
eventId: uuidv4(),
66+
triggerId,
67+
action,
68+
};
69+
70+
await this.params.storage.create(event);
71+
this.reviveAction(event);
72+
}
73+
74+
protected reviveAction(event: SerializedEvent) {
75+
const { eventId, triggerId, action } = event;
76+
const { uiActions, isCompatible } = this.params;
77+
const { name } = action;
78+
79+
const actionId = this.generateActionId(eventId);
80+
const factory = uiActions.getActionFactory(event.action.factoryId);
81+
const actionDefinition: ActionDefinition<any> = {
82+
...factory.create(action as SerializedAction<object>),
83+
id: actionId,
84+
isCompatible,
85+
getDisplayName: () => name,
86+
getIconType: context => factory.getIconType(context),
87+
};
88+
89+
uiActions.attachAction(triggerId as any, actionDefinition as any);
90+
}
91+
92+
protected killAction(actionId: string) {}
3093
}

‎src/plugins/ui_actions/public/actions/dynamic_action_storage.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@
1717
* under the License.
1818
*/
1919

20-
import { SerializedAction } from './action_internal';
20+
import { SerializedAction } from './types';
2121

2222
/**
2323
* Serialized representation of event-action pair, used to persist in storage.
2424
*/
2525
export interface SerializedEvent {
2626
eventId: string;
2727
triggerId: string;
28-
action: SerializedAction;
28+
action: SerializedAction<unknown>;
2929
}
3030

3131
/**

‎src/plugins/ui_actions/public/actions/index.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ export * from './action_factory';
2424
export * from './create_action';
2525
export * from './incompatible_action_error';
2626
export * from './dynamic_action_storage';
27+
export * from './dynamic_action_manager';
28+
export * from './types';
Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,8 @@
1717
* under the License.
1818
*/
1919

20-
import { ActionType, ActionContextMapping } from '../types';
21-
import { Presentable } from '../util/presentable';
22-
23-
export interface ActionDefinition<T extends ActionType>
24-
extends Partial<Presentable<ActionContextMapping[T]>> {
25-
/**
26-
* ID of the action factory for this action. Action factories are registered
27-
* int X-Pack `ui_actions` plugin.
28-
*/
29-
readonly type?: T;
30-
31-
/**
32-
* Executes the action.
33-
*/
34-
execute(context: ActionContextMapping[T]): Promise<void>;
20+
export interface SerializedAction<Config> {
21+
readonly factoryId: string;
22+
readonly name: string;
23+
readonly config: Config;
3524
}

‎src/plugins/ui_actions/public/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ export {
4444
} from './util';
4545
export { Trigger, TriggerContext } from './triggers';
4646
export { TriggerContextMapping, TriggerId, ActionContextMapping, ActionType } from './types';
47-
export { ActionByType } from './actions';
47+
export { ActionByType, DynamicActionManager as UiActionsDynamicActionManager } from './actions';

‎src/plugins/ui_actions/public/service/ui_actions_service.test.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ describe('UiActionsService', () => {
102102
type: 'test' as ActionType,
103103
});
104104
});
105+
106+
test.todo('return action instance');
105107
});
106108

107109
describe('.getTriggerActions()', () => {
@@ -481,5 +483,7 @@ describe('UiActionsService', () => {
481483
test.todo('.getActionFactories() returns empty array if no action factories registered');
482484
test.todo('can register an action factory');
483485
test.todo('can retrieve all action factories');
486+
test.todo('can retrieve action factory by ID');
487+
test.todo('throws when retrieving action factory that does not exist');
484488
});
485489
});

0 commit comments

Comments
 (0)