diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts index 77ebddbf8c2dd9..bfee8a94d6dd6d 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts @@ -430,7 +430,7 @@ describe('bulkDisableRules', () => { }); }); - test('should thow an error if number of matched rules greater than 10,000', async () => { + test('should throw an error if number of matched rules greater than 10,000', async () => { unsecuredSavedObjectsClient.find.mockResolvedValue({ aggregations: { alertTypeId: { @@ -464,12 +464,81 @@ describe('bulkDisableRules', () => { ); }); - test('should skip rule if it is already disabled', async () => { + test('should return both rules if one is already disabled and one is enabled when bulk disable is based on ids', async () => { mockCreatePointInTimeFinderAsInternalUser({ saved_objects: [enabledRuleForBulkOps1, disabledRuleForBulkDisable2], }); unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [disabledRuleForBulkDisable1], + saved_objects: [disabledRuleForBulkDisable1, disabledRuleForBulkDisable2], + }); + + const result = await rulesClient.bulkDisableRules({ ids: ['id1', 'id2'] }); + + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + id: 'id1', + attributes: expect.objectContaining({ + enabled: false, + }), + }), + expect.objectContaining({ + id: 'id2', + attributes: expect.objectContaining({ + enabled: false, + }), + }), + ]), + { overwrite: true } + ); + + expect(result.rules[0].id).toBe('id1'); + expect(result.rules[1].id).toBe('id2'); + }); + + test('should return rules in correct format', async () => { + mockCreatePointInTimeFinderAsInternalUser({ + saved_objects: [enabledRuleForBulkOps1, disabledRuleForBulkDisable2], + }); + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [disabledRuleForBulkDisable1, disabledRuleForBulkDisable2], + }); + + const result = await rulesClient.bulkDisableRules({ ids: ['id1', 'id2'] }); + + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + id: 'id1', + attributes: expect.objectContaining({ + enabled: false, + }), + }), + expect.objectContaining({ + id: 'id2', + attributes: expect.objectContaining({ + enabled: false, + }), + }), + ]), + { overwrite: true } + ); + + expect(result).toStrictEqual({ + errors: [], + rules: [returnedRuleForBulkDisable1, returnedRuleForBulkDisable2], + total: 2, + }); + }); + + test('should return both rules if one is already disabled and one is enabled when bulk disable is based on filter', async () => { + mockCreatePointInTimeFinderAsInternalUser({ + saved_objects: [enabledRuleForBulkOps1, disabledRuleForBulkDisable2], + }); + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [disabledRuleForBulkDisable1, disabledRuleForBulkDisable2], }); const result = await rulesClient.bulkDisableRules({ filter: 'fake_filter' }); @@ -483,13 +552,19 @@ describe('bulkDisableRules', () => { enabled: false, }), }), + expect.objectContaining({ + id: 'id2', + attributes: expect.objectContaining({ + enabled: false, + }), + }), ]), { overwrite: true } ); expect(result).toStrictEqual({ errors: [], - rules: [returnedRuleForBulkDisable1], + rules: [returnedRuleForBulkDisable1, returnedRuleForBulkDisable2], total: 2, }); }); @@ -712,8 +787,14 @@ describe('bulkDisableRules', () => { saved_objects: [ enabledRuleForBulkOps1, enabledRuleForBulkOps2, - siemRuleForBulkOps1, - siemRuleForBulkOps2, + { + ...siemRuleForBulkOps1, + attributes: { ...siemRuleForBulkOps1.attributes, enabled: true }, + }, + { + ...siemRuleForBulkOps2, + attributes: { ...siemRuleForBulkOps2.attributes, enabled: true }, + }, ], }; }, @@ -723,8 +804,14 @@ describe('bulkDisableRules', () => { saved_objects: [ enabledRuleForBulkOps1, enabledRuleForBulkOps2, - siemRuleForBulkOps1, - siemRuleForBulkOps2, + { + ...siemRuleForBulkOps1, + attributes: { ...siemRuleForBulkOps1.attributes, enabled: true }, + }, + { + ...siemRuleForBulkOps2, + attributes: { ...siemRuleForBulkOps2.attributes, enabled: true }, + }, ], }); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts index 72a06868e9a3da..78dc98cbbe84ae 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts @@ -133,8 +133,6 @@ const bulkDisableRulesWithOCC = async ( untrack: boolean; } ) => { - const additionalFilter = nodeBuilder.is('alert.attributes.enabled', 'true'); - const rulesFinder = await withSpan( { name: 'encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser', @@ -143,7 +141,7 @@ const bulkDisableRulesWithOCC = async ( () => context.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser( { - filter: filter ? nodeBuilder.and([filter, additionalFilter]) : additionalFilter, + filter, type: RULE_SAVED_OBJECT_TYPE, perPage: 100, ...(context.namespace ? { namespaces: [context.namespace] } : undefined), diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_enable/bulk_enable_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_enable/bulk_enable_rules.test.ts index 9140fcd361769d..78151461285227 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_enable/bulk_enable_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_enable/bulk_enable_rules.test.ts @@ -210,12 +210,12 @@ describe('bulkEnableRules', () => { }); }); - test('should enable two rule and return right actions', async () => { + test('should enable two rules and return right actions', async () => { unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ saved_objects: [enabledRuleForBulkOpsWithActions1, enabledRuleForBulkOpsWithActions2], }); - const result = await rulesClient.bulkDisableRules({ filter: 'fake_filter' }); + const result = await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( @@ -223,13 +223,13 @@ describe('bulkEnableRules', () => { expect.objectContaining({ id: 'id1', attributes: expect.objectContaining({ - enabled: false, + enabled: true, }), }), expect.objectContaining({ id: 'id2', attributes: expect.objectContaining({ - enabled: false, + enabled: true, }), }), ]), @@ -239,6 +239,7 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [], rules: [returnedRuleForBulkEnableWithActions1, returnedRuleForBulkEnableWithActions2], + taskIdsFailedToBeEnabled: [], total: 2, }); }); @@ -432,15 +433,22 @@ describe('bulkEnableRules', () => { }); }); - test('should skip rule if it is already enabled', async () => { + test('should return both rules if one is already enabled and one is disabled when bulk enable is based on ids', async () => { mockCreatePointInTimeFinderAsInternalUser({ saved_objects: [disabledRule1, enabledRuleForBulkOps2], }); unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ - saved_objects: [enabledRuleForBulkOps1], + saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2], }); - const result = await rulesClient.bulkEnableRules({ filter: 'fake_filter' }); + const result = await rulesClient.bulkEnableRules({ + ids: ['id1', 'id2'], + }); + + expect(taskManager.bulkSchedule).not.toHaveBeenCalled(); + + expect(taskManager.bulkEnable).toHaveBeenCalledTimes(1); + expect(taskManager.bulkEnable).toHaveBeenCalledWith(['id1', 'id2']); expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( @@ -457,7 +465,45 @@ describe('bulkEnableRules', () => { expect(result).toStrictEqual({ errors: [], - rules: [returnedRuleForBulkOps1], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], + total: 2, + taskIdsFailedToBeEnabled: [], + }); + }); + + test('should return both rules if one is already enabled and one is disabled when bulk enable is based on filters', async () => { + mockCreatePointInTimeFinderAsInternalUser({ + saved_objects: [disabledRule1, enabledRuleForBulkOps2], + }); + unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: [enabledRuleForBulkOps1, enabledRuleForBulkOps2], + }); + + const result = await rulesClient.bulkEnableRules({ + filter: 'fake_filter', + }); + + expect(taskManager.bulkSchedule).not.toHaveBeenCalled(); + + expect(taskManager.bulkEnable).toHaveBeenCalledTimes(1); + expect(taskManager.bulkEnable).toHaveBeenCalledWith(['id1', 'id2']); + + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + id: 'id1', + attributes: expect.objectContaining({ + enabled: true, + }), + }), + ]), + { overwrite: true } + ); + + expect(result).toStrictEqual({ + errors: [], + rules: [returnedRuleForBulkOps1, returnedRuleForBulkOps2], total: 2, taskIdsFailedToBeEnabled: [], }); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_enable/bulk_enable_rules.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_enable/bulk_enable_rules.ts index 159fc0df4ba178..0bf346006e4f8d 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_enable/bulk_enable_rules.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_enable/bulk_enable_rules.ts @@ -153,8 +153,6 @@ const bulkEnableRulesWithOCC = async ( context: RulesClientContext, { filter }: { filter: KueryNode | null } ) => { - const additionalFilter = nodeBuilder.is('alert.attributes.enabled', 'false'); - const rulesFinder = await withSpan( { name: 'encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser', @@ -163,7 +161,7 @@ const bulkEnableRulesWithOCC = async ( async () => await context.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser( { - filter: filter ? nodeBuilder.and([filter, additionalFilter]) : additionalFilter, + filter, type: RULE_SAVED_OBJECT_TYPE, perPage: 100, ...(context.namespace ? { namespaces: [context.namespace] } : undefined), @@ -187,9 +185,7 @@ const bulkEnableRulesWithOCC = async ( } await rulesFinder.close(); - const updatedInterval = rulesFinderRules - .filter((rule) => !rule.attributes.enabled) - .map((rule) => rule.attributes.schedule?.interval); + const updatedInterval = rulesFinderRules.map((rule) => rule.attributes.schedule?.interval); const validationPayload = await validateScheduleLimit({ context, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/bulk_enable_disable_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/bulk_enable_disable_rules.ts index 670e72d8663d4b..caaaf619bab55f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/bulk_enable_disable_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/bulk_enable_disable_rules.ts @@ -33,7 +33,6 @@ export const bulkEnableDisableRules = async ({ mlAuthz, }: BulkEnableDisableRulesArgs): Promise => { const errors: Array> = []; - const updatedRules: RuleAlertType[] = []; // In the first step, we validate if the rules can be enabled const validatedRules: RuleAlertType[] = []; @@ -64,26 +63,6 @@ export const bulkEnableDisableRules = async ({ ? await rulesClient.bulkEnableRules({ ids: ruleIds }) : await rulesClient.bulkDisableRules({ ids: ruleIds }); - const failedRuleIds = results.errors.map(({ rule: { id } }) => id); - - // We need to go through the original rules array and update rules that were - // not returned as failed from the bulkEnableRules. We cannot rely on the - // results from the bulkEnableRules because the response is not consistent. - // Some rules might be missing in the response if they were skipped by - // Alerting Framework. See this issue for more details: - // https://github.com/elastic/kibana/issues/181050 - updatedRules.push( - ...rules.flatMap((rule) => { - if (failedRuleIds.includes(rule.id)) { - return []; - } - return { - ...rule, - enabled: operation === 'enable', - }; - }) - ); - // Rule objects returned from the bulkEnableRules are not // compatible with the response type. So we need to map them to // the original rules and update the enabled field @@ -99,7 +78,7 @@ export const bulkEnableDisableRules = async ({ ); return { - updatedRules, + updatedRules: results.rules as RuleAlertType[], errors, }; }; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_disable.ts index d69055a92e1f4f..a482374409832e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_disable.ts @@ -347,6 +347,79 @@ export default ({ getService }: FtrProviderContext) => { case 'space_1_all_with_restricted_fixture at space1': expect(response.body.total).to.eql(3); expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + total: 3, + rules: [ + { ...getDefaultRules(response), tags: ['multiple-rules-edit'] }, + { + id: response.body.rules[1].id, + notify_when: 'onThrottleInterval', + enabled: false, + name: 'abc', + tags: ['multiple-rules-edit'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[1].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[1].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[1].updated_at, + created_at: response.body.rules[1].created_at, + scheduled_task_id: response.body.rules[1].scheduled_task_id, + execution_status: response.body.rules[1].execution_status, + monitoring: response.body.rules[1].monitoring, + revision: 0, + ...(response.body.rules[1].next_run + ? { next_run: response.body.rules[1].next_run } + : {}), + ...(response.body.rules[1].last_run + ? { last_run: response.body.rules[1].last_run } + : {}), + }, + { + id: response.body.rules[2].id, + notify_when: 'onThrottleInterval', + enabled: false, + name: 'abc', + tags: ['multiple-rules-edit'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[2].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[2].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[2].updated_at, + created_at: response.body.rules[2].created_at, + scheduled_task_id: response.body.rules[2].scheduled_task_id, + execution_status: response.body.rules[2].execution_status, + monitoring: response.body.rules[2].monitoring, + revision: 0, + ...(response.body.rules[1].next_run + ? { next_run: response.body.rules[2].next_run } + : {}), + ...(response.body.rules[1].last_run + ? { last_run: response.body.rules[2].last_run } + : {}), + }, + ], + errors: [], + }); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -397,6 +470,79 @@ export default ({ getService }: FtrProviderContext) => { case 'space_1_all_with_restricted_fixture at space1': expect(response.body.total).to.eql(3); expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + total: 3, + rules: [ + { ...getDefaultRules(response), tags: ['multiple-rules-disable'] }, + { + id: response.body.rules[1].id, + notify_when: 'onThrottleInterval', + enabled: false, + name: 'abc', + tags: ['multiple-rules-disable'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[1].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[1].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[1].updated_at, + created_at: response.body.rules[1].created_at, + scheduled_task_id: response.body.rules[1].scheduled_task_id, + execution_status: response.body.rules[1].execution_status, + monitoring: response.body.rules[1].monitoring, + revision: 0, + ...(response.body.rules[1].next_run + ? { next_run: response.body.rules[1].next_run } + : {}), + ...(response.body.rules[1].last_run + ? { last_run: response.body.rules[1].last_run } + : {}), + }, + { + id: response.body.rules[2].id, + notify_when: 'onThrottleInterval', + enabled: false, + name: 'abc', + tags: ['multiple-rules-disable'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[2].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[2].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[2].updated_at, + created_at: response.body.rules[2].created_at, + scheduled_task_id: response.body.rules[2].scheduled_task_id, + execution_status: response.body.rules[2].execution_status, + monitoring: response.body.rules[2].monitoring, + revision: 0, + ...(response.body.rules[2].next_run + ? { next_run: response.body.rules[2].next_run } + : {}), + ...(response.body.rules[2].last_run + ? { last_run: response.body.rules[2].last_run } + : {}), + }, + ], + errors: [], + }); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts index 4464535dbcd4d1..e7cd7044717c4a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_enable.ts @@ -178,7 +178,49 @@ export default ({ getService }: FtrProviderContext) => { break; case 'superuser at space1': case 'space_1_all_with_restricted_fixture at space1': - expect(response.body).to.eql(defaultSuccessfulResponse); + expect(response.body).to.eql({ + ...defaultSuccessfulResponse, + rules: [ + { + id: response.body.rules[0].id, + notify_when: 'onThrottleInterval', + enabled: true, + name: 'abc', + tags: ['foo'], + consumer: 'alertsRestrictedFixture', + throttle: '1m', + rule_type_id: 'test.restricted-noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[0].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[0].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[0].updated_at, + created_at: response.body.rules[0].created_at, + revision: 0, + scheduled_task_id: response.body.rules[0].scheduled_task_id, + ...(response.body.rules[0].last_run + ? { last_run: response.body.rules[0].last_run } + : {}), + execution_status: { + last_duration: 0, + last_execution_date: + response.body.rules[0].execution_status.last_execution_date, + status: 'pending', + }, + ...(response.body.rules[0].next_run + ? { next_run: response.body.rules[0].next_run } + : {}), + monitoring: response.body.rules[0].monitoring, + }, + ], + }); expect(response.statusCode).to.eql(200); break; default: @@ -275,7 +317,49 @@ export default ({ getService }: FtrProviderContext) => { case 'space_1_all at space1': case 'space_1_all_alerts_none_actions at space1': case 'space_1_all_with_restricted_fixture at space1': - expect(response.body).to.eql(defaultSuccessfulResponse); + expect(response.body).to.eql({ + ...defaultSuccessfulResponse, + rules: [ + { + id: response.body.rules[0].id, + notify_when: 'onThrottleInterval', + enabled: true, + name: 'abc', + tags: ['foo'], + consumer: 'alerts', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[0].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[0].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[0].updated_at, + created_at: response.body.rules[0].created_at, + revision: 0, + scheduled_task_id: response.body.rules[0].scheduled_task_id, + ...(response.body.rules[0].last_run + ? { last_run: response.body.rules[0].last_run } + : {}), + execution_status: { + last_duration: 0, + last_execution_date: + response.body.rules[0].execution_status.last_execution_date, + status: 'pending', + }, + ...(response.body.rules[0].next_run + ? { next_run: response.body.rules[0].next_run } + : {}), + monitoring: response.body.rules[0].monitoring, + }, + ], + }); expect(response.statusCode).to.eql(200); break; default: @@ -289,7 +373,7 @@ export default ({ getService }: FtrProviderContext) => { supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestRuleData({ tags: ['multiple-rules-edit'] })) + .send(getTestRuleData({ tags: ['multiple-rules-edit-with-ids'] })) .expect(200) ) ); @@ -325,7 +409,126 @@ export default ({ getService }: FtrProviderContext) => { case 'superuser at space1': case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': - expect(response.body).to.eql({ ...defaultSuccessfulResponse, total: 3 }); + expect(response.body).to.eql({ + ...defaultSuccessfulResponse, + rules: [ + { + id: response.body.rules[0].id, + notify_when: 'onThrottleInterval', + enabled: true, + name: 'abc', + tags: ['multiple-rules-edit-with-ids'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[0].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[0].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[0].updated_at, + created_at: response.body.rules[0].created_at, + revision: 0, + scheduled_task_id: response.body.rules[0].scheduled_task_id, + ...(response.body.rules[0].last_run + ? { last_run: response.body.rules[0].last_run } + : {}), + execution_status: { + last_duration: 0, + last_execution_date: + response.body.rules[0].execution_status.last_execution_date, + status: 'pending', + }, + ...(response.body.rules[0].next_run + ? { next_run: response.body.rules[0].next_run } + : {}), + monitoring: response.body.rules[0].monitoring, + }, + { + id: response.body.rules[1].id, + notify_when: 'onThrottleInterval', + enabled: true, + name: 'abc', + tags: ['multiple-rules-edit-with-ids'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[1].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[1].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[1].updated_at, + created_at: response.body.rules[1].created_at, + revision: 0, + scheduled_task_id: response.body.rules[1].scheduled_task_id, + ...(response.body.rules[1].last_run + ? { last_run: response.body.rules[1].last_run } + : {}), + execution_status: { + last_duration: 0, + last_execution_date: + response.body.rules[1].execution_status.last_execution_date, + status: 'pending', + }, + ...(response.body.rules[1].next_run + ? { next_run: response.body.rules[1].next_run } + : {}), + monitoring: response.body.rules[1].monitoring, + }, + { + id: response.body.rules[2].id, + notify_when: 'onThrottleInterval', + enabled: true, + name: 'abc', + tags: ['multiple-rules-edit-with-ids'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[2].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[2].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[2].updated_at, + created_at: response.body.rules[2].created_at, + revision: 0, + scheduled_task_id: response.body.rules[2].scheduled_task_id, + ...(response.body.rules[2].last_run + ? { last_run: response.body.rules[2].last_run } + : {}), + execution_status: { + last_duration: 0, + last_execution_date: + response.body.rules[2].execution_status.last_execution_date, + status: 'pending', + }, + ...(response.body.rules[2].next_run + ? { next_run: response.body.rules[2].next_run } + : {}), + monitoring: response.body.rules[2].monitoring, + }, + ], + total: 3, + }); expect(response.statusCode).to.eql(200); break; default: @@ -339,7 +542,7 @@ export default ({ getService }: FtrProviderContext) => { supertest .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestRuleData({ tags: ['multiple-rules-enable'] })) + .send(getTestRuleData({ tags: ['multiple-rules-edit-with-filter'] })) .expect(200) ) ); @@ -350,7 +553,7 @@ export default ({ getService }: FtrProviderContext) => { const response = await supertestWithoutAuth .patch(`${getUrlPrefix(space.id)}/internal/alerting/rules/_bulk_enable`) .set('kbn-xsrf', 'foo') - .send({ filter: `alert.attributes.tags: "multiple-rules-enable"` }) + .send({ filter: `alert.attributes.tags: "multiple-rules-edit-with-filter"` }) .auth(user.username, user.password); switch (scenario.id) { @@ -375,7 +578,126 @@ export default ({ getService }: FtrProviderContext) => { case 'superuser at space1': case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': - expect(response.body).to.eql({ ...defaultSuccessfulResponse, total: 3 }); + expect(response.body).to.eql({ + ...defaultSuccessfulResponse, + total: 3, + rules: [ + { + id: response.body.rules[0].id, + notify_when: 'onThrottleInterval', + enabled: true, + name: 'abc', + tags: ['multiple-rules-edit-with-filter'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[0].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[0].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[0].updated_at, + created_at: response.body.rules[0].created_at, + revision: 0, + scheduled_task_id: response.body.rules[0].scheduled_task_id, + ...(response.body.rules[0].last_run + ? { last_run: response.body.rules[0].last_run } + : {}), + execution_status: { + last_duration: 0, + last_execution_date: + response.body.rules[0].execution_status.last_execution_date, + status: 'pending', + }, + ...(response.body.rules[0].next_run + ? { next_run: response.body.rules[0].next_run } + : {}), + monitoring: response.body.rules[0].monitoring, + }, + { + id: response.body.rules[1].id, + notify_when: 'onThrottleInterval', + enabled: true, + name: 'abc', + tags: ['multiple-rules-edit-with-filter'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[1].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[1].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[1].updated_at, + created_at: response.body.rules[1].created_at, + revision: 0, + scheduled_task_id: response.body.rules[1].scheduled_task_id, + ...(response.body.rules[1].last_run + ? { last_run: response.body.rules[1].last_run } + : {}), + execution_status: { + last_duration: 0, + last_execution_date: + response.body.rules[1].execution_status.last_execution_date, + status: 'pending', + }, + ...(response.body.rules[1].next_run + ? { next_run: response.body.rules[1].next_run } + : {}), + monitoring: response.body.rules[1].monitoring, + }, + { + id: response.body.rules[2].id, + notify_when: 'onThrottleInterval', + enabled: true, + name: 'abc', + tags: ['multiple-rules-edit-with-filter'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[2].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[2].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[2].updated_at, + created_at: response.body.rules[2].created_at, + revision: 0, + scheduled_task_id: response.body.rules[2].scheduled_task_id, + ...(response.body.rules[2].last_run + ? { last_run: response.body.rules[2].last_run } + : {}), + execution_status: { + last_duration: 0, + last_execution_date: + response.body.rules[2].execution_status.last_execution_date, + status: 'pending', + }, + ...(response.body.rules[2].next_run + ? { next_run: response.body.rules[2].next_run } + : {}), + monitoring: response.body.rules[2].monitoring, + }, + ], + }); expect(response.statusCode).to.eql(200); break; default: @@ -400,7 +722,49 @@ export default ({ getService }: FtrProviderContext) => { switch (scenario.id) { // This superuser has more privileges that we think case 'superuser at space1': - expect(response.body).to.eql(defaultSuccessfulResponse); + expect(response.body).to.eql({ + ...defaultSuccessfulResponse, + rules: [ + { + id: response.body.rules[0].id, + notify_when: 'onThrottleInterval', + enabled: true, + name: 'abc', + tags: ['foo'], + consumer: 'alertsFixture', + throttle: '1m', + rule_type_id: 'test.noop', + api_key_created_by_user: false, + api_key_owner: response.body.rules[0].api_key_owner, + created_by: 'elastic', + updated_by: response.body.rules[0].updated_by, + mute_all: false, + muted_alert_ids: [], + schedule: { interval: '1m' }, + actions: [], + params: {}, + running: false, + snooze_schedule: [], + updated_at: response.body.rules[0].updated_at, + created_at: response.body.rules[0].created_at, + revision: 0, + scheduled_task_id: response.body.rules[0].scheduled_task_id, + ...(response.body.rules[0].last_run + ? { last_run: response.body.rules[0].last_run } + : {}), + execution_status: { + last_duration: 0, + last_execution_date: + response.body.rules[0].execution_status.last_execution_date, + status: 'pending', + }, + ...(response.body.rules[0].next_run + ? { next_run: response.body.rules[0].next_run } + : {}), + monitoring: response.body.rules[0].monitoring, + }, + ], + }); expect(response.statusCode).to.eql(200); break; case 'global_read at space1':