Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Actions] Set system actions on Kibana start #160983

Merged
merged 34 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1352e52
Add isSystemAction to ActionResult type and default it to false
cnasikas Jun 28, 2023
09b82f6
Disallow registering system actions
cnasikas Jun 28, 2023
dc1a2c8
Add test
cnasikas Jun 28, 2023
bb7e9a5
Fix alerting types
cnasikas Jun 28, 2023
774b389
Fix integration tests
cnasikas Jun 28, 2023
83ee747
Fix triggers_actions_ui types
cnasikas Jun 28, 2023
d2b8e67
Fix cases types
cnasikas Jun 28, 2023
31f1f39
Fix cases tests
cnasikas Jun 28, 2023
85ca6a4
Fix integration tests
cnasikas Jun 28, 2023
b070bd9
Fix cases tests
cnasikas Jun 28, 2023
a8e7143
Merge branch 'main' into system_actions_registration
kibanamachine Jun 29, 2023
b50a3e0
Fix types from other plugins
cnasikas Jun 29, 2023
ee5df7b
Merge branch 'system_actions_registration' of github.com:cnasikas/kib…
cnasikas Jun 29, 2023
1caf874
Rename preconfiguredConnectors to inMemoryConnectors
cnasikas Jun 29, 2023
2aabeed
Create system actions
cnasikas Jun 29, 2023
3cf469a
Prevent create/update/delete of system actions
cnasikas Jun 30, 2023
8c1246c
Fix types
cnasikas Jun 30, 2023
0d7aea1
Merge branch 'main' into load_system_actions_in_memory
cnasikas Jun 30, 2023
52ef0e5
Fix types
cnasikas Jun 30, 2023
d763161
Remove cases from basic
cnasikas Jul 1, 2023
5c3245e
Convert preconfigured to in-memory
cnasikas Jul 1, 2023
31bc750
Merge branch 'main' into load_system_actions_in_memory
cnasikas Jul 1, 2023
d35a7f9
Get in-memory connectors after they have been created in the route co…
cnasikas Jul 1, 2023
34ee2ab
Register test system action in integration tests
cnasikas Jul 1, 2023
86b279c
Add integration tests
cnasikas Jul 1, 2023
57b4d3a
Fix i18n
cnasikas Jul 2, 2023
a162163
Fix types
cnasikas Jul 2, 2023
2ea3c9c
Fix integration test
cnasikas Jul 2, 2023
7362b97
Fixes
cnasikas Jul 2, 2023
cbaeb1f
Merge branch 'main' into load_system_actions_in_memory
cnasikas Jul 3, 2023
a1b5d51
Filtered out system connectors in telemetry
cnasikas Jul 3, 2023
01f15e1
Merge branch 'main' into load_system_actions_in_memory
cnasikas Jul 6, 2023
a080d94
PR feedback
cnasikas Jul 7, 2023
abc6014
Fix tests
cnasikas Jul 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const ConnectorSelector: React.FC<Props> = React.memo(
enabledInLicense: true,
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['general'],
isSystemActionType: false,
id: '.gen-ai',
name: 'Generative AI',
enabled: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export const useConnectorSetup = ({
actionTypes?.find((at) => at.id === GEN_AI_CONNECTOR_ID) ?? {
enabledInConfig: true,
enabledInLicense: true,
isSystemActionType: false,
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['general'],
id: '.gen-ai',
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/actions/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface ActionType {
enabledInLicense: boolean;
minimumLicenseRequired: LicenseType;
supportedFeatureIds: string[];
isSystemActionType: boolean;
}

export enum InvalidEmailReason {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const createActionTypeRegistryMock = () => {
ensureActionTypeEnabled: jest.fn(),
isActionTypeEnabled: jest.fn(),
isActionExecutable: jest.fn(),
isSystemActionType: jest.fn(),
getUtils: jest.fn(),
};
return mocked;
Expand Down
130 changes: 126 additions & 4 deletions x-pack/plugins/actions/server/action_type_registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('actionTypeRegistry', () => {
),
actionsConfigUtils: mockedActionsConfig,
licenseState: mockedLicenseState,
preconfiguredActions: [
inMemoryConnectors: [
{
actionTypeId: 'foo',
config: {},
Expand All @@ -47,6 +47,16 @@ describe('actionTypeRegistry', () => {
isDeprecated: false,
isSystemAction: false,
},
{
actionTypeId: '.cases',
config: {},
id: 'system-connector-.cases',
name: 'System action: .cases',
secrets: {},
isPreconfigured: false,
isDeprecated: false,
isSystemAction: true,
},
],
};
});
Expand Down Expand Up @@ -217,7 +227,7 @@ describe('actionTypeRegistry', () => {
expect(actionTypeRegistryParams.licensing.featureUsage.register).not.toHaveBeenCalled();
});

test('does not allows registering system actions', () => {
test('allows registering system actions', () => {
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);

expect(() =>
Expand All @@ -226,15 +236,15 @@ describe('actionTypeRegistry', () => {
name: 'My action type',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
isSystemAction: true,
isSystemActionType: true,
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
})
).toThrowErrorMatchingInlineSnapshot(`"System actions are not supported"`);
).not.toThrow();
});
});

Expand Down Expand Up @@ -302,6 +312,7 @@ describe('actionTypeRegistry', () => {
enabledInLicense: true,
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
isSystemActionType: false,
},
]);
expect(mockedActionsConfig.isActionTypeEnabled).toHaveBeenCalled();
Expand Down Expand Up @@ -345,11 +356,46 @@ describe('actionTypeRegistry', () => {
enabledInLicense: true,
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
isSystemActionType: false,
},
]);
expect(mockedActionsConfig.isActionTypeEnabled).toHaveBeenCalled();
expect(mockedLicenseState.isLicenseValidForActionType).toHaveBeenCalled();
});

