diff --git a/api/BusinessLogic/Attachments/AttachmentHandler.php b/api/BusinessLogic/Attachments/AttachmentHandler.php index 04a73e42..4ef3d72b 100644 --- a/api/BusinessLogic/Attachments/AttachmentHandler.php +++ b/api/BusinessLogic/Attachments/AttachmentHandler.php @@ -5,14 +5,55 @@ namespace BusinessLogic\Attachments; use BusinessLogic\Exceptions\ValidationException; use BusinessLogic\ValidationModel; +use DataAccess\Attachments\AttachmentGateway; +use DataAccess\Files\FileWriter; +use DataAccess\Tickets\TicketGateway; class AttachmentHandler { + /* @var $ticketGateway TicketGateway */ + private $ticketGateway; + + /* @var $attachmentGateway AttachmentGateway */ + private $attachmentGateway; + + /* @var $fileWriter FileWriter */ + private $fileWriter; + + function __construct($ticketGateway, $attachmentGateway, $fileWriter) { + $this->ticketGateway = $ticketGateway; + $this->attachmentGateway = $attachmentGateway; + $this->fileWriter = $fileWriter; + } + /** * @param $createAttachmentModel CreateAttachmentForTicketModel + * @param $heskSettings array + * @return TicketAttachment the newly created attachment */ - function createAttachmentForTicket($createAttachmentModel) { + function createAttachmentForTicket($createAttachmentModel, $heskSettings) { $this->validate($createAttachmentModel); + + $decodedAttachment = base64_decode($createAttachmentModel->attachmentContents); + + $ticket = $this->ticketGateway->getTicketById($createAttachmentModel->ticketId, $heskSettings); + $cleanedFileName = $this->cleanFileName($createAttachmentModel->displayName); + + $ticketAttachment = new TicketAttachment(); + $ticketAttachment->savedName = $this->generateSavedName($ticket->trackingId, + $cleanedFileName, $createAttachmentModel->fileExtension); + $ticketAttachment->displayName = $cleanedFileName; + $ticketAttachment->ticketTrackingId = $ticket->trackingId; + $ticketAttachment->type = $createAttachmentModel->type; + $ticketAttachment->downloadCount = 0; + + $ticketAttachment->fileSize = + $this->fileWriter->writeToFile($ticketAttachment->savedName, $heskSettings['attach_dir'], $decodedAttachment); + + $attachmentId = $this->attachmentGateway->createAttachmentForTicket($ticketAttachment, $heskSettings); + $ticketAttachment->id = $attachmentId; + + return $ticketAttachment; } /** @@ -40,6 +81,12 @@ class AttachmentHandler { $errorKeys[] = 'TICKET_ID_MISSING'; } + if (!in_array($createAttachmentModel->type, array(AttachmentType::MESSAGE, AttachmentType::REPLY))) { + $errorKeys[] = 'INVALID_ATTACHMENT_TYPE'; + } + + //-- TODO Extension, size + if (count($errorKeys) > 0) { $validationModel = new ValidationModel(); $validationModel->errorKeys = $errorKeys; @@ -47,4 +94,259 @@ class AttachmentHandler { throw new ValidationException($validationModel); } } + + private function generateSavedName($trackingId, $displayName, $fileExtension) { + $useChars = 'AEUYBDGHJLMNPQRSTVWXZ123456789'; + $tmp = uniqid(); + for ($j = 1; $j < 10; $j++) { + $tmp .= $useChars{mt_rand(0, 29)}; + } + + + return substr($trackingId . '_' . md5($tmp . $displayName), 0, 200) . $fileExtension; + } + + /** + * @param $displayName string original file name + * @return string The cleaned file name + */ + private function cleanFileName($displayName) { + $filename = str_replace(array('%20', '+'), '-', $displayName); + $filename = preg_replace('/[\s-]+/', '-', $filename); + $filename = $this->removeAccents($filename); + $filename = preg_replace('/[^A-Za-z0-9\.\-_]/', '', $filename); + $filename = trim($filename, '-_'); + + return $filename; + } + + // The following code has been borrowed from Wordpress, and also from posting_functions.inc.php :P + // Credits: http://wordpress.org + private function removeAccents($string) + { + if (!preg_match('/[\x80-\xff]/', $string)) { + return $string; + } + + if ($this->seemsUtf8($string)) { + $chars = array( + // Decompositions for Latin-1 Supplement + chr(194) . chr(170) => 'a', chr(194) . chr(186) => 'o', + chr(195) . chr(128) => 'A', chr(195) . chr(129) => 'A', + chr(195) . chr(130) => 'A', chr(195) . chr(131) => 'A', + chr(195) . chr(132) => 'A', chr(195) . chr(133) => 'A', + chr(195) . chr(134) => 'AE', chr(195) . chr(135) => 'C', + chr(195) . chr(136) => 'E', chr(195) . chr(137) => 'E', + chr(195) . chr(138) => 'E', chr(195) . chr(139) => 'E', + chr(195) . chr(140) => 'I', chr(195) . chr(141) => 'I', + chr(195) . chr(142) => 'I', chr(195) . chr(143) => 'I', + chr(195) . chr(144) => 'D', chr(195) . chr(145) => 'N', + chr(195) . chr(146) => 'O', chr(195) . chr(147) => 'O', + chr(195) . chr(148) => 'O', chr(195) . chr(149) => 'O', + chr(195) . chr(150) => 'O', chr(195) . chr(153) => 'U', + chr(195) . chr(154) => 'U', chr(195) . chr(155) => 'U', + chr(195) . chr(156) => 'U', chr(195) . chr(157) => 'Y', + chr(195) . chr(158) => 'TH', chr(195) . chr(159) => 's', + chr(195) . chr(160) => 'a', chr(195) . chr(161) => 'a', + chr(195) . chr(162) => 'a', chr(195) . chr(163) => 'a', + chr(195) . chr(164) => 'a', chr(195) . chr(165) => 'a', + chr(195) . chr(166) => 'ae', chr(195) . chr(167) => 'c', + chr(195) . chr(168) => 'e', chr(195) . chr(169) => 'e', + chr(195) . chr(170) => 'e', chr(195) . chr(171) => 'e', + chr(195) . chr(172) => 'i', chr(195) . chr(173) => 'i', + chr(195) . chr(174) => 'i', chr(195) . chr(175) => 'i', + chr(195) . chr(176) => 'd', chr(195) . chr(177) => 'n', + chr(195) . chr(178) => 'o', chr(195) . chr(179) => 'o', + chr(195) . chr(180) => 'o', chr(195) . chr(181) => 'o', + chr(195) . chr(182) => 'o', chr(195) . chr(184) => 'o', + chr(195) . chr(185) => 'u', chr(195) . chr(186) => 'u', + chr(195) . chr(187) => 'u', chr(195) . chr(188) => 'u', + chr(195) . chr(189) => 'y', chr(195) . chr(190) => 'th', + chr(195) . chr(191) => 'y', chr(195) . chr(152) => 'O', + // Decompositions for Latin Extended-A + chr(196) . chr(128) => 'A', chr(196) . chr(129) => 'a', + chr(196) . chr(130) => 'A', chr(196) . chr(131) => 'a', + chr(196) . chr(132) => 'A', chr(196) . chr(133) => 'a', + chr(196) . chr(134) => 'C', chr(196) . chr(135) => 'c', + chr(196) . chr(136) => 'C', chr(196) . chr(137) => 'c', + chr(196) . chr(138) => 'C', chr(196) . chr(139) => 'c', + chr(196) . chr(140) => 'C', chr(196) . chr(141) => 'c', + chr(196) . chr(142) => 'D', chr(196) . chr(143) => 'd', + chr(196) . chr(144) => 'D', chr(196) . chr(145) => 'd', + chr(196) . chr(146) => 'E', chr(196) . chr(147) => 'e', + chr(196) . chr(148) => 'E', chr(196) . chr(149) => 'e', + chr(196) . chr(150) => 'E', chr(196) . chr(151) => 'e', + chr(196) . chr(152) => 'E', chr(196) . chr(153) => 'e', + chr(196) . chr(154) => 'E', chr(196) . chr(155) => 'e', + chr(196) . chr(156) => 'G', chr(196) . chr(157) => 'g', + chr(196) . chr(158) => 'G', chr(196) . chr(159) => 'g', + chr(196) . chr(160) => 'G', chr(196) . chr(161) => 'g', + chr(196) . chr(162) => 'G', chr(196) . chr(163) => 'g', + chr(196) . chr(164) => 'H', chr(196) . chr(165) => 'h', + chr(196) . chr(166) => 'H', chr(196) . chr(167) => 'h', + chr(196) . chr(168) => 'I', chr(196) . chr(169) => 'i', + chr(196) . chr(170) => 'I', chr(196) . chr(171) => 'i', + chr(196) . chr(172) => 'I', chr(196) . chr(173) => 'i', + chr(196) . chr(174) => 'I', chr(196) . chr(175) => 'i', + chr(196) . chr(176) => 'I', chr(196) . chr(177) => 'i', + chr(196) . chr(178) => 'IJ', chr(196) . chr(179) => 'ij', + chr(196) . chr(180) => 'J', chr(196) . chr(181) => 'j', + chr(196) . chr(182) => 'K', chr(196) . chr(183) => 'k', + chr(196) . chr(184) => 'k', chr(196) . chr(185) => 'L', + chr(196) . chr(186) => 'l', chr(196) . chr(187) => 'L', + chr(196) . chr(188) => 'l', chr(196) . chr(189) => 'L', + chr(196) . chr(190) => 'l', chr(196) . chr(191) => 'L', + chr(197) . chr(128) => 'l', chr(197) . chr(129) => 'L', + chr(197) . chr(130) => 'l', chr(197) . chr(131) => 'N', + chr(197) . chr(132) => 'n', chr(197) . chr(133) => 'N', + chr(197) . chr(134) => 'n', chr(197) . chr(135) => 'N', + chr(197) . chr(136) => 'n', chr(197) . chr(137) => 'N', + chr(197) . chr(138) => 'n', chr(197) . chr(139) => 'N', + chr(197) . chr(140) => 'O', chr(197) . chr(141) => 'o', + chr(197) . chr(142) => 'O', chr(197) . chr(143) => 'o', + chr(197) . chr(144) => 'O', chr(197) . chr(145) => 'o', + chr(197) . chr(146) => 'OE', chr(197) . chr(147) => 'oe', + chr(197) . chr(148) => 'R', chr(197) . chr(149) => 'r', + chr(197) . chr(150) => 'R', chr(197) . chr(151) => 'r', + chr(197) . chr(152) => 'R', chr(197) . chr(153) => 'r', + chr(197) . chr(154) => 'S', chr(197) . chr(155) => 's', + chr(197) . chr(156) => 'S', chr(197) . chr(157) => 's', + chr(197) . chr(158) => 'S', chr(197) . chr(159) => 's', + chr(197) . chr(160) => 'S', chr(197) . chr(161) => 's', + chr(197) . chr(162) => 'T', chr(197) . chr(163) => 't', + chr(197) . chr(164) => 'T', chr(197) . chr(165) => 't', + chr(197) . chr(166) => 'T', chr(197) . chr(167) => 't', + chr(197) . chr(168) => 'U', chr(197) . chr(169) => 'u', + chr(197) . chr(170) => 'U', chr(197) . chr(171) => 'u', + chr(197) . chr(172) => 'U', chr(197) . chr(173) => 'u', + chr(197) . chr(174) => 'U', chr(197) . chr(175) => 'u', + chr(197) . chr(176) => 'U', chr(197) . chr(177) => 'u', + chr(197) . chr(178) => 'U', chr(197) . chr(179) => 'u', + chr(197) . chr(180) => 'W', chr(197) . chr(181) => 'w', + chr(197) . chr(182) => 'Y', chr(197) . chr(183) => 'y', + chr(197) . chr(184) => 'Y', chr(197) . chr(185) => 'Z', + chr(197) . chr(186) => 'z', chr(197) . chr(187) => 'Z', + chr(197) . chr(188) => 'z', chr(197) . chr(189) => 'Z', + chr(197) . chr(190) => 'z', chr(197) . chr(191) => 's', + // Decompositions for Latin Extended-B + chr(200) . chr(152) => 'S', chr(200) . chr(153) => 's', + chr(200) . chr(154) => 'T', chr(200) . chr(155) => 't', + // Euro Sign + chr(226) . chr(130) . chr(172) => 'E', + // GBP (Pound) Sign + chr(194) . chr(163) => '', + // Vowels with diacritic (Vietnamese) + // unmarked + chr(198) . chr(160) => 'O', chr(198) . chr(161) => 'o', + chr(198) . chr(175) => 'U', chr(198) . chr(176) => 'u', + // grave accent + chr(225) . chr(186) . chr(166) => 'A', chr(225) . chr(186) . chr(167) => 'a', + chr(225) . chr(186) . chr(176) => 'A', chr(225) . chr(186) . chr(177) => 'a', + chr(225) . chr(187) . chr(128) => 'E', chr(225) . chr(187) . chr(129) => 'e', + chr(225) . chr(187) . chr(146) => 'O', chr(225) . chr(187) . chr(147) => 'o', + chr(225) . chr(187) . chr(156) => 'O', chr(225) . chr(187) . chr(157) => 'o', + chr(225) . chr(187) . chr(170) => 'U', chr(225) . chr(187) . chr(171) => 'u', + chr(225) . chr(187) . chr(178) => 'Y', chr(225) . chr(187) . chr(179) => 'y', + // hook + chr(225) . chr(186) . chr(162) => 'A', chr(225) . chr(186) . chr(163) => 'a', + chr(225) . chr(186) . chr(168) => 'A', chr(225) . chr(186) . chr(169) => 'a', + chr(225) . chr(186) . chr(178) => 'A', chr(225) . chr(186) . chr(179) => 'a', + chr(225) . chr(186) . chr(186) => 'E', chr(225) . chr(186) . chr(187) => 'e', + chr(225) . chr(187) . chr(130) => 'E', chr(225) . chr(187) . chr(131) => 'e', + chr(225) . chr(187) . chr(136) => 'I', chr(225) . chr(187) . chr(137) => 'i', + chr(225) . chr(187) . chr(142) => 'O', chr(225) . chr(187) . chr(143) => 'o', + chr(225) . chr(187) . chr(148) => 'O', chr(225) . chr(187) . chr(149) => 'o', + chr(225) . chr(187) . chr(158) => 'O', chr(225) . chr(187) . chr(159) => 'o', + chr(225) . chr(187) . chr(166) => 'U', chr(225) . chr(187) . chr(167) => 'u', + chr(225) . chr(187) . chr(172) => 'U', chr(225) . chr(187) . chr(173) => 'u', + chr(225) . chr(187) . chr(182) => 'Y', chr(225) . chr(187) . chr(183) => 'y', + // tilde + chr(225) . chr(186) . chr(170) => 'A', chr(225) . chr(186) . chr(171) => 'a', + chr(225) . chr(186) . chr(180) => 'A', chr(225) . chr(186) . chr(181) => 'a', + chr(225) . chr(186) . chr(188) => 'E', chr(225) . chr(186) . chr(189) => 'e', + chr(225) . chr(187) . chr(132) => 'E', chr(225) . chr(187) . chr(133) => 'e', + chr(225) . chr(187) . chr(150) => 'O', chr(225) . chr(187) . chr(151) => 'o', + chr(225) . chr(187) . chr(160) => 'O', chr(225) . chr(187) . chr(161) => 'o', + chr(225) . chr(187) . chr(174) => 'U', chr(225) . chr(187) . chr(175) => 'u', + chr(225) . chr(187) . chr(184) => 'Y', chr(225) . chr(187) . chr(185) => 'y', + // acute accent + chr(225) . chr(186) . chr(164) => 'A', chr(225) . chr(186) . chr(165) => 'a', + chr(225) . chr(186) . chr(174) => 'A', chr(225) . chr(186) . chr(175) => 'a', + chr(225) . chr(186) . chr(190) => 'E', chr(225) . chr(186) . chr(191) => 'e', + chr(225) . chr(187) . chr(144) => 'O', chr(225) . chr(187) . chr(145) => 'o', + chr(225) . chr(187) . chr(154) => 'O', chr(225) . chr(187) . chr(155) => 'o', + chr(225) . chr(187) . chr(168) => 'U', chr(225) . chr(187) . chr(169) => 'u', + // dot below + chr(225) . chr(186) . chr(160) => 'A', chr(225) . chr(186) . chr(161) => 'a', + chr(225) . chr(186) . chr(172) => 'A', chr(225) . chr(186) . chr(173) => 'a', + chr(225) . chr(186) . chr(182) => 'A', chr(225) . chr(186) . chr(183) => 'a', + chr(225) . chr(186) . chr(184) => 'E', chr(225) . chr(186) . chr(185) => 'e', + chr(225) . chr(187) . chr(134) => 'E', chr(225) . chr(187) . chr(135) => 'e', + chr(225) . chr(187) . chr(138) => 'I', chr(225) . chr(187) . chr(139) => 'i', + chr(225) . chr(187) . chr(140) => 'O', chr(225) . chr(187) . chr(141) => 'o', + chr(225) . chr(187) . chr(152) => 'O', chr(225) . chr(187) . chr(153) => 'o', + chr(225) . chr(187) . chr(162) => 'O', chr(225) . chr(187) . chr(163) => 'o', + chr(225) . chr(187) . chr(164) => 'U', chr(225) . chr(187) . chr(165) => 'u', + chr(225) . chr(187) . chr(176) => 'U', chr(225) . chr(187) . chr(177) => 'u', + chr(225) . chr(187) . chr(180) => 'Y', chr(225) . chr(187) . chr(181) => 'y', + // Vowels with diacritic (Chinese, Hanyu Pinyin) + chr(201) . chr(145) => 'a', + // macron + chr(199) . chr(149) => 'U', chr(199) . chr(150) => 'u', + // acute accent + chr(199) . chr(151) => 'U', chr(199) . chr(152) => 'u', + // caron + chr(199) . chr(141) => 'A', chr(199) . chr(142) => 'a', + chr(199) . chr(143) => 'I', chr(199) . chr(144) => 'i', + chr(199) . chr(145) => 'O', chr(199) . chr(146) => 'o', + chr(199) . chr(147) => 'U', chr(199) . chr(148) => 'u', + chr(199) . chr(153) => 'U', chr(199) . chr(154) => 'u', + // grave accent + chr(199) . chr(155) => 'U', chr(199) . chr(156) => 'u', + ); + + $string = strtr($string, $chars); + } else { + // Assume ISO-8859-1 if not UTF-8 + $chars['in'] = chr(128) . chr(131) . chr(138) . chr(142) . chr(154) . chr(158) + . chr(159) . chr(162) . chr(165) . chr(181) . chr(192) . chr(193) . chr(194) + . chr(195) . chr(196) . chr(197) . chr(199) . chr(200) . chr(201) . chr(202) + . chr(203) . chr(204) . chr(205) . chr(206) . chr(207) . chr(209) . chr(210) + . chr(211) . chr(212) . chr(213) . chr(214) . chr(216) . chr(217) . chr(218) + . chr(219) . chr(220) . chr(221) . chr(224) . chr(225) . chr(226) . chr(227) + . chr(228) . chr(229) . chr(231) . chr(232) . chr(233) . chr(234) . chr(235) + . chr(236) . chr(237) . chr(238) . chr(239) . chr(241) . chr(242) . chr(243) + . chr(244) . chr(245) . chr(246) . chr(248) . chr(249) . chr(250) . chr(251) + . chr(252) . chr(253) . chr(255); + + $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy"; + + $string = strtr($string, $chars['in'], $chars['out']); + $double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254)); + $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'); + $string = str_replace($double_chars['in'], $double_chars['out'], $string); + } + + return $string; + } + + private function seemsUtf8($str) + { + $length = strlen($str); + for ($i = 0; $i < $length; $i++) { + $c = ord($str[$i]); + if ($c < 0x80) $n = 0; # 0bbbbbbb + elseif (($c & 0xE0) == 0xC0) $n = 1; # 110bbbbb + elseif (($c & 0xF0) == 0xE0) $n = 2; # 1110bbbb + elseif (($c & 0xF8) == 0xF0) $n = 3; # 11110bbb + elseif (($c & 0xFC) == 0xF8) $n = 4; # 111110bb + elseif (($c & 0xFE) == 0xFC) $n = 5; # 1111110b + else return false; # Does not match any model + for ($j = 0; $j < $n; $j++) { # n bytes matching 10bbbbbb follow ? + if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) + return false; + } + } + return true; + } } \ No newline at end of file diff --git a/api/BusinessLogic/Attachments/CreateAttachmentModel.php b/api/BusinessLogic/Attachments/CreateAttachmentModel.php index 9a5f5428..cf137828 100644 --- a/api/BusinessLogic/Attachments/CreateAttachmentModel.php +++ b/api/BusinessLogic/Attachments/CreateAttachmentModel.php @@ -10,6 +10,9 @@ class CreateAttachmentModel { /* @var $displayName string */ public $displayName; + /* @var $fileExtension string */ + public $fileExtension; + /* @var $id int */ public $fileSize; diff --git a/api/DataAccess/Files/FileWriter.php b/api/DataAccess/Files/FileWriter.php new file mode 100644 index 00000000..8ca47891 --- /dev/null +++ b/api/DataAccess/Files/FileWriter.php @@ -0,0 +1,25 @@ +attachmentHandler = new AttachmentHandler(); + $this->ticketGateway = $this->createMock(TicketGateway::class); + $this->attachmentGateway = $this->createMock(AttachmentGateway::class); + $this->fileWriter = $this->createMock(FileWriter::class); + $this->heskSettings = array( + 'attach_dir' => 'attachments' + ); + + $this->attachmentHandler = new AttachmentHandler($this->ticketGateway, $this->attachmentGateway, $this->fileWriter); $this->createAttachmentForTicketModel = new CreateAttachmentForTicketModel(); $this->createAttachmentForTicketModel->attachmentContents = base64_encode('string'); - $this->createAttachmentForTicketModel->displayName = 'Display Name'; + $this->createAttachmentForTicketModel->displayName = 'DisplayName'; $this->createAttachmentForTicketModel->ticketId = 1; + $this->createAttachmentForTicketModel->type = AttachmentType::MESSAGE; } function testThatValidateThrowsAnExceptionWhenTheAttachmentBodyIsNull() { @@ -32,7 +56,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/CONTENTS_EMPTY/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheAttachmentBodyIsEmpty() { @@ -44,7 +68,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/CONTENTS_EMPTY/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheAttachmentBodyIsInvalidBase64() { @@ -56,7 +80,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/CONTENTS_NOT_BASE_64/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheDisplayNameIsNull() { @@ -68,7 +92,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/DISPLAY_NAME_EMPTY/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheDisplayNameIsEmpty() { @@ -80,7 +104,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/DISPLAY_NAME_EMPTY/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheTicketIdIsNull() { @@ -92,7 +116,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/TICKET_ID_MISSING/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheTicketIdIsANonPositiveInteger() { @@ -104,6 +128,71 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/TICKET_ID_MISSING/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + } + + function testThatValidateThrowsAnExceptionWhenTheAttachmentTypeIsNeitherMessageNorReply() { + //-- Arrange + $this->createAttachmentForTicketModel->type = 5; + + //-- Assert + $this->expectException(ValidationException::class); + $this->expectExceptionMessageRegExp('/INVALID_ATTACHMENT_TYPE/'); + + //-- Act + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + } + + function testItSavesATicketWithTheProperProperties() { + //-- Arrange + $this->createAttachmentForTicketModel->ticketId = 1; + $ticket = new Ticket(); + $ticket->trackingId = 'ABC-DEF-1234'; + $this->ticketGateway->method('getTicketById')->with(1, $this->anything())->willReturn($ticket); + + $ticketAttachment = new TicketAttachment(); + $ticketAttachment->displayName = $this->createAttachmentForTicketModel->displayName; + $ticketAttachment->ticketTrackingId = $ticket->trackingId; + $ticketAttachment->type = $this->createAttachmentForTicketModel->type; + $ticketAttachment->downloadCount = 0; + $ticketAttachment->id = 50; + + $this->attachmentGateway->method('createAttachmentForTicket')->willReturn(50); + + + //-- Act + $actual = $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + + //-- Assert + self::assertThat($actual->id, self::equalTo(50)); + self::assertThat($actual->downloadCount, self::equalTo(0)); + self::assertThat($actual->type, self::equalTo($this->createAttachmentForTicketModel->type)); + self::assertThat($actual->ticketTrackingId, self::equalTo($ticket->trackingId)); + self::assertThat($actual->displayName, self::equalTo($this->createAttachmentForTicketModel->displayName)); + } + + function testItSavesTheFileToTheFileSystem() { + //-- Arrange + $this->createAttachmentForTicketModel->ticketId = 1; + $ticket = new Ticket(); + $ticket->trackingId = 'ABC-DEF-1234'; + $this->ticketGateway->method('getTicketById')->with(1, $this->anything())->willReturn($ticket); + + $ticketAttachment = new TicketAttachment(); + $ticketAttachment->displayName = $this->createAttachmentForTicketModel->displayName; + $ticketAttachment->ticketTrackingId = $ticket->trackingId; + $ticketAttachment->type = $this->createAttachmentForTicketModel->type; + $ticketAttachment->downloadCount = 0; + $ticketAttachment->id = 50; + + $this->fileWriter->method('writeToFile')->willReturn(1024); + $this->attachmentGateway->method('createAttachmentForTicket')->willReturn(50); + + + //-- Act + $actual = $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + + //-- Assert + self::assertThat($actual->fileSize, self::equalTo(1024)); } }