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

[RAM] System action in bulk enable api #170476

Merged
merged 9 commits into from
Nov 14, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import {
enabledRuleForBulkOps1,
enabledRuleForBulkOps2,
enabledRuleForBulkOps3,
returnedRuleForBulkDelete1,
returnedRuleForBulkDelete2,
returnedRuleForBulkDelete3,
returnedRuleForBulkOps1,
returnedRuleForBulkOps2,
returnedRuleForBulkOps3,
siemRuleForBulkOps1,
} from '../../../../rules_client/tests/test_helpers';
import { migrateLegacyActions } from '../../../../rules_client/lib';
Expand Down Expand Up @@ -81,6 +81,7 @@ const rulesClientParams: jest.Mocked<ConstructorOptions> = {
isAuthenticationTypeAPIKey: jest.fn(),
getAuthenticationAPIKey: jest.fn(),
connectorAdapterRegistry: new ConnectorAdapterRegistry(),
isSystemAction: jest.fn(),
getAlertIndicesAlias: jest.fn(),
alertsService: null,
};
Expand Down Expand Up @@ -192,7 +193,7 @@ describe('bulkDelete', () => {
expect.anything()
);
expect(result).toStrictEqual({
rules: [returnedRuleForBulkDelete1, returnedRuleForBulkDelete3],
rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps3],
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 500 }],
total: 2,
taskIdsFailedToBeDeleted: [],
Expand Down Expand Up @@ -256,7 +257,7 @@ describe('bulkDelete', () => {
expect.anything()
);
expect(result).toStrictEqual({
rules: [returnedRuleForBulkDelete1],
rules: [returnedRuleForBulkOps1],
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 409 }],
total: 2,
taskIdsFailedToBeDeleted: [],
Expand Down Expand Up @@ -314,7 +315,7 @@ describe('bulkDelete', () => {
expect.anything()
);
expect(result).toStrictEqual({
rules: [returnedRuleForBulkDelete1, returnedRuleForBulkDelete2],
rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2],
errors: [],
total: 2,
taskIdsFailedToBeDeleted: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ export interface RRuleAttributes {
byweekday?: Array<string | number>;
bymonth?: number[];
bysetpos?: number[];
bymonthday: number[];
byyearday: number[];
byweekno: number[];
byhour: number[];
byminute: number[];
bysecond: number[];
bymonthday?: number[];
byyearday?: number[];
byweekno?: number[];
byhour?: number[];
byminute?: number[];
bysecond?: number[];
XavierM marked this conversation as resolved.
Show resolved Hide resolved
}

export interface RuleSnoozeScheduleAttributes {
Expand Down
120 changes: 119 additions & 1 deletion x-pack/plugins/alerting/server/routes/bulk_enable_rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
*/

import { httpServiceMock } from '@kbn/core/server/mocks';

import { actionsClientMock } from '@kbn/actions-plugin/server/mocks';
import { bulkEnableRulesRoute } from './bulk_enable_rules';
import { licenseStateMock } from '../lib/license_state.mock';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { rulesClientMock } from '../rules_client.mock';
import { RuleTypeDisabledError } from '../lib/errors/rule_type_disabled';
import { verifyApiAccess } from '../lib/license_api_access';
import { RuleActionTypes, RuleDefaultAction, RuleSystemAction, SanitizedRule } from '../types';

const rulesClient = rulesClientMock.create();

Expand Down Expand Up @@ -123,4 +124,121 @@ describe('bulkEnableRulesRoute', () => {

expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } });
});

describe('actions', () => {
const mockedRule: SanitizedRule<{}> = {
id: '1',
alertTypeId: '1',
schedule: { interval: '10s' },
params: {
bar: true,
},
createdAt: new Date(),
updatedAt: new Date(),
actions: [
{
group: 'default',
id: '2',
actionTypeId: 'test',
params: {
foo: true,
},
uuid: '123-456',
type: RuleActionTypes.DEFAULT,
},
],
consumer: 'bar',
name: 'abc',
tags: ['foo'],
enabled: true,
muteAll: false,
notifyWhen: 'onActionGroupChange',
createdBy: '',
updatedBy: '',
apiKeyOwner: '',
throttle: '30s',
mutedInstanceIds: [],
executionStatus: {
status: 'unknown',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
},
revision: 0,
};

const action: RuleDefaultAction = {
actionTypeId: 'test',
group: 'default',
id: '2',
params: {
foo: true,
},
uuid: '123-456',
type: RuleActionTypes.DEFAULT,
};

const systemAction: RuleSystemAction = {
actionTypeId: 'test-2',
id: 'system_action-id',
params: {
foo: true,
},
uuid: '123-456',
type: RuleActionTypes.SYSTEM,
};

const mockedRules: Array<SanitizedRule<{}>> = [
{ ...mockedRule, actions: [action, systemAction] },
];

const bulkEnableActionsResult = {
rules: mockedRules,
errors: [],
total: 1,
taskIdsFailedToBeEnabled: [],
};

it('removes the type from the actions correctly before sending the response', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
const actionsClient = actionsClientMock.create();
actionsClient.isSystemAction.mockImplementation((id: string) => id === 'system_action-id');

bulkEnableRulesRoute({ router, licenseState });
const [_, handler] = router.patch.mock.calls[0];

// rulesClient.bulkDisableRules.mockResolvedValueOnce(bulkDisableActionsResult);
rulesClient.bulkEnableRules.mockResolvedValueOnce(bulkEnableActionsResult);

const [context, req, res] = mockHandlerArguments(
{ rulesClient, actionsClient },
{
body: bulkEnableRequest,
},
['ok']
);

const routeRes = await handler(context, req, res);

// @ts-expect-error: body exists
expect(routeRes.body.rules[0].actions).toEqual([
{
connector_type_id: 'test',
group: 'default',
id: '2',
params: {
foo: true,
},
uuid: '123-456',
},
{
connector_type_id: 'test-2',
id: 'system_action-id',
params: {
foo: true,
},
uuid: '123-456',
},
]);
});
});
});
18 changes: 16 additions & 2 deletions x-pack/plugins/alerting/server/routes/bulk_enable_rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { IRouter } from '@kbn/core/server';
import { verifyAccessAndContext, handleDisabledApiKeysError } from './lib';
import { ILicenseState, RuleTypeDisabledError } from '../lib';
import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types';
import { transformRuleToRuleResponseV1 } from './rule/transforms';
import { Rule } from '../application/rule/types';