test('sets the isSystemActionType correctly for system actions', () => {
mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true });
const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);

actionTypeRegistry.register({
id: '.cases',
name: 'Cases',
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
isSystemActionType: true,
executor,
});

const actionTypes = actionTypeRegistry.list();

expect(actionTypes).toEqual([
{
id: '.cases',
name: 'Cases',
enabled: true,
enabledInConfig: true,
enabledInLicense: true,
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
isSystemActionType: true,
},
]);
});
});

describe('has()', () => {
Expand Down Expand Up @@ -378,6 +424,7 @@ describe('actionTypeRegistry', () => {

describe('isActionTypeEnabled', () => {
let actionTypeRegistry: ActionTypeRegistry;

const fooActionType: ActionType = {
id: 'foo',
name: 'Foo',
Expand All @@ -393,9 +440,17 @@ describe('actionTypeRegistry', () => {
},
};

const systemActionType: ActionType = {
...fooActionType,
id: 'system-action-type',
name: 'System action type',
isSystemActionType: true,
};

beforeEach(() => {
actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
actionTypeRegistry.register(fooActionType);
actionTypeRegistry.register(systemActionType);
});

test('should call isActionTypeEnabled of the actions config', async () => {
Expand All @@ -417,6 +472,15 @@ describe('actionTypeRegistry', () => {
expect(actionTypeRegistry.isActionExecutable('my-slack1', 'foo')).toEqual(true);
});

test('should return true when isActionTypeEnabled is false and isLicenseValidForActionType is true and it has system connectors', async () => {
mockedActionsConfig.isActionTypeEnabled.mockReturnValue(false);
mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true });

expect(
actionTypeRegistry.isActionExecutable('system-connector-.cases', 'system-action-type')
).toEqual(true);
});

test('should call isLicenseValidForActionType of the license state with notifyUsage false by default', async () => {
mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true });
actionTypeRegistry.isActionTypeEnabled('foo');
Expand Down Expand Up @@ -567,4 +631,62 @@ describe('actionTypeRegistry', () => {
expect(result).toEqual(['foo']);
});
});

describe('isSystemActionType()', () => {
it('should return true if the action type is a system action type', () => {
const registry = new ActionTypeRegistry(actionTypeRegistryParams);

registry.register({
id: '.cases',
name: 'Cases',
minimumLicenseRequired: 'platinum',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
isSystemActionType: true,
executor,
});

const result = registry.isSystemActionType('.cases');
expect(result).toBe(true);
});

it('should return false if the action type is not a system action type', () => {
mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true });

const registry = new ActionTypeRegistry(actionTypeRegistryParams);

registry.register({
id: 'foo',
name: 'Foo',
minimumLicenseRequired: 'basic',
supportedFeatureIds: ['alerting'],
validate: {
config: { schema: schema.object({}) },
secrets: { schema: schema.object({}) },
params: { schema: schema.object({}) },
},
executor,
});

const allTypes = registry.getAllTypes();
expect(allTypes.length).toBe(1);

const result = registry.isSystemActionType('foo');
expect(result).toBe(false);
});

it('should return false if the action type does not exists', () => {
const registry = new ActionTypeRegistry(actionTypeRegistryParams);

const allTypes = registry.getAllTypes();
expect(allTypes.length).toBe(0);

const result = registry.isSystemActionType('not-exist');
expect(result).toBe(false);
});
});
});
34 changes: 14 additions & 20 deletions x-pack/plugins/actions/server/action_type_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ActionsConfigurationUtilities } from './actions_config';
import { getActionTypeFeatureUsageName, TaskRunnerFactory, ILicenseState } from './lib';
import {
ActionType,
PreConfiguredAction,
InMemoryConnector,
ActionTypeConfig,
ActionTypeSecrets,
ActionTypeParams,
Expand All @@ -26,7 +26,7 @@ export interface ActionTypeRegistryOpts {
taskRunnerFactory: TaskRunnerFactory;
actionsConfigUtils: ActionsConfigurationUtilities;
licenseState: ILicenseState;
preconfiguredActions: PreConfiguredAction[];
inMemoryConnectors: InMemoryConnector[];
}

export class ActionTypeRegistry {
Expand All @@ -35,15 +35,15 @@ export class ActionTypeRegistry {
private readonly taskRunnerFactory: TaskRunnerFactory;
private readonly actionsConfigUtils: ActionsConfigurationUtilities;
private readonly licenseState: ILicenseState;
private readonly preconfiguredActions: PreConfiguredAction[];
private readonly inMemoryConnectors: InMemoryConnector[];
private readonly licensing: LicensingPluginSetup;

constructor(constructorParams: ActionTypeRegistryOpts) {
this.taskManager = constructorParams.taskManager;
this.taskRunnerFactory = constructorParams.taskRunnerFactory;
this.actionsConfigUtils = constructorParams.actionsConfigUtils;
this.licenseState = constructorParams.licenseState;
this.preconfiguredActions = constructorParams.preconfiguredActions;
this.inMemoryConnectors = constructorParams.inMemoryConnectors;
this.licensing = constructorParams.licensing;
}

Expand Down Expand Up @@ -78,7 +78,7 @@ export class ActionTypeRegistry {
}

/**
* Returns true if action type is enabled or it is a preconfigured action type.
* Returns true if action type is enabled or it is an in memory action type.
*/
public isActionExecutable(
actionId: string,
Expand All @@ -89,12 +89,17 @@ export class ActionTypeRegistry {
return (
actionTypeEnabled ||
(!actionTypeEnabled &&
this.preconfiguredActions.find(
(preconfiguredAction) => preconfiguredAction.id === actionId
) !== undefined)
this.inMemoryConnectors.find((inMemoryConnector) => inMemoryConnector.id === actionId) !==
undefined)
);
}

/**
* Returns true if the action type is a system action type
*/
public isSystemActionType = (actionTypeId: string): boolean =>
Boolean(this.actionTypes.get(actionTypeId)?.isSystemActionType);

/**
* Registers an action type to the action type registry
*/
Expand All @@ -104,18 +109,6 @@ export class ActionTypeRegistry {
Params extends ActionTypeParams = ActionTypeParams,
ExecutorResultData = void
>(actionType: ActionType<Config, Secrets, Params, ExecutorResultData>) {
// TODO: Remove when system action are supported
if (actionType.isSystemAction) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integration tests should ping us if someone registers a system action.

throw new Error(
i18n.translate(
'xpack.actions.actionTypeRegistry.register.systemActionsNotSupportedErrorMessage',
{
defaultMessage: 'System actions are not supported',
}
)
);
}

if (this.has(actionType.id)) {
throw new Error(
i18n.translate(
Expand Down Expand Up @@ -214,6 +207,7 @@ export class ActionTypeRegistry {
enabledInConfig: this.actionsConfigUtils.isActionTypeEnabled(actionTypeId),
enabledInLicense: !!this.licenseState.isLicenseValidForActionType(actionType).isValid,
supportedFeatureIds: actionType.supportedFeatureIds,
isSystemActionType: !!actionType.isSystemActionType,
}));
}

Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/actions/server/actions_client.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const createActionsClientMock = () => {
listTypes: jest.fn(),
isActionTypeEnabled: jest.fn(),
isPreconfigured: jest.fn(),
isSystemAction: jest.fn(),
getGlobalExecutionKpiWithAuth: jest.fn(),
getGlobalExecutionLogWithAuth: jest.fn(),
};
Expand Down
Loading