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

Define minimum license required for each action type #58668

Merged
8 changes: 8 additions & 0 deletions x-pack/plugins/actions/server/action_type_registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('register()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
Copy link
Member

Choose a reason for hiding this comment

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

maybe there's an hook later, but this seems to violate "non-built-in action types can only set a gold+ license"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can set anything here but it will be validated when you register. The registration validation exists here: https://github.com/elastic/kibana/pull/58668/files#diff-adc6606fd00ebc8b46eecefae314f1b5R187 and the built ins don't go through there, they register directly to the actionTypeRegistry.

executor,
});
expect(actionTypeRegistry.has('my-action-type')).toEqual(true);
Expand All @@ -55,12 +56,14 @@ describe('register()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
expect(() =>
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
})
).toThrowErrorMatchingInlineSnapshot(
Expand All @@ -73,6 +76,7 @@ describe('register()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalledTimes(1);
Expand All @@ -94,13 +98,15 @@ describe('get()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
const actionType = actionTypeRegistry.get('my-action-type');
expect(actionType).toMatchInlineSnapshot(`
Object {
"executor": [Function],
"id": "my-action-type",
"minimumLicenseRequired": "basic",
"name": "My action type",
}
`);
Expand All @@ -120,6 +126,7 @@ describe('list()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
const actionTypes = actionTypeRegistry.list();
Expand All @@ -144,6 +151,7 @@ describe('has()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
expect(actionTypeRegistry.has('my-action-type'));
Expand Down
7 changes: 7 additions & 0 deletions x-pack/plugins/actions/server/actions_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe('create()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
Expand Down Expand Up @@ -100,6 +101,7 @@ describe('create()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
validate: {
config: schema.object({
param1: schema.string(),
Expand Down Expand Up @@ -140,6 +142,7 @@ describe('create()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.create.mockResolvedValueOnce({
Expand Down Expand Up @@ -233,6 +236,7 @@ describe('create()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult);
Expand Down Expand Up @@ -346,6 +350,7 @@ describe('update()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.get.mockResolvedValueOnce({
Expand Down Expand Up @@ -407,6 +412,7 @@ describe('update()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
validate: {
config: schema.object({
param1: schema.string(),
Expand Down Expand Up @@ -440,6 +446,7 @@ describe('update()', () => {
actionTypeRegistry.register({
id: 'my-action-type',
name: 'My action type',
minimumLicenseRequired: 'basic',
executor,
});
savedObjectsClient.get.mockResolvedValueOnce({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export function getActionType(params: GetActionTypeParams): ActionType {
const { logger, configurationUtilities } = params;
return {
id: '.email',
minimumLicenseRequired: 'gold',
name: i18n.translate('xpack.actions.builtin.emailTitle', {
defaultMessage: 'Email',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const ParamsSchema = schema.object({
export function getActionType({ logger }: { logger: Logger }): ActionType {
return {
id: '.index',
minimumLicenseRequired: 'basic',
name: i18n.translate('xpack.actions.builtin.esIndexTitle', {
defaultMessage: 'Index',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export function getActionType({
}): ActionType {
return {
id: '.pagerduty',
minimumLicenseRequired: 'gold',
name: i18n.translate('xpack.actions.builtin.pagerdutyTitle', {
defaultMessage: 'PagerDuty',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const ParamsSchema = schema.object({
export function getActionType({ logger }: { logger: Logger }): ActionType {
return {
id: '.server-log',
minimumLicenseRequired: 'basic',
name: i18n.translate('xpack.actions.builtin.serverLogTitle', {
defaultMessage: 'Server log',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export function getActionType({
}): ActionType {
return {
id: '.servicenow',
minimumLicenseRequired: 'gold',

Choose a reason for hiding this comment

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

@mikecote @peterschretlen I just spoke with @MikePaquette, can we make this platinum. There is a good chance that's where it will land

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I went ahead and made it platinum here: 9669ebd.

name: i18n.translate('xpack.actions.builtin.servicenowTitle', {
defaultMessage: 'ServiceNow',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function getActionType({
}): ActionType {
return {
id: '.slack',
minimumLicenseRequired: 'gold',
name: i18n.translate('xpack.actions.builtin.slackTitle', {
defaultMessage: 'Slack',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export function getActionType({
}): ActionType {
return {
id: '.webhook',
minimumLicenseRequired: 'gold',
name: i18n.translate('xpack.actions.builtin.webhookTitle', {
defaultMessage: 'Webhook',
}),
Expand Down
6 changes: 6 additions & 0 deletions x-pack/plugins/actions/server/lib/action_executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/serv
import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks';
import { eventLoggerMock } from '../../../event_log/server/mocks';
import { spacesServiceMock } from '../../../spaces/server/spaces_service/spaces_service.mock';
import { LicenseType } from '../../../licensing/common/types';

const actionExecutor = new ActionExecutor({ isESOUsingEphemeralEncryptionKey: false });
const savedObjectsClient = savedObjectsClientMock.create();
Expand Down Expand Up @@ -53,6 +54,7 @@ test('successfully executes', async () => {
const actionType = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic' as LicenseType,
executor: jest.fn(),
};
const actionSavedObject = {
Expand Down Expand Up @@ -99,6 +101,7 @@ test('provides empty config when config and / or secrets is empty', async () =>
const actionType = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic' as LicenseType,
executor: jest.fn(),
};
const actionSavedObject = {
Expand All @@ -123,6 +126,7 @@ test('throws an error when config is invalid', async () => {
const actionType = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic' as LicenseType,
validate: {
config: schema.object({
param1: schema.string(),
Expand Down Expand Up @@ -155,6 +159,7 @@ test('throws an error when params is invalid', async () => {
const actionType = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic' as LicenseType,
validate: {
params: schema.object({
param1: schema.string(),
Expand Down Expand Up @@ -194,6 +199,7 @@ test('returns an error if actionType is not enabled', async () => {
const actionType = {
id: 'test',
name: 'Test',
minimumLicenseRequired: 'basic' as LicenseType,
executor: jest.fn(),
};
const actionSavedObject = {
Expand Down
19 changes: 17 additions & 2 deletions x-pack/plugins/actions/server/lib/validate_with_schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,26 @@ const executor: ExecutorType = async options => {
};

test('should validate when there are no validators', () => {
const actionType: ActionType = { id: 'foo', name: 'bar', executor };
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
};
const testValue = { any: ['old', 'thing'] };

const result = validateConfig(actionType, testValue);
expect(result).toEqual(testValue);
});

test('should validate when there are no individual validators', () => {
const actionType: ActionType = { id: 'foo', name: 'bar', executor, validate: {} };
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {},
};

let result;
const testValue = { any: ['old', 'thing'] };
Expand All @@ -42,6 +53,7 @@ test('should validate when validators return incoming value', () => {
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {
params: selfValidator,
Expand Down Expand Up @@ -69,6 +81,7 @@ test('should validate when validators return different values', () => {
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {
params: selfValidator,
Expand Down Expand Up @@ -99,6 +112,7 @@ test('should throw with expected error when validators fail', () => {
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {
params: erroringValidator,
Expand Down Expand Up @@ -127,6 +141,7 @@ test('should work with @kbn/config-schema', () => {
const actionType: ActionType = {
id: 'foo',
name: 'bar',
minimumLicenseRequired: 'basic',
executor,
validate: {
params: testSchema,
Expand Down
56 changes: 55 additions & 1 deletion x-pack/plugins/actions/server/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ActionsPlugin, ActionsPluginsSetup, ActionsPluginsStart } from './plugin';
import { PluginInitializerContext } from '../../../../src/core/server';
import { coreMock, httpServerMock } from '../../../../src/core/server/mocks';
import { licensingMock } from '../../licensing/server/mocks';
import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks';
import { taskManagerMock } from '../../task_manager/server/mocks';
import { eventLogMock } from '../../event_log/server/mocks';
import { ActionType } from './types';
import {
ActionsPlugin,
ActionsPluginsSetup,
ActionsPluginsStart,
PluginSetupContract,
} from './plugin';

describe('Actions Plugin', () => {
describe('setup()', () => {
Expand Down Expand Up @@ -90,6 +96,54 @@ describe('Actions Plugin', () => {
);
});
});

describe('registerType()', () => {
let setup: PluginSetupContract;
const sampleActionType: ActionType = {
id: 'test',
name: 'test',
minimumLicenseRequired: 'basic',
async executor() {},
};

beforeEach(async () => {
setup = await plugin.setup(coreSetup, pluginsSetup);
});

it('should throw error when license type is invalid', async () => {
expect(() =>
setup.registerType({
...sampleActionType,
minimumLicenseRequired: 'foo' as any,
})
).toThrowErrorMatchingInlineSnapshot(`"\\"foo\\" is not a valid license type"`);
});

it('should throw error when license type is less than gold', async () => {
expect(() =>
setup.registerType({
...sampleActionType,
minimumLicenseRequired: 'basic',
})
).toThrowErrorMatchingInlineSnapshot(
`"Third party action type \\"test\\" can only set minimumLicenseRequired to a gold license or higher"`
);
});

it('should not throw when license type is gold', async () => {
setup.registerType({
...sampleActionType,
minimumLicenseRequired: 'gold',
});
});

it('should not throw when license type is higher than gold', async () => {
setup.registerType({
...sampleActionType,
minimumLicenseRequired: 'platinum',
});
});
});
});
describe('start()', () => {
let plugin: ActionsPlugin;
Expand Down
Loading