Skip to content

Commit

Permalink
[Drilldowns] misc improvements & fixes (#75276)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Dosant committed Aug 19, 2020
1 parent 4d3095f commit 9dfd153
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 17 deletions.
4 changes: 2 additions & 2 deletions src/plugins/kibana_utils/public/ui/configurable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ export interface Configurable<Config extends object = object, Context = object>
/**
* 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -52,10 +53,24 @@ export class DashboardHelloWorldOnlyRangeSelectDrilldown
name: '',
});

public readonly isConfigValid = (config: Config): config is Config => {
public readonly isConfigValid = (
config: Config,
context: BaseActionFactoryContext<typeof SELECT_RANGE_TRIGGER>
): 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<boolean> {
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)}`);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,9 @@ export function Demo({ actionFactories }: { actionFactories: Array<ActionFactory

setState({
currentActionFactory: newActionFactory,
config: newActionFactory.createConfig(),
config: newActionFactory.createConfig({
triggers: state.selectedTriggers ?? [],
}),
});
}

Expand Down Expand Up @@ -255,7 +257,11 @@ export function Demo({ actionFactories }: { actionFactories: Array<ActionFactory
<div>Action Factory Config: {JSON.stringify(state.config)}</div>
<div>
Is config valid:{' '}
{JSON.stringify(state.currentActionFactory?.isConfigValid(state.config!) ?? false)}
{JSON.stringify(
state.currentActionFactory?.isConfigValid(state.config!, {
triggers: state.selectedTriggers ?? [],
}) ?? false
)}
</div>
<div>Picked trigger: {state.selectedTriggers?.[0]}</div>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface FlyoutDrilldownWizardProps<
}

function useWizardConfigState(
actionFactoryContext: BaseActionFactoryContext,
initialDrilldownWizardConfig?: DrilldownWizardConfig
): [
DrilldownWizardConfig,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -147,7 +151,18 @@ export function FlyoutDrilldownWizard<CurrentActionConfig extends object = objec
const [
wizardConfig,
{ setActionFactory, setActionConfig, setName, setSelectedTriggers },
] = useWizardConfigState(initialDrilldownWizardConfig);
] = useWizardConfigState(
{ ...extraActionFactoryContext, triggers: supportedTriggers },
initialDrilldownWizardConfig
);

const actionFactoryContext: BaseActionFactoryContext = useMemo(
() => ({
...extraActionFactoryContext,
triggers: wizardConfig.selectedTriggers ?? [],
}),
[extraActionFactoryContext, wizardConfig.selectedTriggers]
);

const isActionValid = (
config: DrilldownWizardConfig
Expand All @@ -157,17 +172,12 @@ export function FlyoutDrilldownWizard<CurrentActionConfig extends object = objec
if (!wizardConfig.actionConfig) return false;
if (!wizardConfig.selectedTriggers || wizardConfig.selectedTriggers.length === 0) return false;

return wizardConfig.actionFactory.isConfigValid(wizardConfig.actionConfig);
return wizardConfig.actionFactory.isConfigValid(
wizardConfig.actionConfig,
actionFactoryContext
);
};

const actionFactoryContext: BaseActionFactoryContext = useMemo(
() => ({
...extraActionFactoryContext,
triggers: wizardConfig.selectedTriggers ?? [],
}),
[extraActionFactoryContext, wizardConfig.selectedTriggers]
);

const footer = (
<EuiButton
onClick={() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExecutionContext>
): Promise<boolean>;

/**
* Implements the "navigation" action of the drilldown. This happens when
* user clicks something in the UI that executes a trigger to which this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' } })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export class UiActionsServiceEnhancements {
getHref,
minimalLicense,
supportedTriggers,
isCompatible,
}: DrilldownDefinition<Config, SupportedTriggers, FactoryContext, ExecutionContext>): void => {
const actionFactory: ActionFactoryDefinition<
Config,
Expand All @@ -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<Config, SupportedTriggers, FactoryContext, ExecutionContext>;

Expand Down

0 comments on commit 9dfd153

Please sign in to comment.