From 9dfd1539d9f50bdeba65a7d30486aff9b20bb32a Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Tue, 18 Aug 2020 17:50:02 +0200 Subject: [PATCH] [Drilldowns] misc improvements & fixes (#75276) Added support for isCompatible. It is checked during execution. Pass actionFactory context into createConfig, IsConfigValid Fix bug that selectedTriggers wasn't reset when switching action factories Check if license is active in action factories --- .../kibana_utils/public/ui/configurable.ts | 4 +-- .../index.tsx | 17 +++++++++- .../components/action_wizard/test_data.tsx | 10 ++++-- .../flyout_drilldown_wizard.tsx | 32 ++++++++++++------- .../public/drilldowns/drilldown_definition.ts | 9 ++++++ .../dynamic_actions/action_factory.test.ts | 8 +++++ .../public/dynamic_actions/action_factory.ts | 3 +- .../ui_actions_service_enhancements.test.ts | 13 ++++++++ .../ui_actions_service_enhancements.ts | 4 +++ 9 files changed, 83 insertions(+), 17 deletions(-) diff --git a/src/plugins/kibana_utils/public/ui/configurable.ts b/src/plugins/kibana_utils/public/ui/configurable.ts index a4a9f09c1c0e09..3fa5cdc8b5e479 100644 --- a/src/plugins/kibana_utils/public/ui/configurable.ts +++ b/src/plugins/kibana_utils/public/ui/configurable.ts @@ -26,12 +26,12 @@ export interface Configurable /** * Create default config for this item, used when item is created for the first time. */ - readonly createConfig: () => Config; + readonly createConfig: (context: Context) => Config; /** * Is this config valid. Used to validate user's input before saving. */ - readonly isConfigValid: (config: Config) => boolean; + readonly isConfigValid: (config: Config, context: Context) => boolean; /** * `UiComponent` to be rendered when collecting configuration for this item. diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_only_range_select_drilldown/index.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_only_range_select_drilldown/index.tsx index 38e2abc1f89a33..7394690a61eae8 100644 --- a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_only_range_select_drilldown/index.tsx +++ b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_hello_world_only_range_select_drilldown/index.tsx @@ -11,6 +11,7 @@ import { UiActionsEnhancedDrilldownDefinition as Drilldown } from '../../../../p import { RangeSelectContext } from '../../../../../src/plugins/embeddable/public'; import { CollectConfigProps } from '../../../../../src/plugins/kibana_utils/public'; import { SELECT_RANGE_TRIGGER } from '../../../../../src/plugins/ui_actions/public'; +import { BaseActionFactoryContext } from '../../../../plugins/ui_actions_enhanced/public/dynamic_actions'; export interface Config { name: string; @@ -52,10 +53,24 @@ export class DashboardHelloWorldOnlyRangeSelectDrilldown name: '', }); - public readonly isConfigValid = (config: Config): config is Config => { + public readonly isConfigValid = ( + config: Config, + context: BaseActionFactoryContext + ): config is Config => { + // eslint-disable-next-line no-console + console.log('Showcasing, that can access action factory context:', context); + return !!config.name; }; + /** + * Showcase isCompatible. Disabled drilldown action in case if range.length === 0 + */ + isCompatible(config: Config, context: RangeSelectContext): Promise { + if (context.data.range.length === 0) return Promise.resolve(false); + return Promise.resolve(true); + } + public readonly execute = async (config: Config, context: RangeSelectContext) => { alert(`Hello, ${config.name}, your selected range: ${JSON.stringify(context.data.range)}`); }; diff --git a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx index 2ac8dd63925520..d48cb13b1a4705 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/test_data.tsx @@ -220,7 +220,9 @@ export function Demo({ actionFactories }: { actionFactories: ArrayAction Factory Config: {JSON.stringify(state.config)}
Is config valid:{' '} - {JSON.stringify(state.currentActionFactory?.isConfigValid(state.config!) ?? false)} + {JSON.stringify( + state.currentActionFactory?.isConfigValid(state.config!, { + triggers: state.selectedTriggers ?? [], + }) ?? false + )}
Picked trigger: {state.selectedTriggers?.[0]}
diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx index 4b84a177e682c2..a908d53bf6ae75 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx @@ -57,6 +57,7 @@ export interface FlyoutDrilldownWizardProps< } function useWizardConfigState( + actionFactoryContext: BaseActionFactoryContext, initialDrilldownWizardConfig?: DrilldownWizardConfig ): [ DrilldownWizardConfig, @@ -102,7 +103,10 @@ function useWizardConfigState( setWizardConfig({ ...wizardConfig, actionFactory, - actionConfig: actionConfigCache[actionFactory.id] ?? actionFactory.createConfig(), + actionConfig: + actionConfigCache[actionFactory.id] ?? + actionFactory.createConfig(actionFactoryContext), + selectedTriggers: [], }); } else { if (wizardConfig.actionFactory?.id) { @@ -147,7 +151,18 @@ export function FlyoutDrilldownWizard ({ + ...extraActionFactoryContext, + triggers: wizardConfig.selectedTriggers ?? [], + }), + [extraActionFactoryContext, wizardConfig.selectedTriggers] + ); const isActionValid = ( config: DrilldownWizardConfig @@ -157,17 +172,12 @@ export function FlyoutDrilldownWizard ({ - ...extraActionFactoryContext, - triggers: wizardConfig.selectedTriggers ?? [], - }), - [extraActionFactoryContext, wizardConfig.selectedTriggers] - ); - const footer = ( { diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts index 0efde6214ab2ba..ff455c6ae45b66 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/drilldown_definition.ts @@ -106,6 +106,15 @@ export interface DrilldownDefinition< */ getDisplayName: () => string; + /** + * isCompatible during execution + * Could be used to prevent drilldown from execution + */ + isCompatible?( + config: Config, + context: ExecutionContext | ActionExecutionContext + ): Promise; + /** * Implements the "navigation" action of the drilldown. This happens when * user clicks something in the UI that executes a trigger to which this diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.test.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.test.ts index 159e8be95f2727..a07fed8486438a 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.test.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.test.ts @@ -37,6 +37,14 @@ describe('License & ActionFactory', () => { expect(factory.isCompatibleLicence()).toBe(false); }); + test('licence has expired', async () => { + const factory = new ActionFactory({ ...def, minimalLicense: 'gold' }, () => + licensingMock.createLicense({ license: { type: 'gold', status: 'expired' } }) + ); + expect(await factory.isCompatible({ triggers: [] })).toBe(true); + expect(factory.isCompatibleLicence()).toBe(false); + }); + test('enough license level', async () => { const factory = new ActionFactory({ ...def, minimalLicense: 'gold' }, () => licensingMock.createLicense({ license: { type: 'gold' } }) diff --git a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts index cb764d99b4a039..35e06ab036fc97 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/dynamic_actions/action_factory.ts @@ -70,7 +70,8 @@ export class ActionFactory< */ public isCompatibleLicence() { if (!this.minimalLicense) return true; - return this.getLicence().hasAtLeast(this.minimalLicense); + const licence = this.getLicence(); + return licence.isAvailable && licence.isActive && licence.hasAtLeast(this.minimalLicense); } public create( diff --git a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.test.ts b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.test.ts index ab17c3f549dcd9..08823833b9af28 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.test.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.test.ts @@ -75,5 +75,18 @@ describe('UiActionsService', () => { 'Action factory [actionFactoryId = UNKNOWN_ID] does not exist.' ); }); + + test('isCompatible from definition is used on registered factory', async () => { + const service = new UiActionsServiceEnhancements({ getLicenseInfo }); + + service.registerActionFactory({ + ...factoryDefinition1, + isCompatible: () => Promise.resolve(false), + }); + + await expect( + service.getActionFactory(factoryDefinition1.id).isCompatible({ triggers: [] }) + ).resolves.toBe(false); + }); }); }); diff --git a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts index 10786697243dcb..95753295148356 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/services/ui_actions_service_enhancements.ts @@ -95,6 +95,7 @@ export class UiActionsServiceEnhancements { getHref, minimalLicense, supportedTriggers, + isCompatible, }: DrilldownDefinition): void => { const actionFactory: ActionFactoryDefinition< Config, @@ -119,6 +120,9 @@ export class UiActionsServiceEnhancements { getDisplayName: () => serializedAction.name, execute: async (context) => await execute(serializedAction.config, context), getHref: getHref ? async (context) => getHref(serializedAction.config, context) : undefined, + isCompatible: isCompatible + ? async (context) => isCompatible(serializedAction.config, context) + : undefined, }), } as ActionFactoryDefinition;