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

feat(RuleTicket): add new action 'replace' for actors #15718

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/CommonITILObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -8819,6 +8819,8 @@ protected function updateActors(bool $disable_notifications = false)
? sprintf('_additional_%ss', $actor_type)
: sprintf('_additional_%ss_%ss', strtolower($actor_itemtype), $actor_type);

$actors_id_replace_input_key = sprintf('_replace_%ss_%ss', strtolower($actor_itemtype), $actor_type);

$get_unique_key = function (array $actor) use ($actors_id_input_key): string {
// Use alternative_email in value key for "email" actors
return sprintf('%s_%s', $actors_id_input_key, $actor['items_id'] ?: $actor['alternative_email'] ?? '');
Expand Down Expand Up @@ -8967,6 +8969,40 @@ protected function updateActors(bool $disable_notifications = false)
}
}
}


if (array_key_exists($actors_id_replace_input_key, $this->input)) {
// when input key used is _replace_$actor_$actor_type(provided by rule engine)
// Ex: _replace_groups_assign or _replace_users_assign or _replace_suppliers_assign
// Ex: _replace_groups_observer or _replace_users_observer or _replace_suppliers_observer
// Ex: _replace_groups_requester or _replace_users_requester or _replace_suppliers_requester
// this indicates a complete replacement of actors of the same type

// remove all related actors
$actors_deleted_input_key = sprintf('_%s_%s_deleted', $actor_fkey, $actor_type);
$existing_actors = $this->{str_replace("_id", "", $actor_fkey)}; // `$this->users` or `$this->groups` or `$this->suppliers`
foreach ($existing_actors[$actor_type_value] as $actor_value) {
$this->input[$actors_deleted_input_key][] = [
"itemtype" => $actor_itemtype,
"id" => $actor_value["id"],
];
}


// add actor defined by rule
foreach ($this->input[$actors_id_replace_input_key] as $actor_id) {
$actor_id = (int)$actor_id;
$actor = [
'itemtype' => $actor_itemtype,
'items_id' => $actor_id,
'type' => $actor_type_value,
];
$unique_key = $get_unique_key($actor);
if (!array_key_exists($unique_key, $actors)) {
$actors[$unique_key] = $actor;
}
}
}
}

// Search for added/updated actors
Expand Down
1 change: 1 addition & 0 deletions src/RuleAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ public static function getActions()

return ['assign' => __('Assign'),
'append' => __('Add'),
'replace' => __('Replace by'),
'regex_result' => __('Assign the value from regular expression'),
'append_regex_result' => __('Add the result of regular expression'),
'affectbyip' => __('Assign: equipment by IP address'),
Expand Down
27 changes: 19 additions & 8 deletions src/RuleCommonITILObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ public function executeActions($output, $params, array $input = [])
break;
}
break;

case "replace":
$actions = $this->getActions();
$output[$actions[$action->fields["field"]]["replaceto"]][] = $action->fields["value"];
break;
case "assign":
$output[$action->fields["field"]] = $action->fields["value"];

Expand Down Expand Up @@ -756,19 +759,21 @@ public function getActions()

$actions['_users_id_requester']['name'] = _n('Requester', 'Requesters', 1);
$actions['_users_id_requester']['type'] = 'dropdown_users';
$actions['_users_id_requester']['force_actions'] = ['assign', 'append'];
$actions['_users_id_requester']['force_actions'] = ['assign', 'append', 'replace'];
$actions['_users_id_requester']['permitseveral'] = ['append'];
$actions['_users_id_requester']['appendto'] = '_additional_requesters';
$actions['_users_id_requester']['replaceto'] = '_replace_users_requesters';
$actions['_users_id_requester']['appendtoarray'] = ['use_notification' => 1];
$actions['_users_id_requester']['appendtoarrayfield'] = 'users_id';

$actions['_groups_id_requester']['name'] = _n('Requester group', 'Requester groups', 1);
$actions['_groups_id_requester']['type'] = 'dropdown';
$actions['_groups_id_requester']['table'] = 'glpi_groups';
$actions['_groups_id_requester']['condition'] = ['is_requester' => 1];
$actions['_groups_id_requester']['force_actions'] = ['assign', 'append', 'fromitem', 'defaultfromuser','regex_result'];
$actions['_groups_id_requester']['force_actions'] = ['assign', 'append', 'replace', 'fromitem', 'defaultfromuser','regex_result'];
$actions['_groups_id_requester']['permitseveral'] = ['append'];
$actions['_groups_id_requester']['appendto'] = '_additional_groups_requesters';
$actions['_groups_id_requester']['replaceto'] = '_replace_groups_requesters';

