mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Security Solution][Detections] increases coverage of bulk edit rules action according to test plan (#141929) (#142978)
## Summary
- adds missing tests according to [test plan](https://docs.google.com/document/d/116x7ITTTJQ6cTiwaGK831_f6Ox7XB3qyLiHxC3Cmf8w/edit#heading=h.tzaw2977z8ue) (internal document)
- small refactoring of cypress tasks related to rule actions: create Email connector moved to common tasks from create_rule screen, as it used in bulk editing as well
(cherry picked from commit d07301fcb6
)
Co-authored-by: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com>
This commit is contained in:
parent
2ed57765c6
commit
da2bd15e9d
19 changed files with 670 additions and 194 deletions
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ROLES } from '../../../common/test';
|
||||
|
||||
import {
|
||||
RULES_BULK_EDIT_ACTIONS_INFO,
|
||||
RULES_BULK_EDIT_ACTIONS_WARNING,
|
||||
ADD_RULE_ACTIONS_MENU_ITEM,
|
||||
} from '../../screens/rules_bulk_edit';
|
||||
import { actionFormSelector } from '../../screens/common/rule_actions';
|
||||
|
||||
import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../tasks/common';
|
||||
import {
|
||||
addSlackRuleAction,
|
||||
assertSlackRuleAction,
|
||||
addEmailConnectorAndRuleAction,
|
||||
assertEmailRuleAction,
|
||||
} from '../../tasks/common/rule_actions';
|
||||
import {
|
||||
waitForRulesTableToBeLoaded,
|
||||
selectNumberOfRules,
|
||||
loadPrebuiltDetectionRulesFromHeaderBtn,
|
||||
goToEditRuleActionsSettingsOf,
|
||||
} from '../../tasks/alerts_detection_rules';
|
||||
import {
|
||||
waitForBulkEditActionToFinish,
|
||||
submitBulkEditForm,
|
||||
checkOverwriteRuleActionsCheckbox,
|
||||
openBulkEditRuleActionsForm,
|
||||
pickActionFrequency,
|
||||
openBulkActionsMenu,
|
||||
} from '../../tasks/rules_bulk_edit';
|
||||
import { assertSelectedActionFrequency } from '../../tasks/edit_rule';
|
||||
import { login, visitWithoutDateRange } from '../../tasks/login';
|
||||
import { esArchiverResetKibana } from '../../tasks/es_archiver';
|
||||
|
||||
import { SECURITY_DETECTIONS_RULES_URL } from '../../urls/navigation';
|
||||
|
||||
import {
|
||||
createMachineLearningRule,
|
||||
createCustomIndicatorRule,
|
||||
createEventCorrelationRule,
|
||||
createThresholdRule,
|
||||
createNewTermsRule,
|
||||
createSavedQueryRule,
|
||||
createCustomRuleEnabled,
|
||||
} from '../../tasks/api_calls/rules';
|
||||
import { createSlackConnector } from '../../tasks/api_calls/connectors';
|
||||
|
||||
import {
|
||||
getEqlRule,
|
||||
getNewThreatIndicatorRule,
|
||||
getNewRule,
|
||||
getNewThresholdRule,
|
||||
getMachineLearningRule,
|
||||
getNewTermsRule,
|
||||
} from '../../objects/rule';
|
||||
|
||||
const ruleNameToAssert = 'Custom rule name with actions';
|
||||
const expectedNumberOfCustomRulesToBeEdited = 7;
|
||||
// 7 custom rules of different types + 3 prebuilt.
|
||||
// number of selected rules doesn't matter, we only want to make sure they will be edited an no modal window displayed as for other actions
|
||||
const expectedNumberOfRulesToBeEdited = expectedNumberOfCustomRulesToBeEdited + 3;
|
||||
|
||||
const expectedExistingSlackMessage = 'Existing slack action';
|
||||
const expectedSlackMessage = 'Slack action test message';
|
||||
|
||||
describe('Detection rules, bulk edit of rule actions', () => {
|
||||
before(() => {
|
||||
cleanKibana();
|
||||
login();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
deleteConnectors();
|
||||
esArchiverResetKibana();
|
||||
|
||||
createSlackConnector().then(({ body }) => {
|
||||
const actions = [
|
||||
{
|
||||
id: body.id,
|
||||
action_type_id: '.slack',
|
||||
group: 'default',
|
||||
params: {
|
||||
message: expectedExistingSlackMessage,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
createCustomRuleEnabled(
|
||||
{
|
||||
...getNewRule(),
|
||||
name: ruleNameToAssert,
|
||||
},
|
||||
'1',
|
||||
'100m',
|
||||
500,
|
||||
actions
|
||||
);
|
||||
});
|
||||
|
||||
createEventCorrelationRule(getEqlRule(), '2');
|
||||
createMachineLearningRule(getMachineLearningRule(), '3');
|
||||
createCustomIndicatorRule(getNewThreatIndicatorRule(), '4');
|
||||
createThresholdRule(getNewThresholdRule(), '5');
|
||||
createNewTermsRule(getNewTermsRule(), '6');
|
||||
createSavedQueryRule({ ...getNewRule(), savedId: 'mocked' }, '7');
|
||||
|
||||
createSlackConnector();
|
||||
});
|
||||
|
||||
context('Restricted action privileges', () => {
|
||||
it("User with no privileges can't add rule actions", () => {
|
||||
login(ROLES.hunter_no_actions);
|
||||
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL, ROLES.hunter_no_actions);
|
||||
waitForRulesTableToBeLoaded();
|
||||
|
||||
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
|
||||
|
||||
openBulkActionsMenu();
|
||||
|
||||
cy.get(ADD_RULE_ACTIONS_MENU_ITEM).should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
context('All actions privileges', () => {
|
||||
beforeEach(() => {
|
||||
visitWithoutDateRange(SECURITY_DETECTIONS_RULES_URL);
|
||||
waitForRulesTableToBeLoaded();
|
||||
});
|
||||
|
||||
it('Add a rule action to rules (existing connector)', () => {
|
||||
const expectedActionFrequency = 'Daily';
|
||||
|
||||
loadPrebuiltDetectionRulesFromHeaderBtn();
|
||||
|
||||
// select both custom and prebuilt rules
|
||||
selectNumberOfRules(expectedNumberOfRulesToBeEdited);
|
||||
openBulkEditRuleActionsForm();
|
||||
|
||||
// ensure rule actions info callout displayed on the form
|
||||
cy.get(RULES_BULK_EDIT_ACTIONS_INFO).should('be.visible');
|
||||
|
||||
pickActionFrequency(expectedActionFrequency);
|
||||
addSlackRuleAction(expectedSlackMessage);
|
||||
|
||||
submitBulkEditForm();
|
||||
waitForBulkEditActionToFinish({ rulesCount: expectedNumberOfRulesToBeEdited });
|
||||
|
||||
// check if rule has been updated
|
||||
goToEditRuleActionsSettingsOf(ruleNameToAssert);
|
||||
|
||||
assertSelectedActionFrequency(expectedActionFrequency);
|
||||
assertSlackRuleAction(expectedExistingSlackMessage, 0);
|
||||
assertSlackRuleAction(expectedSlackMessage, 1);
|
||||
// ensure there is no third action
|
||||
cy.get(actionFormSelector(2)).should('not.exist');
|
||||
});
|
||||
|
||||
it('Overwrite rule actions in rules', () => {
|
||||
const expectedActionFrequency = 'On each rule execution';
|
||||
|
||||
loadPrebuiltDetectionRulesFromHeaderBtn();
|
||||
|
||||
// select both custom and prebuilt rules
|
||||
selectNumberOfRules(expectedNumberOfRulesToBeEdited);
|
||||
openBulkEditRuleActionsForm();
|
||||
|
||||
pickActionFrequency(expectedActionFrequency);
|
||||
addSlackRuleAction(expectedSlackMessage);
|
||||
|
||||
// check overwrite box, ensure warning is displayed
|
||||
checkOverwriteRuleActionsCheckbox();
|
||||
cy.get(RULES_BULK_EDIT_ACTIONS_WARNING).contains(
|
||||
`You're about to overwrite rule actions for ${expectedNumberOfRulesToBeEdited} selected rules`
|
||||
);
|
||||
|
||||
submitBulkEditForm();
|
||||
waitForBulkEditActionToFinish({ rulesCount: expectedNumberOfRulesToBeEdited });
|
||||
|
||||
// check if rule has been updated
|
||||
goToEditRuleActionsSettingsOf(ruleNameToAssert);
|
||||
|
||||
assertSelectedActionFrequency(expectedActionFrequency);
|
||||
assertSlackRuleAction(expectedSlackMessage);
|
||||
// ensure existing action was overwritten
|
||||
cy.get(actionFormSelector(1)).should('not.exist');
|
||||
});
|
||||
|
||||
it('Add a rule action to rules (new connector)', () => {
|
||||
const expectedActionFrequency = 'Hourly';
|
||||
const expectedEmail = 'test@example.com';
|
||||
const expectedSubject = 'Subject';
|
||||
|
||||
selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited);
|
||||
openBulkEditRuleActionsForm();
|
||||
|
||||
pickActionFrequency(expectedActionFrequency);
|
||||
addEmailConnectorAndRuleAction(expectedEmail, expectedSubject);
|
||||
|
||||
submitBulkEditForm();
|
||||
waitForBulkEditActionToFinish({ rulesCount: expectedNumberOfCustomRulesToBeEdited });
|
||||
|
||||
// check if rule has been updated
|
||||
goToEditRuleActionsSettingsOf(ruleNameToAssert);
|
||||
|
||||
assertSelectedActionFrequency(expectedActionFrequency);
|
||||
assertEmailRuleAction(expectedEmail, expectedSubject);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -39,16 +39,10 @@ import {
|
|||
RULE_NAME_INPUT,
|
||||
SCHEDULE_INTERVAL_AMOUNT_INPUT,
|
||||
SCHEDULE_INTERVAL_UNITS_INPUT,
|
||||
SCHEDULE_CONTINUE_BUTTON,
|
||||
SEVERITY_DROPDOWN,
|
||||
TAGS_CLEAR_BUTTON,
|
||||
TAGS_FIELD,
|
||||
EMAIL_ACTION_BTN,
|
||||
CREATE_ACTION_CONNECTOR_BTN,
|
||||
SAVE_ACTION_CONNECTOR_BTN,
|
||||
FROM_VALIDATION_ERROR,
|
||||
EMAIL_ACTION_TO_INPUT,
|
||||
EMAIL_ACTION_SUBJECT_INPUT,
|
||||
SCHEDULE_CONTINUE_BUTTON,
|
||||
} from '../../screens/create_new_rule';
|
||||
import {
|
||||
ADDITIONAL_LOOK_BACK_DETAILS,
|
||||
|
@ -86,12 +80,12 @@ import {
|
|||
import { createCustomRuleEnabled } from '../../tasks/api_calls/rules';
|
||||
import { createTimeline } from '../../tasks/api_calls/timelines';
|
||||
import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common';
|
||||
import { addEmailConnectorAndRuleAction } from '../../tasks/common/rule_actions';
|
||||
import {
|
||||
createAndEnableRule,
|
||||
fillAboutRule,
|
||||
fillAboutRuleAndContinue,
|
||||
fillDefineCustomRuleAndContinue,
|
||||
fillEmailConnectorForm,
|
||||
fillScheduleRuleAndContinue,
|
||||
goToAboutStepTab,
|
||||
goToActionsStepTab,
|
||||
|
@ -377,15 +371,8 @@ describe('Custom query rules', () => {
|
|||
cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions');
|
||||
|
||||
cy.get(ACTIONS_THROTTLE_INPUT).select('Weekly');
|
||||
cy.get(EMAIL_ACTION_BTN).click();
|
||||
cy.get(CREATE_ACTION_CONNECTOR_BTN).click();
|
||||
fillEmailConnectorForm();
|
||||
cy.get(SAVE_ACTION_CONNECTOR_BTN).click();
|
||||
|
||||
cy.get(EMAIL_ACTION_TO_INPUT).type('test@example.com');
|
||||
cy.get(EMAIL_ACTION_SUBJECT_INPUT).type('Subject');
|
||||
|
||||
cy.get(FROM_VALIDATION_ERROR).should('not.exist');
|
||||
addEmailConnectorAndRuleAction('test@example.com', 'Subject');
|
||||
|
||||
goToAboutStepTab();
|
||||
cy.get(TAGS_CLEAR_BUTTON).click({ force: true });
|
||||
|
|
|
@ -10,3 +10,7 @@ export const TIMELINE_SEARCHBOX = '[data-test-subj="timeline-super-select-search
|
|||
export const EUI_FILTER_SELECT_ITEM = '.euiFilterSelectItem';
|
||||
|
||||
export const EUI_CHECKBOX = '.euiCheckbox__input';
|
||||
|
||||
export const COMBO_BOX_INPUT = '[data-test-subj="comboBoxInput"]';
|
||||
|
||||
export const COMBO_BOX_SELECTION = '.euiMark';
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const EMAIL_ACTION_BTN = '[data-test-subj=".email-siem-ActionTypeSelectOption"]';
|
||||
|
||||
export const CREATE_ACTION_CONNECTOR_BTN = '[data-test-subj="createActionConnectorButton-0"]';
|
||||
|
||||
export const SAVE_ACTION_CONNECTOR_BTN = '[data-test-subj="saveActionButtonModal"]';
|
||||
|
||||
export const EMAIL_ACTION_TO_INPUT = '[data-test-subj="toEmailAddressInput"]';
|
||||
|
||||
export const EMAIL_ACTION_SUBJECT_INPUT = '[data-test-subj="subjectInput"]';
|
||||
|
||||
export const SLACK_ACTION_BTN = '[data-test-subj=".slack-siem-ActionTypeSelectOption"]';
|
||||
|
||||
export const SLACK_ACTION_MESSAGE_TEXTAREA = '[data-test-subj="messageTextArea"]';
|
||||
|
||||
export const CONNECTOR_NAME_INPUT = '[data-test-subj="nameInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_FROM_INPUT = '[data-test-subj="emailFromInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_HOST_INPUT = '[data-test-subj="emailHostInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_PORT_INPUT = '[data-test-subj="emailPortInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_USER_INPUT = '[data-test-subj="emailUserInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_PASSWORD_INPUT = '[data-test-subj="emailPasswordInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_SERVICE_SELECTOR = '[data-test-subj="emailServiceSelectInput"]';
|
||||
|
||||
export const FORM_VALIDATION_ERROR = '.euiFormErrorText';
|
||||
|
||||
export const JSON_EDITOR = "[data-test-subj='actionJsonEditor']";
|
||||
|
||||
export const INDEX_SELECTOR = "[data-test-subj='.index-siem-ActionTypeSelectOption']";
|
||||
|
||||
export const actionFormSelector = (position: number) =>
|
||||
`[data-test-subj="alertActionAccordion-${position}"]`;
|
|
@ -16,34 +16,6 @@ export const ACTIONS_EDIT_TAB = '[data-test-subj="edit-rule-actions-tab"]';
|
|||
export const ACTIONS_THROTTLE_INPUT =
|
||||
'[data-test-subj="stepRuleActions"] [data-test-subj="select"]';
|
||||
|
||||
export const EMAIL_ACTION_BTN = '[data-test-subj=".email-siem-ActionTypeSelectOption"]';
|
||||
|
||||
export const CREATE_ACTION_CONNECTOR_BTN = '[data-test-subj="createActionConnectorButton-0"]';
|
||||
|
||||
export const SAVE_ACTION_CONNECTOR_BTN = '[data-test-subj="saveActionButtonModal"]';
|
||||
|
||||
export const EMAIL_ACTION_TO_INPUT = '[data-test-subj="toEmailAddressInput"]';
|
||||
|
||||
export const EMAIL_ACTION_SUBJECT_INPUT = '[data-test-subj="subjectInput"]';
|
||||
|
||||
export const FROM_VALIDATION_ERROR = '.euiFormErrorText';
|
||||
|
||||
export const CONNECTOR_NAME_INPUT = '[data-test-subj="nameInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_FROM_INPUT = '[data-test-subj="emailFromInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_HOST_INPUT = '[data-test-subj="emailHostInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_PORT_INPUT = '[data-test-subj="emailPortInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_USER_INPUT = '[data-test-subj="emailUserInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_PASSWORD_INPUT = '[data-test-subj="emailPasswordInput"]';
|
||||
|
||||
export const EMAIL_CONNECTOR_SERVICE_SELECTOR = '[data-test-subj="emailServiceSelectInput"]';
|
||||
|
||||
export const JSON_EDITOR = "[data-test-subj='actionJsonEditor']";
|
||||
|
||||
export const ADD_FALSE_POSITIVE_BTN =
|
||||
'[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text';
|
||||
|
||||
|
@ -58,10 +30,6 @@ export const BACK_TO_ALL_RULES_LINK = '[data-test-subj="ruleDetailsBackToAllRule
|
|||
|
||||
export const COMBO_BOX_CLEAR_BTN = '[data-test-subj="comboBoxClearButton"]';
|
||||
|
||||
export const COMBO_BOX_INPUT = '[data-test-subj="comboBoxInput"]';
|
||||
|
||||
export const COMBO_BOX_SELECTION = '.euiMark';
|
||||
|
||||
export const CREATE_AND_ENABLE_BTN = '[data-test-subj="create-enable"]';
|
||||
|
||||
export const CUSTOM_QUERY_INPUT = '[data-test-subj="queryInput"]';
|
||||
|
@ -260,7 +228,3 @@ export const savedQueryByName = (savedQueryName: string) =>
|
|||
|
||||
export const APPLY_SELECTED_SAVED_QUERY_BUTTON =
|
||||
'[data-test-subj="saved-query-management-apply-changes-button"]';
|
||||
|
||||
export const INDEX_SELECTOR = "[data-test-subj='.index-siem-ActionTypeSelectOption']";
|
||||
|
||||
export const CREATE_CONNECTOR_BTN = "[data-test-subj='createActionConnectorButton-0']";
|
||||
|
|
|
@ -123,3 +123,5 @@ export const BACK_TO_RULES = '[data-test-subj="ruleDetailsBackToAllRules"]';
|
|||
|
||||
export const DEFINE_RULE_PANEL_PROGRESS =
|
||||
'[data-test-subj="defineRule"] [data-test-subj="stepPanelProgress"]';
|
||||
|
||||
export const EDIT_RULE_SETTINGS_LINK = '[data-test-subj="editRuleSettingsLink"]';
|
||||
|
|
|
@ -15,6 +15,8 @@ export const DELETE_INDEX_PATTERNS_RULE_BULK_MENU_ITEM =
|
|||
|
||||
export const UPDATE_SCHEDULE_MENU_ITEM = '[data-test-subj="setScheduleBulk"]';
|
||||
|
||||
export const ADD_RULE_ACTIONS_MENU_ITEM = '[data-test-subj="addRuleActionsBulk"]';
|
||||
|
||||
export const APPLY_TIMELINE_RULE_BULK_MENU_ITEM = '[data-test-subj="applyTimelineTemplateBulk"]';
|
||||
|
||||
export const TAGS_RULE_BULK_MENU_ITEM = '[data-test-subj="tagsBulkEditRule"]';
|
||||
|
@ -65,3 +67,13 @@ export const UPDATE_SCHEDULE_LOOKBACK_INPUT =
|
|||
export const UPDATE_SCHEDULE_TIME_INTERVAL = '[data-test-subj="interval"]';
|
||||
|
||||
export const UPDATE_SCHEDULE_TIME_UNIT_SELECT = '[data-test-subj="timeType"]';
|
||||
|
||||
export const RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT =
|
||||
'[data-test-subj="bulkEditRulesRuleActionThrottle"] [data-test-subj="select"]';
|
||||
|
||||
export const RULES_BULK_EDIT_ACTIONS_INFO = '[data-test-subj="bulkEditRulesRuleActionInfo"]';
|
||||
|
||||
export const RULES_BULK_EDIT_ACTIONS_WARNING = '[data-test-subj="bulkEditRulesRuleActionsWarning"]';
|
||||
|
||||
export const RULES_BULK_EDIT_OVERWRITE_ACTIONS_CHECKBOX =
|
||||
'[data-test-subj="bulkEditRulesOverwriteRuleActions"]';
|
||||
|
|
|
@ -59,8 +59,12 @@ import {
|
|||
} from '../screens/alerts_detection_rules';
|
||||
import { EUI_CHECKBOX } from '../screens/common/controls';
|
||||
import { ALL_ACTIONS } from '../screens/rule_details';
|
||||
import { EDIT_SUBMIT_BUTTON } from '../screens/edit_rule';
|
||||
import { LOADING_INDICATOR } from '../screens/security_header';
|
||||
|
||||
import { goToRuleEditSettings } from './rule_details';
|
||||
import { goToActionsStepTab } from './create_new_rule';
|
||||
|
||||
export const enableRule = (rulePosition: number) => {
|
||||
cy.get(RULE_SWITCH).eq(rulePosition).click({ force: true });
|
||||
};
|
||||
|
@ -387,3 +391,11 @@ export const cancelConfirmationModal = () => {
|
|||
export const clickErrorToastBtn = () => {
|
||||
cy.get(TOASTER_ERROR_BTN).click();
|
||||
};
|
||||
|
||||
export const goToEditRuleActionsSettingsOf = (name: string) => {
|
||||
goToTheRuleDetailsOf(name);
|
||||
goToRuleEditSettings();
|
||||
// wait until first step loads completely. Otherwise cypress stuck at the first edit page
|
||||
cy.get(EDIT_SUBMIT_BUTTON).should('be.enabled');
|
||||
goToActionsStepTab();
|
||||
};
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const createConnector = (connector: Record<string, unknown>) =>
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: '/api/actions/action',
|
||||
body: connector,
|
||||
headers: { 'kbn-xsrf': 'cypress-creds' },
|
||||
});
|
||||
|
||||
const slackConnectorAPIPayload = {
|
||||
actionTypeId: '.slack',
|
||||
secrets: {
|
||||
webhookUrl: 'http://localhost:123',
|
||||
},
|
||||
name: 'Slack cypress test e2e connector',
|
||||
};
|
||||
|
||||
export const createSlackConnector = () => createConnector(slackConnectorAPIPayload);
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Actions } from '@kbn/securitysolution-io-ts-alerting-types';
|
||||
|
||||
import type {
|
||||
CustomRule,
|
||||
ThreatIndicatorRule,
|
||||
|
@ -70,6 +72,7 @@ export const createCustomRule = (
|
|||
timeline_title: timeline.title,
|
||||
}
|
||||
: {}),
|
||||
actions: rule.actions,
|
||||
},
|
||||
headers: { 'kbn-xsrf': 'cypress-creds' },
|
||||
failOnStatusCode: false,
|
||||
|
@ -254,7 +257,8 @@ export const createCustomRuleEnabled = (
|
|||
rule: CustomRule,
|
||||
ruleId = '1',
|
||||
interval = '100m',
|
||||
maxSignals = 500
|
||||
maxSignals = 500,
|
||||
actions?: Actions
|
||||
) => {
|
||||
const riskScore = rule.riskScore != null ? parseInt(rule.riskScore, 10) : undefined;
|
||||
const severity = rule.severity != null ? rule.severity.toLocaleLowerCase() : undefined;
|
||||
|
@ -280,6 +284,7 @@ export const createCustomRuleEnabled = (
|
|||
tags: ['rule1'],
|
||||
max_signals: maxSignals,
|
||||
building_block_type: rule.buildingBlockType,
|
||||
actions,
|
||||
},
|
||||
headers: { 'kbn-xsrf': 'cypress-creds' },
|
||||
failOnStatusCode: false,
|
||||
|
@ -306,6 +311,7 @@ export const createCustomRuleEnabled = (
|
|||
tags: ['rule1'],
|
||||
max_signals: maxSignals,
|
||||
building_block_type: rule.buildingBlockType,
|
||||
actions,
|
||||
},
|
||||
headers: { 'kbn-xsrf': 'cypress-creds' },
|
||||
failOnStatusCode: false,
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
SLACK_ACTION_BTN,
|
||||
SLACK_ACTION_MESSAGE_TEXTAREA,
|
||||
EMAIL_ACTION_BTN,
|
||||
CREATE_ACTION_CONNECTOR_BTN,
|
||||
SAVE_ACTION_CONNECTOR_BTN,
|
||||
EMAIL_ACTION_TO_INPUT,
|
||||
EMAIL_ACTION_SUBJECT_INPUT,
|
||||
CONNECTOR_NAME_INPUT,
|
||||
EMAIL_CONNECTOR_SERVICE_SELECTOR,
|
||||
EMAIL_CONNECTOR_FROM_INPUT,
|
||||
EMAIL_CONNECTOR_HOST_INPUT,
|
||||
EMAIL_CONNECTOR_PORT_INPUT,
|
||||
EMAIL_CONNECTOR_USER_INPUT,
|
||||
EMAIL_CONNECTOR_PASSWORD_INPUT,
|
||||
FORM_VALIDATION_ERROR,
|
||||
JSON_EDITOR,
|
||||
} from '../../screens/common/rule_actions';
|
||||
import { COMBO_BOX_INPUT, COMBO_BOX_SELECTION } from '../../screens/common/controls';
|
||||
import type { EmailConnector, IndexConnector } from '../../objects/connector';
|
||||
import { getEmailConnector, getIndexConnector } from '../../objects/connector';
|
||||
|
||||
export const addSlackRuleAction = (message: string) => {
|
||||
cy.get(SLACK_ACTION_BTN).click();
|
||||
cy.get(SLACK_ACTION_MESSAGE_TEXTAREA).clear().type(message);
|
||||
};
|
||||
|
||||
export const assertSlackRuleAction = (message: string, position: number = 0) => {
|
||||
cy.get(SLACK_ACTION_MESSAGE_TEXTAREA).eq(position).should('have.value', message);
|
||||
};
|
||||
|
||||
export const fillEmailConnectorForm = (connector: EmailConnector = getEmailConnector()) => {
|
||||
cy.get(CONNECTOR_NAME_INPUT).type(connector.name);
|
||||
cy.get(EMAIL_CONNECTOR_SERVICE_SELECTOR).select(connector.service);
|
||||
cy.get(EMAIL_CONNECTOR_FROM_INPUT).type(connector.from);
|
||||
cy.get(EMAIL_CONNECTOR_HOST_INPUT).type(connector.host);
|
||||
cy.get(EMAIL_CONNECTOR_PORT_INPUT).type(connector.port);
|
||||
cy.get(EMAIL_CONNECTOR_USER_INPUT).type(connector.user);
|
||||
cy.get(EMAIL_CONNECTOR_PASSWORD_INPUT).type(connector.password);
|
||||
};
|
||||
|
||||
export const createEmailConnector = () => {
|
||||
cy.get(CREATE_ACTION_CONNECTOR_BTN).click();
|
||||
fillEmailConnectorForm();
|
||||
cy.get(SAVE_ACTION_CONNECTOR_BTN).click();
|
||||
};
|
||||
|
||||
export const fillEmailRuleActionForm = (email: string, subject: string) => {
|
||||
cy.get(EMAIL_ACTION_TO_INPUT).type(email);
|
||||
cy.get(EMAIL_ACTION_SUBJECT_INPUT).type(subject);
|
||||
};
|
||||
|
||||
export const addEmailConnectorAndRuleAction = (email: string, subject: string) => {
|
||||
cy.get(EMAIL_ACTION_BTN).click();
|
||||
createEmailConnector();
|
||||
fillEmailRuleActionForm(email, subject);
|
||||
|
||||
cy.get(FORM_VALIDATION_ERROR).should('not.exist');
|
||||
};
|
||||
|
||||
export const assertEmailRuleAction = (email: string, subject: string) => {
|
||||
cy.get(EMAIL_ACTION_TO_INPUT).contains(email);
|
||||
cy.get(EMAIL_ACTION_SUBJECT_INPUT).should('have.value', subject);
|
||||
};
|
||||
|
||||
export const fillIndexConnectorForm = (connector: IndexConnector = getIndexConnector()) => {
|
||||
cy.get(CONNECTOR_NAME_INPUT).type(connector.name);
|
||||
cy.get(COMBO_BOX_INPUT).type(connector.index);
|
||||
|
||||
cy.get(COMBO_BOX_SELECTION).click({ force: true });
|
||||
|
||||
cy.get(SAVE_ACTION_CONNECTOR_BTN).click();
|
||||
cy.get(SAVE_ACTION_CONNECTOR_BTN).should('not.exist');
|
||||
cy.get(JSON_EDITOR).should('be.visible');
|
||||
cy.get(JSON_EDITOR).click();
|
||||
cy.get(JSON_EDITOR).type(connector.document, {
|
||||
parseSpecialCharSequences: false,
|
||||
});
|
||||
};
|
|
@ -5,8 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { EmailConnector, IndexConnector } from '../objects/connector';
|
||||
import { getIndexConnector, getEmailConnector } from '../objects/connector';
|
||||
import type {
|
||||
CustomRule,
|
||||
MachineLearningRule,
|
||||
|
@ -30,7 +28,6 @@ import {
|
|||
AT_LEAST_ONE_VALID_MATCH,
|
||||
BACK_TO_ALL_RULES_LINK,
|
||||
COMBO_BOX_CLEAR_BTN,
|
||||
COMBO_BOX_INPUT,
|
||||
CREATE_AND_ENABLE_BTN,
|
||||
CUSTOM_QUERY_INPUT,
|
||||
CUSTOM_QUERY_REQUIRED,
|
||||
|
@ -93,13 +90,6 @@ import {
|
|||
THREAT_MATCH_QUERY_INPUT,
|
||||
THRESHOLD_INPUT_AREA,
|
||||
THRESHOLD_TYPE,
|
||||
CONNECTOR_NAME_INPUT,
|
||||
EMAIL_CONNECTOR_FROM_INPUT,
|
||||
EMAIL_CONNECTOR_HOST_INPUT,
|
||||
EMAIL_CONNECTOR_PORT_INPUT,
|
||||
EMAIL_CONNECTOR_USER_INPUT,
|
||||
EMAIL_CONNECTOR_PASSWORD_INPUT,
|
||||
EMAIL_CONNECTOR_SERVICE_SELECTOR,
|
||||
PREVIEW_HISTOGRAM,
|
||||
DATA_VIEW_COMBO_BOX,
|
||||
DATA_VIEW_OPTION,
|
||||
|
@ -108,19 +98,18 @@ import {
|
|||
NEW_TERMS_HISTORY_TIME_TYPE,
|
||||
NEW_TERMS_INPUT_AREA,
|
||||
ACTIONS_THROTTLE_INPUT,
|
||||
} from '../screens/create_new_rule';
|
||||
import {
|
||||
INDEX_SELECTOR,
|
||||
CREATE_CONNECTOR_BTN,
|
||||
SAVE_ACTION_CONNECTOR_BTN,
|
||||
JSON_EDITOR,
|
||||
CREATE_ACTION_CONNECTOR_BTN,
|
||||
EMAIL_ACTION_BTN,
|
||||
COMBO_BOX_SELECTION,
|
||||
} from '../screens/create_new_rule';
|
||||
} from '../screens/common/rule_actions';
|
||||
import { fillIndexConnectorForm, fillEmailConnectorForm } from './common/rule_actions';
|
||||
import { TOAST_ERROR } from '../screens/shared';
|
||||
import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline';
|
||||
import { TIMELINE } from '../screens/timelines';
|
||||
import { refreshPage } from './security_header';
|
||||
import { EUI_FILTER_SELECT_ITEM } from '../screens/common/controls';
|
||||
import { EUI_FILTER_SELECT_ITEM, COMBO_BOX_INPUT } from '../screens/common/controls';
|
||||
|
||||
export const createAndEnableRule = () => {
|
||||
cy.get(CREATE_AND_ENABLE_BTN).click({ force: true });
|
||||
|
@ -327,7 +316,7 @@ export const fillRuleAction = (rule: CustomRule) => {
|
|||
switch (connector.type) {
|
||||
case 'index':
|
||||
cy.get(INDEX_SELECTOR).click();
|
||||
cy.get(CREATE_CONNECTOR_BTN).click();
|
||||
cy.get(CREATE_ACTION_CONNECTOR_BTN).click();
|
||||
fillIndexConnectorForm(connector);
|
||||
break;
|
||||
case 'email':
|
||||
|
@ -487,31 +476,6 @@ export const fillIndexAndIndicatorIndexPattern = (
|
|||
getIndicatorIndicatorIndex().type(`{backspace}{enter}${indicatorIndex}{enter}`);
|
||||
};
|
||||
|
||||
export const fillEmailConnectorForm = (connector: EmailConnector = getEmailConnector()) => {
|
||||
cy.get(CONNECTOR_NAME_INPUT).type(connector.name);
|
||||
cy.get(EMAIL_CONNECTOR_SERVICE_SELECTOR).select(connector.service);
|
||||
cy.get(EMAIL_CONNECTOR_FROM_INPUT).type(connector.from);
|
||||
cy.get(EMAIL_CONNECTOR_HOST_INPUT).type(connector.host);
|
||||
cy.get(EMAIL_CONNECTOR_PORT_INPUT).type(connector.port);
|
||||
cy.get(EMAIL_CONNECTOR_USER_INPUT).type(connector.user);
|
||||
cy.get(EMAIL_CONNECTOR_PASSWORD_INPUT).type(connector.password);
|
||||
};
|
||||
|
||||
export const fillIndexConnectorForm = (connector: IndexConnector = getIndexConnector()) => {
|
||||
cy.get(CONNECTOR_NAME_INPUT).type(connector.name);
|
||||
cy.get(COMBO_BOX_INPUT).type(connector.index);
|
||||
|
||||
cy.get(COMBO_BOX_SELECTION).click({ force: true });
|
||||
|
||||
cy.get(SAVE_ACTION_CONNECTOR_BTN).click();
|
||||
cy.get(SAVE_ACTION_CONNECTOR_BTN).should('not.exist');
|
||||
cy.get(JSON_EDITOR).should('be.visible');
|
||||
cy.get(JSON_EDITOR).click();
|
||||
cy.get(JSON_EDITOR).type(connector.document, {
|
||||
parseSpecialCharSequences: false,
|
||||
});
|
||||
};
|
||||
|
||||
/** Returns the indicator index drop down field. Pass in row number, default is 1 */
|
||||
export const getIndicatorIndexComboField = (row = 1) =>
|
||||
cy.get(THREAT_COMBO_BOX_INPUT).eq(row * 2 - 2);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { BACK_TO_RULE_DETAILS, EDIT_SUBMIT_BUTTON } from '../screens/edit_rule';
|
||||
import { ACTIONS_THROTTLE_INPUT } from '../screens/create_new_rule';
|
||||
|
||||
export const saveEditedRule = () => {
|
||||
cy.get(EDIT_SUBMIT_BUTTON).should('exist').click({ force: true });
|
||||
|
@ -16,3 +17,7 @@ export const goBackToRuleDetails = () => {
|
|||
cy.get(BACK_TO_RULE_DETAILS).should('exist').click();
|
||||
cy.get(BACK_TO_RULE_DETAILS).should('not.exist');
|
||||
};
|
||||
|
||||
export const assertSelectedActionFrequency = (frequency: string) => {
|
||||
cy.get(ACTIONS_THROTTLE_INPUT).find('option:selected').should('have.text', frequency);
|
||||
};
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
DETAILS_DESCRIPTION,
|
||||
EXCEPTION_ITEM_ACTIONS_BUTTON,
|
||||
EDIT_EXCEPTION_BTN,
|
||||
EDIT_RULE_SETTINGS_LINK,
|
||||
} from '../screens/rule_details';
|
||||
import { addsFields, closeFieldsBrowser, filterFieldsBrowser } from './fields_browser';
|
||||
|
||||
|
@ -166,3 +167,7 @@ export const hasIndexPatterns = (indexPatterns: string) => {
|
|||
getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns);
|
||||
});
|
||||
};
|
||||
|
||||
export const goToRuleEditSettings = () => {
|
||||
cy.get(EDIT_RULE_SETTINGS_LINK).click();
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
import {
|
||||
INDEX_PATTERNS_RULE_BULK_MENU_ITEM,
|
||||
ADD_INDEX_PATTERNS_RULE_BULK_MENU_ITEM,
|
||||
ADD_RULE_ACTIONS_MENU_ITEM,
|
||||
DELETE_INDEX_PATTERNS_RULE_BULK_MENU_ITEM,
|
||||
TAGS_RULE_BULK_MENU_ITEM,
|
||||
ADD_TAGS_RULE_BULK_MENU_ITEM,
|
||||
|
@ -37,6 +38,8 @@ import {
|
|||
UPDATE_SCHEDULE_TIME_UNIT_SELECT,
|
||||
UPDATE_SCHEDULE_LOOKBACK_INPUT,
|
||||
RULES_BULK_EDIT_SCHEDULES_WARNING,
|
||||
RULES_BULK_EDIT_OVERWRITE_ACTIONS_CHECKBOX,
|
||||
RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT,
|
||||
} from '../screens/rules_bulk_edit';
|
||||
import { SCHEDULE_DETAILS } from '../screens/rule_details';
|
||||
|
||||
|
@ -81,6 +84,13 @@ export const openBulkEditAddIndexPatternsForm = () => {
|
|||
cy.get(RULES_BULK_EDIT_FORM_TITLE).should('have.text', 'Add index patterns');
|
||||
};
|
||||
|
||||
export const openBulkEditRuleActionsForm = () => {
|
||||
cy.get(BULK_ACTIONS_BTN).click();
|
||||
cy.get(ADD_RULE_ACTIONS_MENU_ITEM).click();
|
||||
|
||||
cy.get(RULES_BULK_EDIT_FORM_TITLE).should('have.text', 'Add rule actions');
|
||||
};
|
||||
|
||||
export const openBulkEditDeleteIndexPatternsForm = () => {
|
||||
cy.get(BULK_ACTIONS_BTN).click();
|
||||
cy.get(INDEX_PATTERNS_RULE_BULK_MENU_ITEM).click();
|
||||
|
@ -160,6 +170,14 @@ export const checkOverwriteIndexPatternsCheckbox = () => {
|
|||
.should('be.checked');
|
||||
};
|
||||
|
||||
export const checkOverwriteRuleActionsCheckbox = () => {
|
||||
cy.get(RULES_BULK_EDIT_OVERWRITE_ACTIONS_CHECKBOX)
|
||||
.should('have.text', 'Overwrite all selected rules actions')
|
||||
.find('input')
|
||||
.click({ force: true })
|
||||
.should('be.checked');
|
||||
};
|
||||
|
||||
export const checkOverwriteDataViewCheckbox = () => {
|
||||
cy.get(RULES_BULK_EDIT_OVERWRITE_DATA_VIEW_CHECKBOX)
|
||||
.should('have.text', 'Apply changes to rules configured with data views')
|
||||
|
@ -194,6 +212,7 @@ export const typeScheduleInterval = (interval: string) => {
|
|||
.type(interval.toString())
|
||||
.blur();
|
||||
};
|
||||
|
||||
export const typeScheduleLookback = (lookback: string) => {
|
||||
cy.get(UPDATE_SCHEDULE_LOOKBACK_INPUT)
|
||||
.find('input')
|
||||
|
@ -245,3 +264,7 @@ export const assertRuleScheduleValues = ({ interval, lookback }: RuleSchedule) =
|
|||
cy.get('dd').eq(1).should('contain.text', lookback);
|
||||
});
|
||||
};
|
||||
|
||||
export const pickActionFrequency = (frequency: string) => {
|
||||
cy.get(RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT).select(frequency);
|
||||
};
|
||||
|
|
|
@ -122,7 +122,7 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction
|
|||
const throttleFieldComponentProps = useMemo(
|
||||
() => ({
|
||||
idAria: 'bulkEditRulesRuleActionThrottle',
|
||||
dataTestSubj: 'bulkEditRulesRuleActionThrottle',
|
||||
'data-test-subj': 'bulkEditRulesRuleActionThrottle',
|
||||
hasNoInitialSelection: false,
|
||||
euiFieldProps: {
|
||||
options: THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS,
|
||||
|
|
|
@ -364,9 +364,9 @@ export const useBulkActions = ({
|
|||
key: i18n.BULK_ACTION_ADD_RULE_ACTIONS,
|
||||
name: i18n.BULK_ACTION_ADD_RULE_ACTIONS,
|
||||
'data-test-subj': 'addRuleActionsBulk',
|
||||
disabled: isEditDisabled,
|
||||
disabled: !hasActionsPrivileges || isEditDisabled,
|
||||
onClick: handleBulkEdit(BulkActionEditType.add_rule_actions),
|
||||
toolTipContent: missingActionPrivileges ? i18n.EDIT_RULE_SETTINGS_TOOLTIP : undefined,
|
||||
toolTipContent: !hasActionsPrivileges ? i18n.EDIT_RULE_SETTINGS_TOOLTIP : undefined,
|
||||
toolTipPosition: 'right',
|
||||
icon: undefined,
|
||||
},
|
||||
|
|
|
@ -616,6 +616,7 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
|
|||
}
|
||||
return (
|
||||
<LinkButton
|
||||
data-test-subj="editRuleSettingsLink"
|
||||
onClick={goToEditRule}
|
||||
iconType="controlsHorizontal"
|
||||
isDisabled={!isExistingRule || !userHasPermissions(canUserCRUD)}
|
||||
|
|
|
@ -34,6 +34,7 @@ import {
|
|||
installPrePackagedRules,
|
||||
getSimpleMlRule,
|
||||
getWebHookAction,
|
||||
getSlackAction,
|
||||
} from '../../utils';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
@ -63,14 +64,12 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
const fetchRuleByAlertApi = (ruleId: string) =>
|
||||
supertest.get(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'true');
|
||||
|
||||
const createWebHookAction = async () =>
|
||||
(
|
||||
await supertest
|
||||
.post('/api/actions/action')
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(getWebHookAction())
|
||||
.expect(200)
|
||||
).body;
|
||||
const createConnector = async (payload: Record<string, unknown>) =>
|
||||
(await supertest.post('/api/actions/action').set('kbn-xsrf', 'true').send(payload).expect(200))
|
||||
.body;
|
||||
|
||||
const createWebHookConnector = () => createConnector(getWebHookAction());
|
||||
const createSlackConnector = () => createConnector(getSlackAction());
|
||||
|
||||
describe('perform_bulk_action', () => {
|
||||
beforeEach(async () => {
|
||||
|
@ -1092,17 +1091,17 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
const webHookActionMock = {
|
||||
group: 'default',
|
||||
params: {
|
||||
body: '{}',
|
||||
body: '{"test":"action to be saved in a rule"}',
|
||||
},
|
||||
};
|
||||
|
||||
describe('set_rule_actions', () => {
|
||||
it('should set action correctly', async () => {
|
||||
it('should set action correctly to existing empty actions list', async () => {
|
||||
const ruleId = 'ruleId';
|
||||
const createdRule = await createRule(supertest, log, getSimpleRule(ruleId));
|
||||
|
||||
// create a new action
|
||||
const hookAction = await createWebHookAction();
|
||||
// create a new connector
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const { body } = await postBulkAction()
|
||||
.send({
|
||||
|
@ -1116,7 +1115,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
actions: [
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1125,33 +1124,85 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
})
|
||||
.expect(200);
|
||||
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].actions).to.eql([
|
||||
const expectedRuleActions = [
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].actions).to.eql(expectedRuleActions);
|
||||
|
||||
// Check that the updates have been persisted
|
||||
const { body: readRule } = await fetchRule(ruleId).expect(200);
|
||||
|
||||
expect(readRule.actions).to.eql([
|
||||
expect(readRule.actions).to.eql(expectedRuleActions);
|
||||
});
|
||||
|
||||
it('should set action correctly to existing non empty actions list', async () => {
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const existingRuleAction = {
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
group: 'default',
|
||||
params: {
|
||||
body: '{"test":"an existing action"}',
|
||||
},
|
||||
};
|
||||
|
||||
const ruleId = 'ruleId';
|
||||
const createdRule = await createRule(supertest, log, {
|
||||
...getSimpleRule(ruleId),
|
||||
actions: [existingRuleAction],
|
||||
});
|
||||
|
||||
const { body } = await postBulkAction()
|
||||
.send({
|
||||
ids: [createdRule.id],
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [
|
||||
{
|
||||
type: BulkActionEditType.set_rule_actions,
|
||||
value: {
|
||||
throttle: '1h',
|
||||
actions: [
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: webHookConnector.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const expectedRuleActions = [
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].actions).to.eql(expectedRuleActions);
|
||||
|
||||
// Check that the updates have been persisted
|
||||
const { body: readRule } = await fetchRule(ruleId).expect(200);
|
||||
|
||||
expect(readRule.actions).to.eql(expectedRuleActions);
|
||||
});
|
||||
|
||||
it('should set actions to empty list, actions payload is empty list', async () => {
|
||||
// create a new action
|
||||
const hookAction = await createWebHookAction();
|
||||
// create a new connector
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const defaultRuleAction = {
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
group: 'default',
|
||||
params: {
|
||||
|
@ -1197,8 +1248,8 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
const ruleId = 'ruleId';
|
||||
const createdRule = await createRule(supertest, log, getSimpleRule(ruleId));
|
||||
|
||||
// create a new action
|
||||
const hookAction = await createWebHookAction();
|
||||
// create a new connector
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const { body } = await postBulkAction()
|
||||
.send({
|
||||
|
@ -1212,7 +1263,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
actions: [
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1221,33 +1272,29 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
})
|
||||
.expect(200);
|
||||
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].actions).to.eql([
|
||||
const expectedRuleActions = [
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].actions).to.eql(expectedRuleActions);
|
||||
|
||||
// Check that the updates have been persisted
|
||||
const { body: readRule } = await fetchRule(ruleId).expect(200);
|
||||
|
||||
expect(readRule.actions).to.eql([
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
action_type_id: '.webhook',
|
||||
},
|
||||
]);
|
||||
expect(readRule.actions).to.eql(expectedRuleActions);
|
||||
});
|
||||
|
||||
it('should add action correctly to non empty actions list', async () => {
|
||||
// create a new action
|
||||
const hookAction = await createWebHookAction();
|
||||
it('should add action correctly to non empty actions list of the same type', async () => {
|
||||
// create a new connector
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const defaultRuleAction = {
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
group: 'default',
|
||||
params: {
|
||||
|
@ -1274,7 +1321,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
actions: [
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1283,35 +1330,97 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
})
|
||||
.expect(200);
|
||||
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].actions).to.eql([
|
||||
const expectedRuleActions = [
|
||||
defaultRuleAction,
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].actions).to.eql(expectedRuleActions);
|
||||
|
||||
// Check that the updates have been persisted
|
||||
const { body: readRule } = await fetchRule(ruleId).expect(200);
|
||||
|
||||
expect(readRule.actions).to.eql([
|
||||
expect(readRule.actions).to.eql(expectedRuleActions);
|
||||
});
|
||||
|
||||
it('should add action correctly to non empty actions list of a different type', async () => {
|
||||
// create new actions
|
||||
const webHookAction = await createWebHookConnector();
|
||||
const slackConnector = await createSlackConnector();
|
||||
|
||||
const defaultRuleAction = {
|
||||
id: webHookAction.id,
|
||||
action_type_id: '.webhook',
|
||||
group: 'default',
|
||||
params: {
|
||||
body: '{"test":"a default action"}',
|
||||
},
|
||||
};
|
||||
|
||||
const slackConnectorMockProps = {
|
||||
group: 'default',
|
||||
params: {
|
||||
message: 'test slack message',
|
||||
},
|
||||
};
|
||||
|
||||
const ruleId = 'ruleId';
|
||||
const createdRule = await createRule(supertest, log, {
|
||||
...getSimpleRule(ruleId),
|
||||
actions: [defaultRuleAction],
|
||||
throttle: '1d',
|
||||
});
|
||||
|
||||
const { body } = await postBulkAction()
|
||||
.send({
|
||||
ids: [createdRule.id],
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [
|
||||
{
|
||||
type: BulkActionEditType.add_rule_actions,
|
||||
value: {
|
||||
throttle: '1h',
|
||||
actions: [
|
||||
{
|
||||
...slackConnectorMockProps,
|
||||
id: slackConnector.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const expectedRuleActions = [
|
||||
defaultRuleAction,
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
action_type_id: '.webhook',
|
||||
...slackConnectorMockProps,
|
||||
id: slackConnector.id,
|
||||
action_type_id: '.slack',
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].actions).to.eql(expectedRuleActions);
|
||||
|
||||
// Check that the updates have been persisted
|
||||
const { body: readRule } = await fetchRule(ruleId).expect(200);
|
||||
|
||||
expect(readRule.actions).to.eql(expectedRuleActions);
|
||||
});
|
||||
|
||||
it('should not change actions of rule if empty list of actions added', async () => {
|
||||
// create a new action
|
||||
const hookAction = await createWebHookAction();
|
||||
// create a new connector
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const defaultRuleAction = {
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
group: 'default',
|
||||
params: {
|
||||
|
@ -1352,11 +1461,11 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
|
||||
it('should change throttle if actions list in payload is empty', async () => {
|
||||
// create a new action
|
||||
const hookAction = await createWebHookAction();
|
||||
// create a new connector
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const defaultRuleAction = {
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
group: 'default',
|
||||
params: {
|
||||
|
@ -1410,7 +1519,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
it(`should apply "${type}" rule action to prebuilt rule`, async () => {
|
||||
await installPrePackagedRules(supertest, log);
|
||||
const prebuiltRule = await fetchPrebuiltRule();
|
||||
const hookAction = await createWebHookAction();
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const { body } = await postBulkAction()
|
||||
.send({
|
||||
|
@ -1424,7 +1533,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
actions: [
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1438,7 +1547,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
expect(editedRule.actions).to.eql([
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
},
|
||||
]);
|
||||
|
@ -1451,7 +1560,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
expect(readRule.actions).to.eql([
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
action_type_id: '.webhook',
|
||||
},
|
||||
]);
|
||||
|
@ -1464,7 +1573,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
it(`should return error if one of edit action is not eligible for prebuilt rule`, async () => {
|
||||
await installPrePackagedRules(supertest, log);
|
||||
const prebuiltRule = await fetchPrebuiltRule();
|
||||
const hookAction = await createWebHookAction();
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const { body } = await postBulkAction()
|
||||
.send({
|
||||
|
@ -1478,7 +1587,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
actions: [
|
||||
{
|
||||
...webHookActionMock,
|
||||
id: hookAction.id,
|
||||
id: webHookConnector.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1568,46 +1677,58 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
payloadThrottle: '1h',
|
||||
expectedThrottle: '1h',
|
||||
},
|
||||
{
|
||||
payloadThrottle: '1d',
|
||||
expectedThrottle: '1d',
|
||||
},
|
||||
{
|
||||
payloadThrottle: '7d',
|
||||
expectedThrottle: '7d',
|
||||
},
|
||||
];
|
||||
casesForNonEmptyActions.forEach(({ payloadThrottle, expectedThrottle }) => {
|
||||
it(`throttle is set correctly, if payload throttle="${payloadThrottle}" and actions non empty`, async () => {
|
||||
// create a new action
|
||||
const hookAction = await createWebHookAction();
|
||||
[BulkActionEditType.set_rule_actions, BulkActionEditType.add_rule_actions].forEach(
|
||||
(ruleAction) => {
|
||||
casesForNonEmptyActions.forEach(({ payloadThrottle, expectedThrottle }) => {
|
||||
it(`throttle is updated correctly for rule action "${ruleAction}", if payload throttle="${payloadThrottle}" and actions non empty`, async () => {
|
||||
// create a new connector
|
||||
const webHookConnector = await createWebHookConnector();
|
||||
|
||||
const ruleId = 'ruleId';
|
||||
const createdRule = await createRule(supertest, log, getSimpleRule(ruleId));
|
||||
const ruleId = 'ruleId';
|
||||
const createdRule = await createRule(supertest, log, getSimpleRule(ruleId));
|
||||
|
||||
const { body } = await postBulkAction()
|
||||
.send({
|
||||
ids: [createdRule.id],
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [
|
||||
{
|
||||
type: BulkActionEditType.set_rule_actions,
|
||||
value: {
|
||||
throttle: payloadThrottle,
|
||||
actions: [
|
||||
{
|
||||
id: hookAction.id,
|
||||
group: 'default',
|
||||
params: { body: '{}' },
|
||||
const { body } = await postBulkAction()
|
||||
.send({
|
||||
ids: [createdRule.id],
|
||||
action: BulkAction.edit,
|
||||
[BulkAction.edit]: [
|
||||
{
|
||||
type: BulkActionEditType.set_rule_actions,
|
||||
value: {
|
||||
throttle: payloadThrottle,
|
||||
actions: [
|
||||
{
|
||||
id: webHookConnector.id,
|
||||
group: 'default',
|
||||
params: { body: '{}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].throttle).to.eql(expectedThrottle);
|
||||
// Check that the updated rule is returned with the response
|
||||
expect(body.attributes.results.updated[0].throttle).to.eql(expectedThrottle);
|
||||
|
||||
// Check that the updates have been persisted
|
||||
const { body: rule } = await fetchRule(ruleId).expect(200);
|
||||
// Check that the updates have been persisted
|
||||
const { body: rule } = await fetchRule(ruleId).expect(200);
|
||||
|
||||
expect(rule.throttle).to.eql(expectedThrottle);
|
||||
});
|
||||
});
|
||||
expect(rule.throttle).to.eql(expectedThrottle);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('notifyWhen', () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue