From 809380afb20168f569812fa403d6c09064653ecf Mon Sep 17 00:00:00 2001 From: DonutsNL Date: Fri, 5 Jan 2024 23:10:39 +0000 Subject: [PATCH] ... --- src/FilterPattern.php | 28 ++++++- src/TicketHandler.php | 152 ++++++++++++++++++++++------------- tests/TicketFilterTest.php | 6 +- tests/UserValidationTest.php | 32 ++++++++ tests/createTicketViaAPI.php | 86 ++++++++++++++++++++ 5 files changed, 242 insertions(+), 62 deletions(-) create mode 100644 tests/UserValidationTest.php create mode 100644 tests/createTicketViaAPI.php diff --git a/src/FilterPattern.php b/src/FilterPattern.php index c500378..397062e 100644 --- a/src/FilterPattern.php +++ b/src/FilterPattern.php @@ -252,7 +252,8 @@ public function rawSearchOptions() : array return $tab; } - + + /** * getFilterPatterns() : array - * Get match patterns and config from dropdowns @@ -362,4 +363,29 @@ public static function uninstall(Migration $migration) : void $migration->displayMessage("Uninstalling $table"); $migration->dropTable($table); } + + /** + * Return dummy filterPattern for testing purposes + * + * @return array + */ + public static function getDummyPattern() : array + { + return [self::NAME => TestPattern, + self::ACTIVE => true, + self::DATE_CREATION => null, + self::DATE_MOD => null, + self::TICKETMATCHSTR => '/.*?(?\(TESTMATCH-[0-9]{1,4}\)).*/', + self::TICKETMATCHSTRLEN => 16, + self::ASSETMATCHSTR => '/.*?(?\(TESTASSET-[0-9]{1,4}\)).*/', + self::ASSETMATCHSTRLEN => 16, + self::SOLVEDMATCHSTR => '/.*?(?\(TESTSOLVED-[0-9]{1,4}\)).*/', + self::SOLVEDMATCHSTRLEN => 18, + self::AUTOMERGE => true, + self::REOPENCLOSED => true, + self::SEARCHBODY => true, + self::MATCHSOURCE => true, + self::SUPPRESNOTIF => true]; + } + } diff --git a/src/TicketHandler.php b/src/TicketHandler.php index 4f5be43..fb95fd1 100644 --- a/src/TicketHandler.php +++ b/src/TicketHandler.php @@ -48,25 +48,22 @@ class TicketHandler{ private $ticket; // The referenced ticket object private $pattern = false; // The pattern configuration - private $status = '-1'; // Status of this object, 1 if reference ticket is loaded. - - // Not used - public function __construct() {} + private $status = false; // Status of this object /** * initHandler(int ticketId, array filterPattern) : bool - * Loads a ticket from the database with provided ticket ID * and populates used pattern config for various evals. * - * @param int ticketId identity of the ticket that needs to be loaded + * @param int|Ticket ticket Either an testObject or ticket Id of the referenced ticket that needs to be loaded * @param array pattern array holding the pattern used for the match. * @return void Object is passed by reference, no return values required * @since 1.1.0 */ - public function initHandler(int $ticketId, array $filterPattern) : bool + public function initHandler(int|Ticket $ticket, array $filterPattern) : bool { // Load the ticket we need to modify. - if(is_int($ticketId)){ + if(is_int($ticket)){ if(is_array($filterPattern)){ $this->pattern = $filterPattern; } else { @@ -74,14 +71,22 @@ public function initHandler(int $ticketId, array $filterPattern) : bool } $this->ticket = new Ticket(); - $this->ticket->getFromDB((integer) $ticketId); + $this->ticket->getFromDB((integer) $ticket); // Check if we where able to fetch the correct ticket. - if($this->ticket->fields['id'] == $ticketId){ - $this->status = '1'; + if($this->ticket->fields['id'] == $ticket){ + $this->status = true; return true; } else { return false; } + } else { + // Load ticket directly for testing purposes. + // Minimal validation to allow negative assertions + if(is_object($ticket)) { + $this->ticket = $ticket; + $this->pattern = $filterPattern; + $this->status = true; + } } } @@ -226,56 +231,14 @@ public function addSolvedMessage($patternName = '') : bool */ public function processTicket(Ticket $item) : bool { - // Match mailsender with ticket requester. - if($this->pattern[FilterPattern::MATCHSOURCE]) { - // https://github.com/DonutsNL/ticketfilter/issues/4 - $succesfullUserMatch = false; - - // Get all the users from the ticket Object received from the pre_item_add hook. - foreach($item->input['_actors']["requester"] as $null => $mailUser){ - $usersToBeMatched[] = [ - 'userId' => $mailUser['items_id'], - 'userEmail' => ($mailUser['default_email']) ? $mailUser['default_email'] : $mailUser['alternative_email'] - ]; - } - - // Fetch and match the requesters of the currently processed ticket. - // With the requesters in the ticket object received from the pre_item_add hook. - $usrObj = new $this->ticket->userlinkclass(); - $actors = $usrObj->getActors($this->getId()); - foreach ( $actors as $null => $ticketUsers) { - // Verify there are users assigned to process - if(is_array($ticketUsers) && count($ticketUsers) > 0) { - foreach($ticketUsers as $null => $userType) { - // Only evaluate user type requesters ignore watchers and technicians - if($userType['type'] == CommonITILActor::REQUESTER){ - // First search alternative email because we dont want - // to match an users_id:int(0) that would match with all anonymous users - // assigned to the ticket. - if($userType['alternative_email']) { - if(array_search(($userType['alternative_email']), array_column($usersToBeMatched, 'userEmail'))){ - $succesfullUserMatch = true; - } - } else { - // Try to match any non zero users_id in usersToBeMatched. - if(array_search($userType['users_id'], array_column($usersToBeMatched, 'userId'))){ - $succesfullUserMatch = true; - } - } - } // Ignore the user that is either watcher or technician @see glpi/src/Ticket_User.php - } // foreach - }// No requesters assigned to ticket - }// foreach + // Process the followup. + if($this->status == '1') { - // We did not match any user from the email with the referenced ticket - // Per configuration do not merge the followup. - if(!$succesfullUserMatch){ + // Evaluate the requesters of both tickets + if (!$this->verifyRequesters($item)) { return false; } - } - // Process the followup. - if($this->status == '1') { // Do we need to reopen the closed ticket? if($this->getStatus() == CommonITILObject::CLOSED) { if($this->pattern[FilterPattern::REOPENCLOSED]) { @@ -331,15 +294,23 @@ public function processTicket(Ticket $item) : bool $this->addSolvedMessage($this->pattern[FilterPattern::NAME]); // Set status to solved. $this->setStatusToSolved(); + print "Ticket updated to solved!
"; Session::addMessageAfterRedirect(__("New ticket was solved by the plugin!"), true, INFO); } else { + print "Solved Patern length issue
"; trigger_error('TicketFilter: Length of'.$matchArray['solved']['0'].' is longer then allowed by configured Ticket Match String Length', E_USER_WARNING); } - } // Solved pattern not found + } else {// Solved pattern not found + print "Patern not found
"; + } } else { + print "Pregmatch failed
"; trigger_error("TicketFilter: PregMatch failed! please review the Solved pattern $p and correct it", E_USER_WARNING); } + } else{ + print "No solved string found!"; } + die(); return true; } else { return false; @@ -392,4 +363,71 @@ public function searchTicketPool(string $searchString) : array } return $r; } + + /** + * verifyRequesters(Ticket $item) : bool - + * verify if the requesters in the 'to be created' ticket are present + * in the 'to be merged' ticket. Returns false if a match could not be made. + * + * @param Ticket $item Ticket object containing the mailgate uid + * @return bool Returns true on success and false on failure + * @since 1.0.0 + */ + public function verifyRequesters(Ticket $item) : bool + { + // Match mailsender with ticket requester. + if(!$this->pattern[FilterPattern::MATCHSOURCE]) { + // Config does not require us to verify the users so we eval true. + return true; + } else { + // https://github.com/DonutsNL/ticketfilter/issues/4 + $succesfullUserMatch = false; + + // Get all the users from the ticket Object received from the pre_item_add hook. + foreach($item->input['_actors']["requester"] as $null => $mailUser){ + $usersToBeMatched[] = [ + 'userId' => $mailUser['items_id'], + 'userEmail' => ($mailUser['default_email']) ? $mailUser['default_email'] : $mailUser['alternative_email'] + ]; + } + + // Fetch and match the requesters of the currently processed ticket. + // With the requesters in the ticket object received from the pre_item_add hook. + $usrObj = new $this->ticket->userlinkclass(); + $actors = $usrObj->getActors($this->getId()); + foreach ( $actors as $null => $ticketUsers) { + // Verify there are users assigned to process + if(is_array($ticketUsers) && count($ticketUsers) > 0) { + foreach($ticketUsers as $null => $userType) { + // Only evaluate user type requesters ignore watchers and technicians + if($userType['type'] == CommonITILActor::REQUESTER){ + // First search alternative email because we dont want + // to match an users_id:int(0) that would match with all anonymous users + // assigned to the ticket. + if(!empty($userType['alternative_email'])) { + if(array_search(($userType['alternative_email']), array_column($usersToBeMatched, 'userEmail')) === false){ + echo "no match on email: {$userType['alternative_email']}
"; + }else{ + echo "match on email in: {$userType['users_id']}
"; + $succesfullUserMatch = true; + } + } else { + // Try to match any non zero users_id in usersToBeMatched. + if(array_search($userType['users_id'], array_column($usersToBeMatched, 'userId')) === false){ + echo "no match on userId: {$userType['users_id']}
"; + }else{ + echo "match on userId in: {$userType['users_id']}
"; + $succesfullUserMatch = true; + } + } + } // Ignore the user that is either watcher or technician @see glpi/src/Ticket_User.php + } // foreach + }// No requesters assigned to ticket + }// foreach + + // We did not match any user from the email with the referenced ticket + // Per configuration do not merge the followup. + return ($succesfullUserMatch) ? true : false; + } + } } diff --git a/tests/TicketFilterTest.php b/tests/TicketFilterTest.php index df2de41..1880a22 100644 --- a/tests/TicketFilterTest.php +++ b/tests/TicketFilterTest.php @@ -45,7 +45,5 @@ public function testTicketHandler() ); } } -// This plugin uses extensive database functions -// therefor we need to modify the classes to allow for more -// testing. This function is just preparations to allow -// phpunit testing. Simply run ./vendor/bin/phpunit ./tests \ No newline at end of file + +// phpunit testing Run ./vendor/bin/phpunit ./tests \ No newline at end of file diff --git a/tests/UserValidationTest.php b/tests/UserValidationTest.php new file mode 100644 index 0000000..205c0dc --- /dev/null +++ b/tests/UserValidationTest.php @@ -0,0 +1,32 @@ +addToFields('status', 1); + + $ticketHandler = new TicketHandler($ticket, FilterPattern::getDummyPattern()); + //if(!$this->assertTrue() + } +} + +class Ticket { + // Do stuff to represent a real ticket class. +} +*/ \ No newline at end of file diff --git a/tests/createTicketViaAPI.php b/tests/createTicketViaAPI.php new file mode 100644 index 0000000..3599fb5 --- /dev/null +++ b/tests/createTicketViaAPI.php @@ -0,0 +1,86 @@ + [ + "name" => "[TEST TICKET] CREATE TEST TICKET!", // Title + "content" => "Awsom ticket content!", // Content body + "_users_id_requester" => 523, // Match your user + "urgency" => 3, + "itilcategories_id" => 1, // Match your category + "type" => 2, // Request + "status" => 1 // Open. + ], +]); + +// Init Curl library; +// I just assume its present, else an fatal error will occur. +$ch = curl_init(); + +// Define HTTP headers to be send by Curl. +$initHeaders = ['Content-Type: application/json', + "Authorization: user_token $userToken", + "App-Token: $appToken", + ]; + +// Initialize CURL params. +// See: https://www.php.net/manual/en/function.curl-setopt-array.php +$options = [ + CURLOPT_URL => $glpiUrl.'/apirest.php/initSession', // What URL to open + CURLOPT_HEADER => false, // Do not return header information in response + CURLOPT_POST => true, // Use HTTP POST in the request + CURLOPT_TIMEOUT => 4, // Timeout after 4 attempts + CURLOPT_HTTPHEADER => $initHeaders, // Include de Init headers + CURLOPT_RETURNTRANSFER => true, // Put response in the returnvalue of curl_exec() for further processing +]; + +// perform Curl to receive valid session Token. +try { + curl_setopt_array($ch, $options); + if( ! $result = curl_exec($ch)) { + trigger_error(curl_error($ch)); + } +}catch(Exception $e){ // Might also be ValueError :S + echo 'Caught exception: ', $e->getMessage(), "\n"; + exit; +} + +var_dump($result); + +// A nice thing to do would be to catch the HTTP 200 (OK) here and validate the response. +// I assume the request is succesfull, else an error will be shown by var_dump; +// But im lazy in debugging scripts. + +// Lets create our ticket; +$ticketHeaders = ['Content-Type: application/json', + 'App-Token: $appToken', + "Session-Token: ".json_decode($result)->session_token, + ]; + +// ReInitialize CURL params. +$options = [ + CURLOPT_URL => $glpiUrl.'/apirest.php/Ticket', + CURLOPT_HEADER => false, + CURLOPT_POST => true, + CURLOPT_TIMEOUT => 4, + CURLOPT_HTTPHEADER => $ticketHeaders, + CURLOPT_POSTFIELDS => $ticketBody, + CURLOPT_RETURNTRANSFER => true, +]; + +// perform Curl to receive valid session Token. +try { + curl_setopt_array($ch, $options); + if( ! $result = curl_exec($ch)) { + trigger_error(curl_error($ch)); + } +}catch(ValueError $e){ + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +var_dump($result); + +curl_close($ch); +