$actions['_groups_id_requester_by_completename']['name'] = sprintf(__('%1$s (%2$s)'), _n('Requester group', 'Requester groups', 1), __('by completename'));
$actions['_groups_id_requester_by_completename']['type'] = 'dropdown';
Expand All @@ -780,19 +785,21 @@ public function getActions()

$actions['_users_id_assign']['name'] = __('Technician');
$actions['_users_id_assign']['type'] = 'dropdown_assign';
$actions['_users_id_assign']['force_actions'] = ['assign', 'append'];
$actions['_users_id_assign']['force_actions'] = ['assign', 'append', 'replace'];
$actions['_users_id_assign']['permitseveral'] = ['append'];
$actions['_users_id_assign']['appendto'] = '_additional_assigns';
$actions['_users_id_assign']['replaceto'] = '_replace_users_assigns';
$actions['_users_id_assign']['appendtoarray'] = ['use_notification' => 1];
$actions['_users_id_assign']['appendtoarrayfield'] = 'users_id';

$actions['_groups_id_assign']['table'] = 'glpi_groups';
$actions['_groups_id_assign']['name'] = __('Technician group');
$actions['_groups_id_assign']['type'] = 'dropdown';
$actions['_groups_id_assign']['condition'] = ['is_assign' => 1];
$actions['_groups_id_assign']['force_actions'] = ['assign', 'append', 'regex_result'];
$actions['_groups_id_assign']['force_actions'] = ['assign', 'append', 'regex_result', 'replace'];
$actions['_groups_id_assign']['permitseveral'] = ['append'];
$actions['_groups_id_assign']['appendto'] = '_additional_groups_assigns';
$actions['_groups_id_assign']['replaceto'] = '_replace_groups_assigns';

$actions['_groups_id_assign_by_completename']['table'] = 'glpi_groups';
$actions['_groups_id_assign_by_completename']['name'] = sprintf(__('%1$s (%2$s)'), __('Technician group'), __('by completename'));
Expand All @@ -805,27 +812,31 @@ public function getActions()
$actions['_suppliers_id_assign']['table'] = 'glpi_suppliers';
$actions['_suppliers_id_assign']['name'] = __('Assigned to a supplier');
$actions['_suppliers_id_assign']['type'] = 'dropdown';
$actions['_suppliers_id_assign']['force_actions'] = ['assign', 'append'];
$actions['_suppliers_id_assign']['force_actions'] = ['assign', 'append', 'replace'];
$actions['_suppliers_id_assign']['permitseveral'] = ['append'];
$actions['_suppliers_id_assign']['appendto'] = '_additional_suppliers_assigns';
$actions['_suppliers_id_assign']['replaceto'] = '_replace_suppliers_assigns';
$actions['_suppliers_id_assign']['appendtoarray'] = ['use_notification' => 1];
$actions['_suppliers_id_assign']['appendtoarrayfield'] = 'suppliers_id';

$actions['_users_id_observer']['name'] = _n('Observer', 'Observers', 1);
$actions['_users_id_observer']['type'] = 'dropdown_users';
$actions['_users_id_observer']['force_actions'] = ['assign', 'append'];
$actions['_users_id_observer']['force_actions'] = ['assign', 'append', 'replace'];
$actions['_users_id_observer']['permitseveral'] = ['append'];
$actions['_users_id_observer']['appendto'] = '_additional_observers';
$actions['_users_id_observer']['replaceto'] = '_replace_users_observers';
$actions['_users_id_observer']['appendtoarray'] = ['use_notification' => 1];
$actions['_users_id_observer']['appendtoarrayfield'] = 'users_id';

$actions['_groups_id_observer']['table'] = 'glpi_groups';
$actions['_groups_id_observer']['name'] = _n('Observer group', 'Observer groups', 1);
$actions['_groups_id_observer']['type'] = 'dropdown';
$actions['_groups_id_observer']['condition'] = ['is_watcher' => 1];
$actions['_groups_id_observer']['force_actions'] = ['assign', 'append', 'regex_result'];
$actions['_groups_id_observer']['force_actions'] = ['assign', 'append', 'regex_result', 'replace'];
$actions['_groups_id_observer']['permitseveral'] = ['append'];
$actions['_groups_id_observer']['appendto'] = '_additional_groups_observers';
$actions['_groups_id_observer']['replaceto'] = '_replace_groups_observers';