export const bulkEnableRulesRoute = ({
router,
Expand All @@ -35,8 +37,20 @@ export const bulkEnableRulesRoute = ({
const { filter, ids } = req.body;

try {
const result = await rulesClient.bulkEnableRules({ filter, ids });
return res.ok({ body: result });
const bulkEnableResults = await rulesClient.bulkEnableRules({ filter, ids });

const resultBody = {
body: {
...bulkEnableResults,
rules: bulkEnableResults.rules.map((rule) => {
// TODO (http-versioning): Remove this cast, this enables us to move forward
// without fixing all of other solution types
return transformRuleToRuleResponseV1(rule as Rule);
}),
},
};

return res.ok(resultBody);
} catch (e) {
if (e instanceof RuleTypeDisabledError) {
return e.sendResponse(res);
Expand Down
51 changes: 39 additions & 12 deletions x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { SavedObjectsBulkUpdateObject, SavedObjectsFindResult } from '@kbn/core/
import { withSpan } from '@kbn/apm-utils';
import { Logger } from '@kbn/core/server';
import { TaskManagerStartContract, TaskStatus } from '@kbn/task-manager-plugin/server';
import { RawRule, IntervalSchedule } from '../../types';
import { RawRule, IntervalSchedule, SanitizedRule } from '../../types';
import { convertRuleIdsToKueryNode } from '../../lib';
import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events';
import {
Expand All @@ -23,7 +23,6 @@ import { getRuleCircuitBreakerErrorMessage } from '../../../common';
import {
getAuthorizationFilter,
checkAuthorizationAndGetTotal,
getAlertFromRaw,
scheduleTask,
updateMeta,
createNewAPIKeySet,
Expand All @@ -32,6 +31,12 @@ import {
import { RulesClientContext, BulkOperationError, BulkOptions } from '../types';
import { validateScheduleLimit } from '../../application/rule/methods/get_schedule_frequency';
import { RuleAttributes } from '../../data/rule/types';
import {
transformRuleAttributesToRuleDomain,
transformRuleDomainToRule,
} from '../../application/rule/transforms';
import type { RuleParams, RuleDomain } from '../../application/rule/types';
import { ruleDomainSchema } from '../../application/rule/schemas';

const getShouldScheduleTask = async (
context: RulesClientContext,
Expand All @@ -57,8 +62,12 @@ const getShouldScheduleTask = async (
}
};

export const bulkEnableRules = async (context: RulesClientContext, options: BulkOptions) => {
export const bulkEnableRules = async <Params extends RuleParams>(
context: RulesClientContext,
options: BulkOptions
) => {
const { ids, filter } = getAndValidateCommonBulkOptions(options);
const actionsClient = await context.getActionsClient();

const kueryNodeFilter = ids ? convertRuleIdsToKueryNode(ids) : buildKueryNodeFilter(filter);
const authorizationFilter = await getAuthorizationFilter(context, { action: 'ENABLE' });
Expand Down Expand Up @@ -89,18 +98,36 @@ export const bulkEnableRules = async (context: RulesClientContext, options: Bulk
taskManager: context.taskManager,
});

const updatedRules = rules.map(({ id, attributes, references }) => {
return getAlertFromRaw(
context,
id,
attributes.alertTypeId as string,
attributes as RawRule,
references,
false
const enabledRules = rules.map(({ id, attributes, references }) => {
// TODO (http-versioning): alertTypeId should never be null, but we need to
// fix the type cast from SavedObjectsBulkUpdateObject to SavedObjectsBulkUpdateObject
// when we are doing the bulk disable and this should fix itself
const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId!);
const ruleDomain = transformRuleAttributesToRuleDomain<Params>(
attributes as RuleAttributes,
{
id,
logger: context.logger,
ruleType,
references,
omitGeneratedValues: false,
},
(connectorId: string) => actionsClient.isSystemAction(connectorId)
);

try {
ruleDomainSchema.validate(ruleDomain);
} catch (e) {
context.logger.warn(`Error validating bulk enabled rule domain object for id: ${id}, ${e}`);
}
return ruleDomain;
});
// TODO (http-versioning): This should be of type Rule, change this when all rule types are fixed
const enabledPublicRules = enabledRules.map((rule: RuleDomain<Params>) => {
return transformRuleDomainToRule<Params>(rule);
}) as Array<SanitizedRule<Params>>;

return { errors, rules: updatedRules, total, taskIdsFailedToBeEnabled };
return { errors, rules: enabledPublicRules, total, taskIdsFailedToBeEnabled };
};

const bulkEnableRulesWithOCC = async (
Expand Down
Loading