[uiActions] notify action usage (#76294)

Notify feature usage when dynamic actions with specified license requirements are executed

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Anton Dosov 2020-09-02 16:00:38 +02:00 committed by GitHub
parent 5345af9281
commit 25c176255a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 247 additions and 68 deletions

View file

@ -44,6 +44,7 @@ export class DashboardToUrlDrilldown implements Drilldown<Config, UrlTrigger> {
public readonly order = 8;
readonly minimalLicense = 'gold'; // example of minimal license support
readonly licenseFeatureName = 'Sample URL Drilldown';
public readonly getDisplayName = () => 'Go to URL (example)';

View file

@ -15,6 +15,8 @@ const createSetupMock = (): jest.Mocked<FeatureUsageServiceSetup> => {
register: jest.fn(),
};
mock.register.mockImplementation(() => Promise.resolve());
return mock;
};
@ -23,6 +25,8 @@ const createStartMock = (): jest.Mocked<FeatureUsageServiceStart> => {
notifyUsage: jest.fn(),
};
mock.notifyUsage.mockImplementation(() => Promise.resolve());
return mock;
};

View file

@ -15,7 +15,7 @@ import {
urlDrilldownActionFactory,
} from './test_data';
import { ActionFactory } from '../../dynamic_actions';
import { licenseMock } from '../../../../licensing/common/licensing.mock';
import { licensingMock } from '../../../../licensing/public/mocks';
// TODO: afterEach is not available for it globally during setup
// https://github.com/elastic/kibana/issues/59469
@ -68,8 +68,12 @@ test('If not enough license, button is disabled', () => {
{
...urlDrilldownActionFactory,
minimalLicense: 'gold',
licenseFeatureName: 'Url Drilldown',
},
() => licenseMock.createLicense()
{
getLicense: () => licensingMock.createLicense(),
getFeatureUsageStart: () => licensingMock.createStart().featureUsage,
}
);
const screen = render(<Demo actionFactories={[dashboardFactory, urlWithGoldLicense]} />);

View file

@ -93,7 +93,7 @@ export const ActionWizard: React.FC<ActionWizardProps> = ({
if (
!currentActionFactory &&
actionFactories.length === 1 &&
actionFactories[0].isCompatibleLicence()
actionFactories[0].isCompatibleLicense()
) {
onActionFactoryChange(actionFactories[0]);
}
@ -314,8 +314,8 @@ const ActionFactorySelector: React.FC<ActionFactorySelectorProps> = ({
* make sure not compatible factories are in the end
*/
const ensureOrder = (factories: ActionFactory[]) => {
const compatibleLicense = factories.filter((f) => f.isCompatibleLicence());
const notCompatibleLicense = factories.filter((f) => !f.isCompatibleLicence());
const compatibleLicense = factories.filter((f) => f.isCompatibleLicense());
const notCompatibleLicense = factories.filter((f) => !f.isCompatibleLicense());
return [
...compatibleLicense.sort((f1, f2) => f2.order - f1.order),
...notCompatibleLicense.sort((f1, f2) => f2.order - f1.order),
@ -328,7 +328,7 @@ const ActionFactorySelector: React.FC<ActionFactorySelectorProps> = ({
<EuiFlexItem grow={false} key={actionFactory.id}>
<EuiToolTip
content={
!actionFactory.isCompatibleLicence() && (
!actionFactory.isCompatibleLicense() && (
<FormattedMessage
defaultMessage="Insufficient license level"
id="xpack.uiActionsEnhanced.components.actionWizard.insufficientLicenseLevelTooltip"
@ -341,7 +341,7 @@ const ActionFactorySelector: React.FC<ActionFactorySelectorProps> = ({
label={actionFactory.getDisplayName(context)}
data-test-subj={`${TEST_SUBJ_ACTION_FACTORY_ITEM}-${actionFactory.id}`}
onClick={() => onActionFactorySelected(actionFactory)}
disabled={!actionFactory.isCompatibleLicence()}
disabled={!actionFactory.isCompatibleLicense()}
>
{actionFactory.getIconType(context) && (
<EuiIcon type={actionFactory.getIconType(context)!} size="m" />

View file

@ -10,7 +10,7 @@ import { reactToUiComponent } from '../../../../../../src/plugins/kibana_react/p
import { ActionWizard } from './action_wizard';
import { ActionFactory, ActionFactoryDefinition } from '../../dynamic_actions';
import { CollectConfigProps } from '../../../../../../src/plugins/kibana_utils/public';
import { licenseMock } from '../../../../licensing/common/licensing.mock';
import { licensingMock } from '../../../../licensing/public/mocks';
import {
APPLY_FILTER_TRIGGER,
SELECT_RANGE_TRIGGER,
@ -116,9 +116,10 @@ export const dashboardDrilldownActionFactory: ActionFactoryDefinition<
},
};
export const dashboardFactory = new ActionFactory(dashboardDrilldownActionFactory, () =>
licenseMock.createLicense()
);
export const dashboardFactory = new ActionFactory(dashboardDrilldownActionFactory, {
getLicense: () => licensingMock.createLicense(),
getFeatureUsageStart: () => licensingMock.createStart().featureUsage,
});
interface UrlDrilldownConfig {
url: string;
@ -176,9 +177,10 @@ export const urlDrilldownActionFactory: ActionFactoryDefinition<UrlDrilldownConf
},
};
export const urlFactory = new ActionFactory(urlDrilldownActionFactory, () =>
licenseMock.createLicense()
);
export const urlFactory = new ActionFactory(urlDrilldownActionFactory, {
getLicense: () => licensingMock.createLicense(),
getFeatureUsageStart: () => licensingMock.createStart().featureUsage,
});
export const mockSupportedTriggers: TriggerId[] = [
VALUE_CLICK_TRIGGER,

View file

@ -148,7 +148,7 @@ export function createFlyoutManageDrilldowns({
icon: actionFactory?.getIconType(drilldownFactoryContext),
error: !actionFactory
? invalidDrilldownType(drilldown.action.factoryId) // this shouldn't happen for the end user, but useful during development
: !actionFactory.isCompatibleLicence()
: !actionFactory.isCompatibleLicense()
? insufficientLicenseLevel
: undefined,
triggers: drilldown.triggers.map((trigger) => getTrigger(trigger as TriggerId)),

View file

@ -75,7 +75,7 @@ export const FormDrilldownWizard: React.FC<FormDrilldownWizardProps> = ({
);
const hasNotCompatibleLicenseFactory = () =>
actionFactories?.some((f) => !f.isCompatibleLicence());
actionFactories?.some((f) => !f.isCompatibleLicense());
const renderGetMoreActionsLink = () => (
<EuiText size="s">

View file

@ -37,11 +37,18 @@ export interface DrilldownDefinition<
id: string;
/**
* Minimal licence level
* Minimal license level
* Empty means no restrictions
*/
minimalLicense?: LicenseType;
/**
* Required when `minimalLicense` is used.
* Is a user-facing string. Has to be unique. Doesn't need i18n.
* The feature's name will be displayed to Cloud end-users when they're billed based on their feature usage.
*/
licenseFeatureName?: string;
/**
* Determines the display order of the drilldowns in the flyout picker.
* Higher numbers are displayed first.

View file

@ -7,6 +7,7 @@
import { ActionFactory } from './action_factory';
import { ActionFactoryDefinition } from './action_factory_definition';
import { licensingMock } from '../../../licensing/public/mocks';
import { PublicLicense } from '../../../licensing/public';
const def: ActionFactoryDefinition = {
id: 'ACTION_FACTORY_1',
@ -22,34 +23,94 @@ const def: ActionFactoryDefinition = {
supportedTriggers: () => [],
};
const featureUsage = licensingMock.createStart().featureUsage;
const createActionFactory = (
defOverride: Partial<ActionFactoryDefinition> = {},
license?: Partial<PublicLicense>
) => {
return new ActionFactory(
{ ...def, ...defOverride },
{
getLicense: () => licensingMock.createLicense({ license }),
getFeatureUsageStart: () => featureUsage,
}
);
};
describe('License & ActionFactory', () => {
test('no license requirements', async () => {
const factory = new ActionFactory(def, () => licensingMock.createLicense());
const factory = createActionFactory();
expect(await factory.isCompatible({ triggers: [] })).toBe(true);
expect(factory.isCompatibleLicence()).toBe(true);
expect(factory.isCompatibleLicense()).toBe(true);
});
test('not enough license level', async () => {
const factory = new ActionFactory({ ...def, minimalLicense: 'gold' }, () =>
licensingMock.createLicense()
);
const factory = createActionFactory({ minimalLicense: 'gold', licenseFeatureName: 'Feature' });
expect(await factory.isCompatible({ triggers: [] })).toBe(true);
expect(factory.isCompatibleLicence()).toBe(false);
expect(factory.isCompatibleLicense()).toBe(false);
});
test('licence has expired', async () => {
const factory = new ActionFactory({ ...def, minimalLicense: 'gold' }, () =>
licensingMock.createLicense({ license: { type: 'gold', status: 'expired' } })
test('license has expired', async () => {
const factory = createActionFactory(
{ minimalLicense: 'gold', licenseFeatureName: 'Feature' },
{ type: 'gold', status: 'expired' }
);
expect(await factory.isCompatible({ triggers: [] })).toBe(true);
expect(factory.isCompatibleLicence()).toBe(false);
expect(factory.isCompatibleLicense()).toBe(false);
});
test('enough license level', async () => {
const factory = new ActionFactory({ ...def, minimalLicense: 'gold' }, () =>
licensingMock.createLicense({ license: { type: 'gold' } })
const factory = createActionFactory(
{ minimalLicense: 'gold', licenseFeatureName: 'Feature' },
{ type: 'gold' }
);
expect(await factory.isCompatible({ triggers: [] })).toBe(true);
expect(factory.isCompatibleLicence()).toBe(true);
expect(factory.isCompatibleLicense()).toBe(true);
});
describe('licenseFeatureName', () => {
test('licenseFeatureName is required, if minimalLicense is provided', () => {
expect(() => {
createActionFactory();
}).not.toThrow();
expect(() => {
createActionFactory({ minimalLicense: 'gold', licenseFeatureName: 'feature' });
}).not.toThrow();
expect(() => {
createActionFactory({ minimalLicense: 'gold' });
}).toThrow();
});
test('"licenseFeatureName"', () => {
expect(
createActionFactory({ minimalLicense: 'gold', licenseFeatureName: 'feature' })
.licenseFeatureName
).toBe('feature');
expect(createActionFactory().licenseFeatureName).toBeUndefined();
});
});
describe('notifyFeatureUsage', () => {
const spy = jest.spyOn(featureUsage, 'notifyUsage');
beforeEach(() => {
spy.mockClear();
});
test('is not called if no license requirements', async () => {
const action = createActionFactory().create({ name: 'fake', config: {} });
await action.execute({});
expect(spy).not.toBeCalled();
});
test('is called if has license requirements', async () => {
const action = createActionFactory({
minimalLicense: 'gold',
licenseFeatureName: 'feature',
}).create({ name: 'fake', config: {} });
await action.execute({});
expect(spy).toBeCalledWith('feature');
});
});
});

View file

@ -13,9 +13,14 @@ import {
import { ActionFactoryDefinition } from './action_factory_definition';
import { Configurable } from '../../../../../src/plugins/kibana_utils/public';
import { BaseActionFactoryContext, SerializedAction } from './types';
import { ILicense } from '../../../licensing/public';
import { ILicense, LicensingPluginStart } from '../../../licensing/public';
import { UiActionsActionDefinition as ActionDefinition } from '../../../../../src/plugins/ui_actions/public';
export interface ActionFactoryDeps {
readonly getLicense: () => ILicense;
readonly getFeatureUsageStart: () => LicensingPluginStart['featureUsage'];
}
export class ActionFactory<
Config extends object = object,
SupportedTriggers extends TriggerId = TriggerId,
@ -31,11 +36,18 @@ export class ActionFactory<
FactoryContext,
ActionContext
>,
protected readonly getLicence: () => ILicense
) {}
protected readonly deps: ActionFactoryDeps
) {
if (def.minimalLicense && !def.licenseFeatureName) {
throw new Error(
`ActionFactory [actionFactory.id = ${def.id}] "licenseFeatureName" is required, if "minimalLicense" is provided`
);
}
}
public readonly id = this.def.id;
public readonly minimalLicense = this.def.minimalLicense;
public readonly licenseFeatureName = this.def.licenseFeatureName;
public readonly order = this.def.order || 0;
public readonly MenuItem? = this.def.MenuItem;
public readonly ReactMenuItem? = this.MenuItem ? uiToReactComponent(this.MenuItem) : undefined;
@ -65,13 +77,13 @@ export class ActionFactory<
}
/**
* Does this action factory licence requirements
* Does this action factory license requirements
* compatible with current license?
*/
public isCompatibleLicence() {
public isCompatibleLicense() {
if (!this.minimalLicense) return true;
const licence = this.getLicence();
return licence.isAvailable && licence.isActive && licence.hasAtLeast(this.minimalLicense);
const license = this.deps.getLicense();
return license.isAvailable && license.isActive && license.hasAtLeast(this.minimalLicense);
}
public create(
@ -81,14 +93,31 @@ export class ActionFactory<
return {
...action,
isCompatible: async (context: ActionContext): Promise<boolean> => {
if (!this.isCompatibleLicence()) return false;
if (!this.isCompatibleLicense()) return false;
if (!action.isCompatible) return true;
return action.isCompatible(context);
},
execute: async (context: ActionContext): Promise<void> => {
this.notifyFeatureUsage();
return action.execute(context);
},
};
}
public supportedTriggers(): SupportedTriggers[] {
return this.def.supportedTriggers();
}
private notifyFeatureUsage(): void {
if (!this.minimalLicense || !this.licenseFeatureName) return;
this.deps
.getFeatureUsageStart()
.notifyUsage(this.licenseFeatureName)
.catch(() => {
// eslint-disable-next-line no-console
console.warn(
`ActionFactory [actionFactory.id = ${this.def.id}] fail notify feature usage.`
);
});
}
}

View file

@ -34,11 +34,18 @@ export interface ActionFactoryDefinition<
id: string;
/**
* Minimal licence level
* Empty means no licence restrictions
* Minimal license level
* Empty means no license restrictions
*/
readonly minimalLicense?: LicenseType;
/**
* Required when `minimalLicense` is used.
* Is a user-facing string. Has to be unique. Doesn't need i18n.
* The feature's name will be displayed to Cloud end-users when they're billed based on their feature usage.
*/
licenseFeatureName?: string;
/**
* This method should return a definition of a new action, normally used to
* register it in `ui_actions` registry.

View file

@ -87,7 +87,9 @@ const setup = (
actions,
});
const uiActionsEnhancements = new UiActionsServiceEnhancements({
getLicenseInfo,
getLicense: getLicenseInfo,
featureUsageSetup: licensingMock.createSetup().featureUsage,
getFeatureUsageStart: () => licensingMock.createStart().featureUsage,
});
const manager = new DynamicActionManager({
isCompatible,
@ -671,11 +673,13 @@ describe('DynamicActionManager', () => {
const basicActionFactory: ActionFactoryDefinition = {
...actionFactoryDefinition1,
minimalLicense: 'basic',
licenseFeatureName: 'Feature 1',
};
const goldActionFactory: ActionFactoryDefinition = {
...actionFactoryDefinition2,
minimalLicense: 'gold',
licenseFeatureName: 'Feature 2',
};
uiActions.registerActionFactory(basicActionFactory);

View file

@ -11,6 +11,7 @@ import { embeddablePluginMock } from '../../../../src/plugins/embeddable/public/
import { AdvancedUiActionsSetup, AdvancedUiActionsStart } from '.';
import { plugin as pluginInitializer } from '.';
import { licensingMock } from '../../licensing/public/mocks';
import { StartDependencies } from './plugin';
export type Setup = jest.Mocked<AdvancedUiActionsSetup>;
export type Start = jest.Mocked<AdvancedUiActionsStart>;
@ -35,7 +36,7 @@ const createStartContract = (): Start => {
};
const createPlugin = (
coreSetup: CoreSetup = coreMock.createSetup(),
coreSetup: CoreSetup<StartDependencies> = coreMock.createSetup(),
coreStart: CoreStart = coreMock.createStart()
) => {
const pluginInitializerContext = coreMock.createPluginInitializerContext();
@ -47,6 +48,7 @@ const createPlugin = (
const setup = plugin.setup(coreSetup, {
uiActions: uiActions.setup,
embeddable: embeddable.setup,
licensing: licensingMock.createSetup(),
});
return {

View file

@ -36,16 +36,17 @@ import {
} from './custom_time_range_badge';
import { CommonlyUsedRange } from './types';
import { UiActionsServiceEnhancements } from './services';
import { ILicense, LicensingPluginStart } from '../../licensing/public';
import { ILicense, LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public';
import { createFlyoutManageDrilldowns } from './drilldowns';
import { Storage } from '../../../../src/plugins/kibana_utils/public';
import { createStartServicesGetter, Storage } from '../../../../src/plugins/kibana_utils/public';
interface SetupDependencies {
embeddable: EmbeddableSetup; // Embeddable are needed because they register basic triggers/actions.
uiActions: UiActionsSetup;
licensing: LicensingPluginSetup;
}
interface StartDependencies {
export interface StartDependencies {
embeddable: EmbeddableStart;
uiActions: UiActionsStart;
licensing: LicensingPluginStart;
@ -70,23 +71,30 @@ declare module '../../../../src/plugins/ui_actions/public' {
export class AdvancedUiActionsPublicPlugin
implements Plugin<SetupContract, StartContract, SetupDependencies, StartDependencies> {
readonly licenceInfo = new BehaviorSubject<ILicense | undefined>(undefined);
readonly licenseInfo = new BehaviorSubject<ILicense | undefined>(undefined);
private getLicenseInfo(): ILicense {
if (!this.licenceInfo.getValue()) {
if (!this.licenseInfo.getValue()) {
throw new Error(
'AdvancedUiActionsPublicPlugin: Licence is not ready! Licence becomes available only after setup.'
'AdvancedUiActionsPublicPlugin: License is not ready! License becomes available only after setup.'
);
}
return this.licenceInfo.getValue()!;
return this.licenseInfo.getValue()!;
}
private readonly enhancements = new UiActionsServiceEnhancements({
getLicenseInfo: () => this.getLicenseInfo(),
});
private enhancements?: UiActionsServiceEnhancements;
private subs: Subscription[] = [];
constructor(initializerContext: PluginInitializerContext) {}
public setup(core: CoreSetup, { uiActions }: SetupDependencies): SetupContract {
public setup(
core: CoreSetup<StartDependencies>,
{ uiActions, licensing }: SetupDependencies
): SetupContract {
const startServices = createStartServicesGetter(core.getStartServices);
this.enhancements = new UiActionsServiceEnhancements({
getLicense: () => this.getLicenseInfo(),
featureUsageSetup: licensing.featureUsage,
getFeatureUsageStart: () => startServices().plugins.licensing.featureUsage,
});
return {
...uiActions,
...this.enhancements,
@ -94,7 +102,7 @@ export class AdvancedUiActionsPublicPlugin
}
public start(core: CoreStart, { uiActions, licensing }: StartDependencies): StartContract {
this.subs.push(licensing.license$.subscribe(this.licenceInfo));
this.subs.push(licensing.license$.subscribe(this.licenseInfo));
const dateFormat = core.uiSettings.get('dateFormat') as string;
const commonlyUsedRanges = core.uiSettings.get(
@ -117,9 +125,9 @@ export class AdvancedUiActionsPublicPlugin
return {
...uiActions,
...this.enhancements,
...this.enhancements!,
FlyoutManageDrilldowns: createFlyoutManageDrilldowns({
actionFactories: this.enhancements.getActionFactories(),
actionFactories: this.enhancements!.getActionFactories(),
getTrigger: (triggerId: TriggerId) => uiActions.getTrigger(triggerId),
storage: new Storage(window?.localStorage),
toastService: core.notifications.toasts,

View file

@ -4,11 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { UiActionsServiceEnhancements } from './ui_actions_service_enhancements';
import {
UiActionsServiceEnhancements,
UiActionsServiceEnhancementsParams,
} from './ui_actions_service_enhancements';
import { ActionFactoryDefinition, ActionFactory } from '../dynamic_actions';
import { licensingMock } from '../../../licensing/public/mocks';
const getLicenseInfo = () => licensingMock.createLicense();
const deps: UiActionsServiceEnhancementsParams = {
getLicense: () => licensingMock.createLicense(),
featureUsageSetup: licensingMock.createSetup().featureUsage,
getFeatureUsageStart: () => licensingMock.createStart().featureUsage,
};
describe('UiActionsService', () => {
describe('action factories', () => {
@ -34,7 +41,7 @@ describe('UiActionsService', () => {
};
test('.getActionFactories() returns empty array if no action factories registered', () => {
const service = new UiActionsServiceEnhancements({ getLicenseInfo });
const service = new UiActionsServiceEnhancements(deps);
const factories = service.getActionFactories();
@ -42,7 +49,7 @@ describe('UiActionsService', () => {
});
test('can register and retrieve an action factory', () => {
const service = new UiActionsServiceEnhancements({ getLicenseInfo });
const service = new UiActionsServiceEnhancements(deps);
service.registerActionFactory(factoryDefinition1);
@ -53,7 +60,7 @@ describe('UiActionsService', () => {
});
test('can retrieve all action factories', () => {
const service = new UiActionsServiceEnhancements({ getLicenseInfo });
const service = new UiActionsServiceEnhancements(deps);
service.registerActionFactory(factoryDefinition1);
service.registerActionFactory(factoryDefinition2);
@ -67,7 +74,7 @@ describe('UiActionsService', () => {
});
test('throws when retrieving action factory that does not exist', () => {
const service = new UiActionsServiceEnhancements({ getLicenseInfo });
const service = new UiActionsServiceEnhancements(deps);
service.registerActionFactory(factoryDefinition1);
@ -77,7 +84,7 @@ describe('UiActionsService', () => {
});
test('isCompatible from definition is used on registered factory', async () => {
const service = new UiActionsServiceEnhancements({ getLicenseInfo });
const service = new UiActionsServiceEnhancements(deps);
service.registerActionFactory({
...factoryDefinition1,
@ -88,5 +95,27 @@ describe('UiActionsService', () => {
service.getActionFactory(factoryDefinition1.id).isCompatible({ triggers: [] })
).resolves.toBe(false);
});
describe('registerFeature for licensing', () => {
const spy = jest.spyOn(deps.featureUsageSetup, 'register');
beforeEach(() => {
spy.mockClear();
});
test('registerFeature is not called if no license requirements', () => {
const service = new UiActionsServiceEnhancements(deps);
service.registerActionFactory(factoryDefinition1);
expect(spy).not.toBeCalled();
});
test('registerFeature is called if has license requirements', () => {
const service = new UiActionsServiceEnhancements(deps);
service.registerActionFactory({
...factoryDefinition1,
minimalLicense: 'gold',
licenseFeatureName: 'a name',
});
expect(spy).toBeCalledWith('a name', 'gold');
});
});
});
});

View file

@ -13,19 +13,22 @@ import {
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';
export interface UiActionsServiceEnhancementsParams {
readonly actionFactories?: ActionFactoryRegistry;
readonly getLicenseInfo: () => ILicense;
readonly getLicense: () => ILicense;
readonly featureUsageSetup: LicensingPluginSetup['featureUsage'];
readonly getFeatureUsageStart: () => LicensingPluginStart['featureUsage'];
}
export class UiActionsServiceEnhancements {
protected readonly actionFactories: ActionFactoryRegistry;
protected readonly getLicenseInfo: () => ILicense;
protected readonly deps: Omit<UiActionsServiceEnhancementsParams, 'actionFactories'>;
constructor({ actionFactories = new Map(), getLicenseInfo }: UiActionsServiceEnhancementsParams) {
constructor({ actionFactories = new Map(), ...deps }: UiActionsServiceEnhancementsParams) {
this.actionFactories = actionFactories;
this.getLicenseInfo = getLicenseInfo;
this.deps = deps;
}
/**
@ -51,9 +54,10 @@ export class UiActionsServiceEnhancements {
SupportedTriggers,
FactoryContext,
ActionContext
>(definition, this.getLicenseInfo);
>(definition, this.deps);
this.actionFactories.set(actionFactory.id, actionFactory as ActionFactory<any, any, any>);
this.registerFeatureUsage(definition);
};
public readonly getActionFactory = (actionFactoryId: string): ActionFactory => {
@ -94,6 +98,7 @@ export class UiActionsServiceEnhancements {
execute,
getHref,
minimalLicense,
licenseFeatureName,
supportedTriggers,
isCompatible,
}: DrilldownDefinition<Config, SupportedTriggers, FactoryContext, ExecutionContext>): void => {
@ -105,6 +110,7 @@ export class UiActionsServiceEnhancements {
> = {
id: factoryId,
minimalLicense,
licenseFeatureName,
order,
CollectConfig,
createConfig,
@ -128,4 +134,19 @@ export class UiActionsServiceEnhancements {
this.registerActionFactory(actionFactory);
};
private registerFeatureUsage = (definition: ActionFactoryDefinition<any, any, any>): void => {
if (!definition.minimalLicense || !definition.licenseFeatureName) return;
// Intentionally don't wait for response because
// happens in setup phase and has to be sync
this.deps.featureUsageSetup
.register(definition.licenseFeatureName, definition.minimalLicense)
.catch(() => {
// eslint-disable-next-line no-console
console.warn(
`ActionFactory [actionFactory.id = ${definition.id}] fail to register feature for featureUsage.`
);
});
};
}