$actions['_groups_id_observer_by_completename']['table'] = 'glpi_groups';
$actions['_groups_id_observer_by_completename']['name'] = sprintf(__('%1$s (%2$s)'), _n('Watcher group', 'Watcher groups', 1), __('by completename'));
Expand Down
114 changes: 114 additions & 0 deletions tests/functional/RuleTicket.php
Original file line number Diff line number Diff line change
Expand Up @@ -1169,4 +1169,118 @@ public function testAssignLocationFromUser(
$ticket->getFromDB($ticket->getID());
$this->integer($ticket->fields['locations_id'])->isEqualTo($expected_location_after_creation);
}

public function testAssignGroupOnUpdate()
Copy link
Member

Choose a reason for hiding this comment

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

Test is not enough complete.

  1. It should ensure that action like replace users assigned will have no impact on requesters and observers, nor on groups or suppliers.
  2. It should verify that replacement also removes actors in existing input (not only existing actors).
  3. It should verify that, when there is no existing actors, it does not fail.
  4. It should verify the ONADD case too.

You should probably have a data provider that is used to test multiple combinations.

{
$this->login();

//create new group1
$group1 = new \Group();
$group_id1 = $group1->add($group_input1 = [
"name" => "group1",
"is_assign" => true
]);
$this->checkInput($group1, $group_id1, $group_input1);

//create new group2
$group2 = new \Group();
$group_id2 = $group2->add($group_input2 = [
"name" => "group2",
"is_assign" => true
]);
$this->checkInput($group2, $group_id2, $group_input2);

//create new itilcategory
$cat = new \ITILCategory();
$cat_id = $cat->add($cat_input = [
"name" => "category",
]);
$this->checkInput($cat, $cat_id, $cat_input);

// Create rule
$ruleticket = new \RuleTicket();
$rulecrit = new \RuleCriteria();
$ruleaction = new \RuleAction();

$ruletid = $ruleticket->add($ruletinput = [
'name' => 'test assign group on update',
'match' => 'AND',
'is_active' => 1,
'sub_type' => 'RuleTicket',
'condition' => \RuleTicket::ONUPDATE,
'is_recursive' => 1,
]);
$this->checkInput($ruleticket, $ruletid, $ruletinput);

//create criteria to check
$crit_id = $rulecrit->add($crit_input = [
'rules_id' => $ruletid,
'criteria' => 'itilcategories_id',
'condition' => \Rule::PATTERN_IS,
'pattern' => $cat_id,
]);
$this->checkInput($rulecrit, $crit_id, $crit_input);

//create action to add group as group requester
$action_id = $ruleaction->add($action_input = [
'rules_id' => $ruletid,
'action_type' => 'replace',
'field' => '_groups_id_assign',
'value' => $group_id2,
]);
$this->checkInput($ruleaction, $action_id, $action_input);

// Create ticket
$ticket = new \Ticket();
$tickets_id = $ticket->add($ticket_input = [
'name' => 'when assigning delete groups to add',
'content' => 'test',
]);
$this->checkInput($ticket, $tickets_id, $ticket_input);

//add group1 to ticket
$ticketGroup = new \Group_Ticket();
$ticketGroup->add([
'tickets_id' => $tickets_id,
'groups_id' => $group_id1,
'type' => \CommonITILActor::ASSIGN
]);

//load TicketGroup1 (expected true)
$ticketGroup = new \Group_Ticket();
$this->boolean(
$ticketGroup->getFromDBByCrit([
'tickets_id' => $tickets_id,
'groups_id' => $group_id1,
'type' => \CommonITILActor::ASSIGN
])
)->isTrue();

//update ticket and set cat
$ticket->update([
'id' => $tickets_id,
'itilcategories_id' => $cat_id,
]);
$this->checkInput($ticket, $tickets_id, $ticket_input);

//load TicketGroup1 (expected false)
$ticketGroup = new \Group_Ticket();
$this->boolean(
$ticketGroup->getFromDBByCrit([
'tickets_id' => $tickets_id,
'groups_id' => $group_id1,
'type' => \CommonITILActor::ASSIGN
])
)->isFalse();

//load TicketGroup2 (expected true)
$ticketGroup = new \Group_Ticket();
$this->boolean(
$ticketGroup->getFromDBByCrit([
'tickets_id' => $tickets_id,
'groups_id' => $group_id2,
'type' => \CommonITILActor::ASSIGN
])
)->isTrue();
}
}