diff --git a/src/Dto/Tickets/AllTicketsDTO.php b/src/Dto/Tickets/AllTicketsDTO.php index 3c6eb85..aeb9672 100644 --- a/src/Dto/Tickets/AllTicketsDTO.php +++ b/src/Dto/Tickets/AllTicketsDTO.php @@ -2,6 +2,8 @@ namespace CodebarAg\Zendesk\Dto\Tickets; +use Exception; +use Illuminate\Support\Arr; use Saloon\Http\Response; use Spatie\LaravelData\Data; @@ -19,13 +21,17 @@ public static function fromResponse(Response $response): self { $data = $response->json(); + if (! $data) { + throw new Exception('Unable to create DTO. Data missing from response.'); + } + return new self( - tickets: collect($data['tickets'])->map(function (array $ticket) { + tickets: collect(Arr::get($data, 'tickets'))->map(function (array $ticket) { return SingleTicketDTO::fromArray($ticket); })->toArray(), - count: $data['count'], - next_page_url: $data['next_page'], - previous_page_url: $data['previous_page'], + count: Arr::get($data, 'count'), + next_page_url: Arr::get($data, 'next_page'), + previous_page_url: Arr::get($data, 'previous_page'), ); } } diff --git a/src/Dto/Tickets/Attachments/AttachmentDTO.php b/src/Dto/Tickets/Attachments/AttachmentDTO.php index 23324ed..a139f48 100644 --- a/src/Dto/Tickets/Attachments/AttachmentDTO.php +++ b/src/Dto/Tickets/Attachments/AttachmentDTO.php @@ -3,6 +3,7 @@ namespace CodebarAg\Zendesk\Dto\Tickets\Attachments; use CodebarAg\Zendesk\Enums\MalwareScanResult; +use Illuminate\Support\Arr; use Spatie\LaravelData\Data; class AttachmentDTO extends Data @@ -27,7 +28,7 @@ public function __construct( public static function fromArray(array $data): self { - $thumbnails = $data['thumbnails'] ?? null; + $thumbnails = Arr::get($data, 'thumbnails'); if ($thumbnails) { foreach ($thumbnails as $key => $thumbnail) { @@ -36,20 +37,20 @@ public static function fromArray(array $data): self } return new self( - content_type: $data['content_type'] ?? null, - content_url: $data['content_url'] ?? null, - deleted: $data['deleted'] ?? null, - file_name: $data['file_name'] ?? null, - height: $data['height'] ?? null, - id: $data['id'] ?? null, - inline: $data['inline'] ?? null, - malware_access_override: $data['malware_access_override'] ?? null, - malware_scan_result: MalwareScanResult::tryFrom($data['malware_scan_result'] ?? null), - mapped_content_url: $data['mapped_content_url'] ?? null, - size: $data['size'] ?? null, + content_type: Arr::get($data, 'content_type'), + content_url: Arr::get($data, 'content_url'), + deleted: Arr::get($data, 'deleted'), + file_name: Arr::get($data, 'file_name'), + height: Arr::get($data, 'height'), + id: Arr::get($data, 'id'), + inline: Arr::get($data, 'inline'), + malware_access_override: Arr::get($data, 'malware_access_override'), + malware_scan_result: MalwareScanResult::tryFrom(Arr::get($data, 'malware_scan_result')), + mapped_content_url: Arr::get($data, 'mapped_content_url'), + size: Arr::get($data, 'size'), thumbnails: $thumbnails ?? null, - url: $data['url'] ?? null, - width: $data['width'] ?? null, + url: Arr::get($data, 'url'), + width: Arr::get($data, 'width'), ); } diff --git a/src/Dto/Tickets/Attachments/ThumbnailDTO.php b/src/Dto/Tickets/Attachments/ThumbnailDTO.php index d4930fd..74db1d3 100644 --- a/src/Dto/Tickets/Attachments/ThumbnailDTO.php +++ b/src/Dto/Tickets/Attachments/ThumbnailDTO.php @@ -3,6 +3,7 @@ namespace CodebarAg\Zendesk\Dto\Tickets\Attachments; use CodebarAg\Zendesk\Enums\MalwareScanResult; +use Illuminate\Support\Arr; use Spatie\LaravelData\Data; class ThumbnailDTO extends Data @@ -27,19 +28,19 @@ public function __construct( public static function fromArray(array $data): self { return new self( - content_type: $data['content_type'] ?? null, - content_url: $data['content_url'] ?? null, - deleted: $data['deleted'] ?? null, - file_name: $data['file_name'] ?? null, - height: $data['height'] ?? null, - id: $data['id'] ?? null, - inline: $data['inline'] ?? null, - malware_access_override: $data['malware_access_override'] ?? null, - malware_scan_result: MalwareScanResult::tryFrom($data['malware_scan_result'] ?? null), - mapped_content_url: $data['mapped_content_url'] ?? null, - size: $data['size'] ?? null, - url: $data['url'] ?? null, - width: $data['width'] ?? null, + content_type: Arr::get($data, 'content_type'), + content_url: Arr::get($data, 'content_url'), + deleted: Arr::get($data, 'deleted'), + file_name: Arr::get($data, 'file_name'), + height: Arr::get($data, 'height'), + id: Arr::get($data, 'id'), + inline: Arr::get($data, 'inline'), + malware_access_override: Arr::get($data, 'malware_access_override'), + malware_scan_result: MalwareScanResult::tryFrom(Arr::get($data, 'malware_scan_result')), + mapped_content_url: Arr::get($data, 'mapped_content_url'), + size: Arr::get($data, 'size'), + url: Arr::get($data, 'url'), + width: Arr::get($data, 'width'), ); } } diff --git a/src/Dto/Tickets/Attachments/UploadDTO.php b/src/Dto/Tickets/Attachments/UploadDTO.php index 809065d..3e75673 100644 --- a/src/Dto/Tickets/Attachments/UploadDTO.php +++ b/src/Dto/Tickets/Attachments/UploadDTO.php @@ -2,6 +2,7 @@ namespace CodebarAg\Zendesk\Dto\Tickets\Attachments; +use Illuminate\Support\Arr; use Illuminate\Support\Carbon; use Saloon\Http\Response; use Spatie\LaravelData\Data; @@ -18,14 +19,14 @@ public function __construct( public static function fromResponse(Response $response): self { - $data = $response->json()['upload']; + $data = Arr::get($response->json(), 'upload'); return self::fromArray($data); } public static function fromArray(array $data): self { - $attachments = $data['attachments'] ?? null; + $attachments = Arr::get($data, 'attachments'); if ($attachments) { foreach ($attachments as $key => $attachment) { @@ -34,10 +35,10 @@ public static function fromArray(array $data): self } return new self( - token: $data['token'] ?? null, - expires_at: Carbon::parse($data['expires_at'] ?? null), + token: Arr::get($data, 'token'), + expires_at: Carbon::parse(Arr::get($data, 'expires_at')), attachments: $attachments, - attachment: self::getAttachment($data['attachment'] ?? null), + attachment: self::getAttachment(Arr::get($data, 'attachment')), ); } diff --git a/src/Dto/Tickets/Comments/CommentDTO.php b/src/Dto/Tickets/Comments/CommentDTO.php index 789c7bf..43cdc6a 100644 --- a/src/Dto/Tickets/Comments/CommentDTO.php +++ b/src/Dto/Tickets/Comments/CommentDTO.php @@ -2,6 +2,7 @@ namespace CodebarAg\Zendesk\Dto\Tickets\Comments; +use Illuminate\Support\Arr; use Illuminate\Support\Carbon; use Spatie\LaravelData\Data; @@ -27,19 +28,19 @@ public function __construct( public static function fromArray(array $data): self { return new self( - attachments: $data['attachments'] ?? null, - audit_id: $data['audit_id'] ?? null, - author_id: $data['author_id'] ?? null, - body: $data['body'] ?? null, - created_at: $data['created_at'] ?? null, - html_body: $data['html_body'] ?? null, - id: $data['id'] ?? null, - metadata: $data['metadata'] ?? null, - plain_body: $data['plain_body'] ?? null, - public: $data['public'] ?? null, - type: $data['type'] ?? null, - uploads: $data['uploads'] ?? null, - via: $data['via'] ?? null, + attachments: Arr::get($data, 'attachments'), + audit_id: Arr::get($data, 'audit_id'), + author_id: Arr::get($data, 'author_id'), + body: Arr::get($data, 'body'), + created_at: Arr::get($data, 'created_at'), + html_body: Arr::get($data, 'html_body'), + id: Arr::get($data, 'id'), + metadata: Arr::get($data, 'metadata'), + plain_body: Arr::get($data, 'plain_body'), + public: Arr::get($data, 'public'), + type: Arr::get($data, 'type'), + uploads: Arr::get($data, 'uploads'), + via: Arr::get($data, 'via'), ); } } diff --git a/src/Dto/Tickets/CountTicketsDTO.php b/src/Dto/Tickets/CountTicketsDTO.php index f6ee4b2..53de431 100644 --- a/src/Dto/Tickets/CountTicketsDTO.php +++ b/src/Dto/Tickets/CountTicketsDTO.php @@ -2,6 +2,8 @@ namespace CodebarAg\Zendesk\Dto\Tickets; +use Exception; +use Illuminate\Support\Arr; use Illuminate\Support\Carbon; use Saloon\Http\Response; use Spatie\LaravelData\Data; @@ -16,7 +18,11 @@ public function __construct( public static function fromResponse(Response $response): self { - $data = $response->json()['count']; + $data = Arr::get($response->json(), 'count'); + + if (! $data) { + throw new Exception('Unable to create DTO. Data missing from response.'); + } return new self( value: $data['value'], diff --git a/src/Dto/Tickets/SingleTicketDTO.php b/src/Dto/Tickets/SingleTicketDTO.php index c559373..8ec53ec 100644 --- a/src/Dto/Tickets/SingleTicketDTO.php +++ b/src/Dto/Tickets/SingleTicketDTO.php @@ -5,6 +5,8 @@ use CodebarAg\Zendesk\Dto\Tickets\Comments\CommentDTO; use CodebarAg\Zendesk\Enums\TicketPriority; use CodebarAg\Zendesk\Enums\TicketType; +use Exception; +use Illuminate\Support\Arr; use Illuminate\Support\Carbon; use Saloon\Http\Response; use Spatie\LaravelData\Data; @@ -68,83 +70,87 @@ public function __construct( public static function fromResponse(Response $response): self { - $data = $response->json()['ticket']; + $data = Arr::get($response->json(), 'ticket'); + + if (! $data) { + throw new Exception('Unable to create DTO. Data missing from response.'); + } return self::fromArray($data); } public static function fromArray(array $data): self { - $comment = array_key_exists('comment', $data) ? $data['comment'] : null; + $comment = Arr::get($data, 'comment'); if ($comment && ! $comment instanceof CommentDTO) { $comment = CommentDTO::fromArray($comment); } - $priority = array_key_exists('priority', $data) ? $data['priority'] : null; + $priority = Arr::get($data, 'priority'); if ($priority && ! $priority instanceof TicketPriority) { $priority = TicketPriority::tryFrom($priority); } - $type = array_key_exists('type', $data) ? $data['type'] : null; + $type = Arr::get($data, 'type'); if ($type && ! $type instanceof TicketType) { $type = TicketType::tryFrom($type); } return new self( - allow_attachments: $data['allow_attachments'] ?? null, - allow_channelback: $data['allow_channelback'] ?? null, - assignee_email: $data['assignee_email'] ?? null, - assignee_id: $data['assignee_id'] ?? null, - attribute_value_ids: $data['attribute_value_ids'] ?? null, - brand_id: $data['brand_id'] ?? null, - collaborator_ids: $data['collaborator_ids'] ?? null, - collaborators: $data['collaborators'] ?? null, + allow_attachments: Arr::get($data, 'allow_attachments'), + allow_channelback: Arr::get($data, 'allow_channelback'), + assignee_email: Arr::get($data, 'assignee_email'), + assignee_id: Arr::get($data, 'assignee_id'), + attribute_value_ids: Arr::get($data, 'attribute_value_ids'), + brand_id: Arr::get($data, 'brand_id'), + collaborator_ids: Arr::get($data, 'collaborator_ids'), + collaborators: Arr::get($data, 'collaborators'), comment: $comment, - created_at: Carbon::parse($data['created_at'] ?? null), - custom_fields: $data['custom_fields'] ?? null, - description: $data['description'] ?? null, - due_at: Carbon::parse($data['due_at'] ?? null), - email_cc_ids: $data['email_cc_ids'] ?? null, - email_ccs: $data['email_ccs'] ?? null, - external_id: $data['external_id'] ?? null, - follower_ids: $data['follower_ids'] ?? null, - followers: $data['followers'] ?? null, - followup_ids: $data['followup_ids'] ?? null, - forum_topic_id: $data['forum_topic_id'] ?? null, - from_messaging_channel: $data['from_messaging_channel'] ?? null, - group_id: $data['group_id'] ?? null, - has_incidents: $data['has_incidents'] ?? null, - id: $data['id'] ?? null, - is_public: $data['is_public'] ?? null, - macro_id: $data['macro_id'] ?? null, - macro_ids: $data['macro_ids'] ?? null, - metadata: $data['metadata'] ?? null, - organization_id: $data['organization_id'] ?? null, + created_at: Carbon::parse(Arr::get($data, 'created_at')), + custom_fields: Arr::get($data, 'custom_fields'), + description: Arr::get($data, 'description'), + due_at: Carbon::parse(Arr::get($data, 'due_at')), + email_cc_ids: Arr::get($data, 'email_cc_ids'), + email_ccs: Arr::get($data, 'email_ccs'), + external_id: Arr::get($data, 'external_id'), + follower_ids: Arr::get($data, 'follower_ids'), + followers: Arr::get($data, 'followers'), + followup_ids: Arr::get($data, 'followup_ids'), + forum_topic_id: Arr::get($data, 'forum_topic_id'), + from_messaging_channel: Arr::get($data, 'from_messaging_channel'), + group_id: Arr::get($data, 'group_id'), + has_incidents: Arr::get($data, 'has_incidents'), + id: Arr::get($data, 'id'), + is_public: Arr::get($data, 'is_public'), + macro_id: Arr::get($data, 'macro_id'), + macro_ids: Arr::get($data, 'macro_ids'), + metadata: Arr::get($data, 'metadata'), + organization_id: Arr::get($data, 'organization_id'), priority: $priority, - problem_id: $data['problem_id'] ?? null, - raw_subject: $data['raw_subject'] ?? null, - recipient: $data['recipient'] ?? null, - requester: $data['requester'] ?? null, - requester_id: $data['requester_id'] ?? null, - self_update: $data['self_update'] ?? null, - satisfaction_rating: $data['satisfaction_rating'] ?? null, - sharing_agreement_ids: $data['sharing_agreement_ids'] ?? null, - status: $data['status'] ?? null, - subject: $data['subject'] ?? null, - submitter_id: $data['submitter_id'] ?? null, - tags: $data['tags'] ?? null, - ticket_form_id: $data['ticket_form_id'] ?? null, + problem_id: Arr::get($data, 'problem_id'), + raw_subject: Arr::get($data, 'raw_subject'), + recipient: Arr::get($data, 'recipient'), + requester: Arr::get($data, 'requester'), + requester_id: Arr::get($data, 'requester_id'), + self_update: Arr::get($data, 'self_update'), + satisfaction_rating: Arr::get($data, 'satisfaction_rating'), + sharing_agreement_ids: Arr::get($data, 'sharing_agreement_ids'), + status: Arr::get($data, 'status'), + subject: Arr::get($data, 'subject'), + submitter_id: Arr::get($data, 'submitter_id'), + tags: Arr::get($data, 'tags'), + ticket_form_id: Arr::get($data, 'ticket_form_id'), type: $type, - updated_at: Carbon::parse($data['updated_at'] ?? null), - updated_stamp: $data['updated_stamp'] ?? null, - url: $data['url'] ?? null, - via: $data['via'] ?? null, - via_followup_source_id: $data['via_followup_source_id'] ?? null, - via_id: $data['via_id'] ?? null, - voice_comment: $data['voice_comment'] ?? null, + updated_at: Carbon::parse(Arr::get($data, 'updated_at')), + updated_stamp: Arr::get($data, 'updated_stamp'), + url: Arr::get($data, 'url'), + via: Arr::get($data, 'via'), + via_followup_source_id: Arr::get($data, 'via_followup_source_id'), + via_id: Arr::get($data, 'via_id'), + voice_comment: Arr::get($data, 'voice_comment'), ); } } diff --git a/src/Requests/AllTicketsRequest.php b/src/Requests/AllTicketsRequest.php index 18b0cbb..1ac12a8 100644 --- a/src/Requests/AllTicketsRequest.php +++ b/src/Requests/AllTicketsRequest.php @@ -3,6 +3,7 @@ namespace CodebarAg\Zendesk\Requests; use CodebarAg\Zendesk\Dto\Tickets\AllTicketsDTO; +use Exception; use Saloon\Contracts\Response; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -18,6 +19,10 @@ public function resolveEndpoint(): string public function createDtoFromResponse(Response $response): mixed { + if (! $response->successful()) { + throw new Exception('Request was not successful. Unable to create DTO.'); + } + return AllTicketsDTO::fromResponse($response); } } diff --git a/src/Requests/CountTicketsRequest.php b/src/Requests/CountTicketsRequest.php index 58fa67e..e5fbfe6 100644 --- a/src/Requests/CountTicketsRequest.php +++ b/src/Requests/CountTicketsRequest.php @@ -3,6 +3,7 @@ namespace CodebarAg\Zendesk\Requests; use CodebarAg\Zendesk\Dto\Tickets\CountTicketsDTO; +use Exception; use Saloon\Contracts\Response; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -18,6 +19,10 @@ public function resolveEndpoint(): string public function createDtoFromResponse(Response $response): mixed { + if (! $response->successful()) { + throw new Exception('Request was not successful. Unable to create DTO.'); + } + return CountTicketsDTO::fromResponse($response); } } diff --git a/src/Requests/CreateAttachmentRequest.php b/src/Requests/CreateAttachmentRequest.php index 75622ed..412af75 100644 --- a/src/Requests/CreateAttachmentRequest.php +++ b/src/Requests/CreateAttachmentRequest.php @@ -3,6 +3,7 @@ namespace CodebarAg\Zendesk\Requests; use CodebarAg\Zendesk\Dto\Tickets\Attachments\UploadDTO; +use Exception; use Saloon\Contracts\Body\HasBody; use Saloon\Contracts\Response; use Saloon\Enums\Method; @@ -42,6 +43,10 @@ protected function defaultBody(): mixed public function createDtoFromResponse(Response $response): mixed { + if (! $response->successful()) { + throw new Exception('Request was not successful. Unable to create DTO.'); + } + return UploadDTO::fromResponse($response); } } diff --git a/src/Requests/CreateSingleTicketRequest.php b/src/Requests/CreateSingleTicketRequest.php index 23621ef..a494d89 100644 --- a/src/Requests/CreateSingleTicketRequest.php +++ b/src/Requests/CreateSingleTicketRequest.php @@ -3,6 +3,7 @@ namespace CodebarAg\Zendesk\Requests; use CodebarAg\Zendesk\Dto\Tickets\SingleTicketDTO; +use Exception; use Saloon\Contracts\Body\HasBody; use Saloon\Contracts\Response; use Saloon\Enums\Method; @@ -40,6 +41,10 @@ protected function defaultBody(): array public function createDtoFromResponse(Response $response): mixed { + if (! $response->successful()) { + throw new Exception('Request was not successful. Unable to create DTO.'); + } + return SingleTicketDTO::fromResponse($response); } } diff --git a/src/Requests/SingleTicketRequest.php b/src/Requests/SingleTicketRequest.php index bacd226..64a5cbb 100644 --- a/src/Requests/SingleTicketRequest.php +++ b/src/Requests/SingleTicketRequest.php @@ -3,6 +3,7 @@ namespace CodebarAg\Zendesk\Requests; use CodebarAg\Zendesk\Dto\Tickets\SingleTicketDTO; +use Exception; use Saloon\Contracts\Response; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -25,6 +26,10 @@ public function resolveEndpoint(): string public function createDtoFromResponse(Response $response): mixed { + if (! $response->successful()) { + throw new Exception('Request was not successful. Unable to create DTO.'); + } + return SingleTicketDTO::fromResponse($response); } } diff --git a/src/ZendeskConnector.php b/src/ZendeskConnector.php index 4face32..ca8d8c0 100644 --- a/src/ZendeskConnector.php +++ b/src/ZendeskConnector.php @@ -26,6 +26,13 @@ protected function defaultHeaders(): array } protected function defaultAuth(): ?Authenticator + { + $authenticationString = $this->setAuth(); + + return new TokenAuthenticator(base64_encode($authenticationString), 'Basic'); + } + + public function setAuth(): string { if (! config('zendesk.auth.method')) { throw new \Exception('No authentication method provided.', 500); @@ -47,12 +54,10 @@ protected function defaultAuth(): ?Authenticator throw new \Exception('No API token provided for token authentication.', 500); } - $authenticationString = match (config('zendesk.auth.method')) { - 'basic' => $authenticationString = config('zendesk.auth.email_address').':'.config('zendesk.auth.password'), - 'token' => $authenticationString = config('zendesk.auth.email_address').'/token:'.config('zendesk.auth.api_token'), + return match (config('zendesk.auth.method')) { + 'basic' => config('zendesk.auth.email_address').':'.config('zendesk.auth.password'), + 'token' => config('zendesk.auth.email_address').'/token:'.config('zendesk.auth.api_token'), default => throw new \Exception('Invalid authentication method provided.', 500), }; - - return new TokenAuthenticator(base64_encode($authenticationString), 'Basic'); } } diff --git a/tests/Connectors/ZendeskConnectorTest.php b/tests/Connectors/ZendeskConnectorTest.php new file mode 100644 index 0000000..413adf1 --- /dev/null +++ b/tests/Connectors/ZendeskConnectorTest.php @@ -0,0 +1,190 @@ +resolveBaseUrl(); + +})->throws('No subdomain provided.', 500); + +it('will not throw an exception if a subdomain is set', closure: function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsag', + ]); + + $connector = new ZendeskConnector; + $connector->resolveBaseUrl(); + +})->expectNotToPerformAssertions(); + +it('will return the base path', closure: function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsag', + ]); + + $connector = new ZendeskConnector; + $path = $connector->resolveBaseUrl(); + + expect($path)->toBe('https://codebarsolutionsag.zendesk.com/api/v2'); + +}); + +it('will throw an exception if an auth method is not set', closure: function () { + config([ + 'zendesk.auth.method' => null, + ]); + + $connector = new ZendeskConnector; + $connector->setAuth(); + +})->throws('No authentication method provided.', 500); + +it('will throw an exception if an auth method invalid', closure: function () { + config([ + 'zendesk.auth.method' => 'not-a-valid-method', + ]); + + $connector = new ZendeskConnector; + $connector->setAuth(); + +})->throws('Invalid authentication method provided.', 500); + +it('will not throw an exception if an auth method valid', closure: function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsag', + 'zendesk.auth.email_address' => 'test@example.com', + 'zendesk.auth.api_token' => 'test-token', + 'zendesk.auth.password' => 'test-password', + ]); + + config([ + 'zendesk.auth.method' => 'token', + ]); + + $connector = new ZendeskConnector; + $connector->setAuth(); + + config([ + 'zendesk.auth.method' => 'basic', + ]); + + $connector = new ZendeskConnector; + $connector->setAuth(); + +})->expectNotToPerformAssertions(); + +it('will throw an exception if a token is not provided when using the token method', closure: function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsag', + 'zendesk.auth.method' => 'token', + 'zendesk.auth.email_address' => 'test@example.com', + 'zendesk.auth.api_token' => null, + 'zendesk.auth.password' => null, + ]); + + $connector = new ZendeskConnector; + $connector->setAuth(); + +})->throws('No API token provided for token authentication.', 500); + +it('will not throw an exception if a token is provided when using the token method', closure: function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsag', + 'zendesk.auth.method' => 'token', + 'zendesk.auth.email_address' => 'test@example.com', + 'zendesk.auth.api_token' => 'test-token', + 'zendesk.auth.password' => null, + ]); + + $connector = new ZendeskConnector; + $connector->setAuth(); + +})->expectNotToPerformAssertions(); + +it('will throw an exception if a password is not provided when using the basic method', closure: function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsag', + 'zendesk.auth.method' => 'basic', + 'zendesk.auth.email_address' => 'test@example.com', + 'zendesk.auth.api_token' => null, + 'zendesk.auth.password' => null, + ]); + + $connector = new ZendeskConnector; + $connector->setAuth(); + +})->throws('No password provided for basic authentication.', 500); + +it('will not throw an exception if a password is provided when using the password method', closure: function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsag', + 'zendesk.auth.method' => 'basic', + 'zendesk.auth.email_address' => 'test@example.com', + 'zendesk.auth.api_token' => null, + 'zendesk.auth.password' => 'test-password', + ]); + + $connector = new ZendeskConnector; + $connector->setAuth(); + +})->expectNotToPerformAssertions(); + +it('will compile the correct authentication string for token method', function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsag', + 'zendesk.auth.method' => 'token', + 'zendesk.auth.email_address' => 'test@example.com', + 'zendesk.auth.api_token' => 'test-token', + 'zendesk.auth.password' => null, + ]); + + $connector = new ZendeskConnector; + + $token = $connector->setAuth(); + + expect($token)->toBe('test@example.com/token:test-token'); +}); + +it('will compile the correct authentication string for basic method', function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsag', + 'zendesk.auth.method' => 'basic', + 'zendesk.auth.email_address' => 'test@example.com', + 'zendesk.auth.api_token' => null, + 'zendesk.auth.password' => 'test-password', + ]); + + $connector = new ZendeskConnector; + + $token = $connector->setAuth(); + + expect($token)->toBe('test@example.com:test-password'); +}); + + +it('will throw and authentication error when details are incorrect', function () { + config([ + 'zendesk.subdomain' => 'codebarsolutionsagwrong', + 'zendesk.auth.method' => 'basic', + 'zendesk.auth.email_address' => 'tessdft@example.com', + 'zendesk.auth.api_token' => null, + 'zendesk.auth.password' => 'test-passwordsdfsf', + ]); + + $mockClient = new MockClient([ + SingleTicketRequest::class => MockResponse::fixture('details-incorrect-request'), + ]); + + $connector = new ZendeskConnector; + $connector->withMockClient($mockClient); + + $response = $connector->send(new SingleTicketRequest(81)); + + $mockClient->assertSent(SingleTicketRequest::class); + + expect($response->status())->toBe(404); +}); diff --git a/tests/Fixtures/Files/test.png b/tests/Fixtures/Files/test.png new file mode 100644 index 0000000..372c2a2 Binary files /dev/null and b/tests/Fixtures/Files/test.png differ diff --git a/tests/Fixtures/Saloon/all-tickets-request.json b/tests/Fixtures/Saloon/all-tickets-request.json new file mode 100644 index 0000000..9b1059f --- /dev/null +++ b/tests/Fixtures/Saloon/all-tickets-request.json @@ -0,0 +1,38 @@ +{ + "statusCode": 200, + "headers": { + "Date": "Mon, 03 Jul 2023 11:31:03 GMT", + "Content-Type": "application\/json; charset=utf-8", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "x-zendesk-api-version": "v2", + "x-zendesk-application-version": "v17438", + "x-frame-options": "SAMEORIGIN", + "zendesk-rate-limit-tickets-index": "total=100; remaining=99; resets=57", + "x-rate-limit": "400", + "rate-limit": "400", + "x-rate-limit-remaining": "399", + "rate-limit-remaining": "399", + "rate-limit-reset": "57", + "strict-transport-security": "max-age=31536000;", + "etag": "W\/\"8cdd07d5c75ffd1b225da69dee7e9985\"", + "cache-control": "max-age=0, private, must-revalidate", + "x-zendesk-origin-server": "classic-app-server-6fc8db697c-cgrcj", + "set-cookie": [ + "_zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca; path=\/; expires=Wed, 03 Jul 2024 04:36:17 GMT; secure; HttpOnly; SameSite=None", + "__cfruid=96c8e6e12fd2ae1a1fb6295fcc3d95a8a1bb0890-1688383863; path=\/; domain=.codebarsolutionsag.zendesk.com; HttpOnly; Secure; SameSite=None" + ], + "x-request-id": [ + "7e0eccc609fa7692-LHR", + "7e0eccc609fa7692-LHR" + ], + "x-runtime": "0.124075", + "X-Zendesk-Zorg": "yes", + "CF-Cache-Status": "DYNAMIC", + "Report-To": "{\"endpoints\":[{\"url\":\"https:\\\/\\\/a.nel.cloudflare.com\\\/report\\\/v3?s=FrrIioe8zsDqyeop5JgV0cpPiCkfW62wcV7EiVd%2F0fYtWuHvzB5RW1ln%2FfyR4Zbuk7fIcfkLKjUQ8qj%2Fpi5htVqKXZJKY%2FMxVXu4YyHBaDW3pp6%2BIDFYAOAwYHwKH2cz1D0car5EemABOXu9BrdQog%3D%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}", + "NEL": "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}", + "Server": "cloudflare", + "CF-RAY": "7e0eccc609fa7692-LHR" + }, + "data": "{\"tickets\":[{\"url\":\"https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/tickets\/79.json\",\"id\":79,\"external_id\":null,\"via\":{\"channel\":\"api\",\"source\":{\"from\":{},\"to\":{},\"rel\":null}},\"created_at\":\"2023-07-01T12:50:02Z\",\"updated_at\":\"2023-07-01T13:05:54Z\",\"type\":null,\"subject\":\"Eine neue Anfrage der B\u00e4rtschi ist eingetroffen: Projekt #1.\",\"raw_subject\":\"Eine neue Anfrage der B\u00e4rtschi ist eingetroffen: Projekt #1.\",\"description\":\"Eine neue Anfrage der B\u00e4rtschi ist eingetroffen.\\n\\nAnfrage\\nIdentifikation 1\\nBemerkung asdasd\\nURL https:\/\/app.pv.test\/nova\/resources\/inquiries\/1\\nErstellt am 01.07.2023 14:21 Uhr\\n**Kunde**\\nKunden B\u00e4rtschi\\nKunden ID 437046\\nBenutzer ID 437046_ralph\\nBenutzername Ralph Senn\\n**Konfiguration**\\nName Projekt #1\\nDachform Sattel-\/Pultdach\\nMontage Aufdach\\nHerkunft Asiatisch\\nWechselrichter Typ String\\n**Adresse**\\nStrasse M\u00fchlematten 12\\nPLZ 4455\\nOrt Zunzgen\\nLand CH\",\"priority\":\"urgent\",\"status\":\"open\",\"recipient\":null,\"requester_id\":17145664265741,\"submitter_id\":17145664265741,\"assignee_id\":17145664265741,\"organization_id\":17145651654157,\"group_id\":17145664762125,\"collaborator_ids\":[],\"follower_ids\":[],\"email_cc_ids\":[],\"forum_topic_id\":null,\"problem_id\":null,\"has_incidents\":false,\"is_public\":true,\"due_at\":null,\"tags\":[],\"custom_fields\":[{\"id\":17146061349901,\"value\":null},{\"id\":17195718961677,\"value\":null},{\"id\":17195752153741,\"value\":null}],\"satisfaction_rating\":null,\"sharing_agreement_ids\":[],\"custom_status_id\":17145678334989,\"fields\":[{\"id\":17146061349901,\"value\":null},{\"id\":17195718961677,\"value\":null},{\"id\":17195752153741,\"value\":null}],\"followup_ids\":[],\"ticket_form_id\":17145664563725,\"brand_id\":17145651602957,\"allow_channelback\":false,\"allow_attachments\":true,\"from_messaging_channel\":false},{\"url\":\"https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/tickets\/81.json\",\"id\":81,\"external_id\":null,\"via\":{\"channel\":\"api\",\"source\":{\"from\":{},\"to\":{},\"rel\":null}},\"created_at\":\"2023-07-03T11:16:03Z\",\"updated_at\":\"2023-07-03T11:16:05Z\",\"type\":null,\"subject\":\"My printer is on fire!\",\"raw_subject\":\"My printer is on fire!\",\"description\":\"The smoke is very colorful.\",\"priority\":\"urgent\",\"status\":\"open\",\"recipient\":null,\"requester_id\":17145664265741,\"submitter_id\":17145664265741,\"assignee_id\":17145664265741,\"organization_id\":17145651654157,\"group_id\":17145664762125,\"collaborator_ids\":[],\"follower_ids\":[],\"email_cc_ids\":[],\"forum_topic_id\":null,\"problem_id\":null,\"has_incidents\":false,\"is_public\":true,\"due_at\":null,\"tags\":[],\"custom_fields\":[{\"id\":17146061349901,\"value\":null},{\"id\":17195718961677,\"value\":\"Check field works\"},{\"id\":17195752153741,\"value\":\"Check field works number 2\"}],\"satisfaction_rating\":null,\"sharing_agreement_ids\":[],\"custom_status_id\":17145678334989,\"fields\":[{\"id\":17146061349901,\"value\":null},{\"id\":17195718961677,\"value\":\"Check field works\"},{\"id\":17195752153741,\"value\":\"Check field works number 2\"}],\"followup_ids\":[],\"ticket_form_id\":17145664563725,\"brand_id\":17145651602957,\"allow_channelback\":false,\"allow_attachments\":true,\"from_messaging_channel\":false}],\"next_page\":null,\"previous_page\":null,\"count\":2}" +} diff --git a/tests/Fixtures/Saloon/count-tickets-request.json b/tests/Fixtures/Saloon/count-tickets-request.json new file mode 100644 index 0000000..ec31ab3 --- /dev/null +++ b/tests/Fixtures/Saloon/count-tickets-request.json @@ -0,0 +1 @@ +{"statusCode":200,"headers":{"Date":"Mon, 03 Jul 2023 13:19:53 GMT","Content-Type":"application\/json; charset=utf-8","Transfer-Encoding":"chunked","Connection":"keep-alive","x-zendesk-api-version":"v2","x-zendesk-application-version":"v17438","x-frame-options":"SAMEORIGIN","x-rate-limit":"400","rate-limit":"400","x-rate-limit-remaining":"399","rate-limit-remaining":"399","rate-limit-reset":"7","strict-transport-security":"max-age=31536000;","etag":"W\/\"dd5b53a4c6c7cd54d9e75cb4d51c3a8a\"","cache-control":"max-age=0, private, must-revalidate","x-zendesk-origin-server":"classic-app-server-6fc8db697c-msbrs","set-cookie":["_zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca; path=\/; expires=Wed, 03 Jul 2024 04:36:28 GMT; secure; HttpOnly; SameSite=None","__cfruid=0e155e55bd96d1d52b7c0b19c4c735ae1b2e0d93-1688390393; path=\/; domain=.codebarsolutionsag.zendesk.com; HttpOnly; Secure; SameSite=None"],"x-request-id":["7e0f6c36fb797735-LHR","7e0f6c36fb797735-LHR"],"x-runtime":"0.073519","X-Zendesk-Zorg":"yes","CF-Cache-Status":"DYNAMIC","Report-To":"{\"endpoints\":[{\"url\":\"https:\\\/\\\/a.nel.cloudflare.com\\\/report\\\/v3?s=bl0dv422vOkNLREKNLdaQy5qTMuOaeDMK0kHOBFaaHa6bdBul%2FWMn6KIhIC1GeWb8Z5cXJMcbSVQhZgHH4KjUiJYs%2FFPWcZQD7vYwvy7SWODBlcd6FSqwVYFnq8ghN8Vq6a45PZLHAy2EtL2BEFDqg%3D%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}","NEL":"{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}","Server":"cloudflare","CF-RAY":"7e0f6c36fb797735-LHR"},"data":"{\"count\":{\"value\":4,\"refreshed_at\":\"2023-07-03T13:19:53+00:00\"}}"} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/create-attachment-request.json b/tests/Fixtures/Saloon/create-attachment-request.json new file mode 100644 index 0000000..ad47a59 --- /dev/null +++ b/tests/Fixtures/Saloon/create-attachment-request.json @@ -0,0 +1,40 @@ +{ + "statusCode": 201, + "headers": { + "Date": "Mon, 03 Jul 2023 13:33:16 GMT", + "Content-Type": "application\/json; charset=utf-8", + "Content-Length": "2293", + "Connection": "keep-alive", + "x-zendesk-api-version": "v2", + "x-zendesk-application-version": "v17438", + "x-frame-options": "SAMEORIGIN", + "location": "https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/attachments\/17278923683597.json", + "access-control-allow-origin": "*", + "access-control-expose-headers": "X-Zendesk-API-Warn,X-Zendesk-User-Id,X-Zendesk-User-Session-Expires-At", + "x-rate-limit": "400", + "rate-limit": "400", + "x-rate-limit-remaining": "399", + "rate-limit-remaining": "399", + "rate-limit-reset": "44", + "strict-transport-security": "max-age=31536000;", + "etag": "W\/\"387b535ca927c3e885bc4ebf45675c64\"", + "cache-control": "max-age=0, private, must-revalidate", + "x-zendesk-origin-server": "classic-app-server-6fc8db697c-lbxfk", + "set-cookie": [ + "_zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca; path=\/; expires=Wed, 03 Jul 2024 07:05:39 GMT; secure; HttpOnly; SameSite=None", + "__cfruid=9a71e3de0932ad74b42e7fcf2ed091e67ad18b84-1688391196; path=\/; domain=.codebarsolutionsag.zendesk.com; HttpOnly; Secure; SameSite=None" + ], + "x-request-id": [ + "7e0f7fcbdbec7545-LHR", + "7e0f7fcbdbec7545-LHR" + ], + "x-runtime": "0.374089", + "X-Zendesk-Zorg": "yes", + "CF-Cache-Status": "DYNAMIC", + "Report-To": "{\"endpoints\":[{\"url\":\"https:\\\/\\\/a.nel.cloudflare.com\\\/report\\\/v3?s=TL6Q7BjsnUmgl8FO0vyKlypUJhEHM8ywbUzWFRH46niQLknkM8iDggX2mC7pWJJ8V8iBHBrGs0U8%2FGwAQEyClKMQMHTIDmcs%2Bl%2BlOH339vU0XQ%2FSAlPzIG1WkX6vs5k%2FCZCmES8PcDYJ0I44ZqeZIg%3D%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}", + "NEL": "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}", + "Server": "cloudflare", + "CF-RAY": "7e0f7fcbdbec7545-LHR" + }, + "data": "{\"upload\":{\"token\":\"OPvgMbfg5Der4DYn66hTC31in\",\"expires_at\":\"2023-07-06T13:33:15Z\",\"attachments\":[{\"url\":\"https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/attachments\/17278923683597.json\",\"id\":17278923683597,\"file_name\":\"test.png\",\"content_url\":\"https:\/\/codebarsolutionsag.zendesk.com\/attachments\/token\/52HCaRgRZM38MvaUuUQsYzhUA\/?name=test.png\",\"mapped_content_url\":\"https:\/\/codebarsolutionsag.zendesk.com\/attachments\/token\/52HCaRgRZM38MvaUuUQsYzhUA\/?name=test.png\",\"content_type\":\"image\/png\",\"size\":26271,\"width\":640,\"height\":360,\"inline\":false,\"deleted\":false,\"malware_access_override\":false,\"malware_scan_result\":\"not_scanned\",\"thumbnails\":[{\"url\":\"https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/attachments\/17278921757709.json\",\"id\":17278921757709,\"file_name\":\"test_thumb.png\",\"content_url\":\"https:\/\/codebarsolutionsag.zendesk.com\/attachments\/token\/gvLU5FyT7CoKakThdKaxHwVNg\/?name=test_thumb.png\",\"mapped_content_url\":\"https:\/\/codebarsolutionsag.zendesk.com\/attachments\/token\/gvLU5FyT7CoKakThdKaxHwVNg\/?name=test_thumb.png\",\"content_type\":\"image\/png\",\"size\":1823,\"width\":80,\"height\":45,\"inline\":false,\"deleted\":false,\"malware_access_override\":false,\"malware_scan_result\":\"not_scanned\"}]}],\"attachment\":{\"url\":\"https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/attachments\/17278923683597.json\",\"id\":17278923683597,\"file_name\":\"test.png\",\"content_url\":\"https:\/\/codebarsolutionsag.zendesk.com\/attachments\/token\/52HCaRgRZM38MvaUuUQsYzhUA\/?name=test.png\",\"mapped_content_url\":\"https:\/\/codebarsolutionsag.zendesk.com\/attachments\/token\/52HCaRgRZM38MvaUuUQsYzhUA\/?name=test.png\",\"content_type\":\"image\/png\",\"size\":26271,\"width\":640,\"height\":360,\"inline\":false,\"deleted\":false,\"malware_access_override\":false,\"malware_scan_result\":\"not_scanned\",\"thumbnails\":[{\"url\":\"https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/attachments\/17278921757709.json\",\"id\":17278921757709,\"file_name\":\"test_thumb.png\",\"content_url\":\"https:\/\/codebarsolutionsag.zendesk.com\/attachments\/token\/gvLU5FyT7CoKakThdKaxHwVNg\/?name=test_thumb.png\",\"mapped_content_url\":\"https:\/\/codebarsolutionsag.zendesk.com\/attachments\/token\/gvLU5FyT7CoKakThdKaxHwVNg\/?name=test_thumb.png\",\"content_type\":\"image\/png\",\"size\":1823,\"width\":80,\"height\":45,\"inline\":false,\"deleted\":false,\"malware_access_override\":false,\"malware_scan_result\":\"not_scanned\"}]}}}" +} diff --git a/tests/Fixtures/Saloon/create-single-ticket-request.json b/tests/Fixtures/Saloon/create-single-ticket-request.json new file mode 100644 index 0000000..237fda8 --- /dev/null +++ b/tests/Fixtures/Saloon/create-single-ticket-request.json @@ -0,0 +1 @@ +{"statusCode":201,"headers":{"Date":"Mon, 03 Jul 2023 13:16:35 GMT","Content-Type":"application\/json; charset=utf-8","Content-Length":"3197","Connection":"keep-alive","x-zendesk-api-version":"v2","x-zendesk-application-version":"v17438","x-zendesk-api-warn":"{:allowed_parameters=>{:controller=>\"tickets\", :action=>\"create\", :unpermitted_keys=>[\"ticket.allow_attachments\", \"ticket.allow_channelback\", \"ticket.comment.attachments\", \"ticket.comment.audit_id\", \"ticket.comment.created_at\", \"ticket.comment.id\", \"ticket.comment.metadata\", \"ticket.comment.plain_body\", \"ticket.comment.type\", \"ticket.comment.via\", \"ticket.created_at\", \"ticket.email_cc_ids\", \"ticket.follower_ids\", \"ticket.followup_ids\", \"ticket.from_messaging_channel\", \"ticket.has_incidents\", \"ticket.id\", \"ticket.is_public\", \"ticket.self_update\", \"ticket.satisfaction_rating\", \"ticket.updated_at\"], :invalid_values=>[]}}","x-frame-options":"SAMEORIGIN","zendesk-ep":"-22","location":"https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/tickets\/83.json","x-rate-limit":"400","rate-limit":"400","x-rate-limit-remaining":"399","rate-limit-remaining":"399","rate-limit-reset":"25","strict-transport-security":"max-age=31536000;","etag":"W\/\"3f19ce42212521a313aba3506dabd849\"","cache-control":"max-age=0, private, must-revalidate","x-zendesk-origin-server":"classic-app-server-6fc8db697c-vp95d","set-cookie":["_zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca; path=\/; expires=Wed, 03 Jul 2024 07:05:25 GMT; secure; HttpOnly; SameSite=None","__cfruid=212285535bde6012c576e4f7e5897005b752ea29-1688390195; path=\/; domain=.codebarsolutionsag.zendesk.com; HttpOnly; Secure; SameSite=None"],"x-request-id":["7e0f675b4829d168-LHR","7e0f675b4829d168-LHR"],"x-runtime":"0.490684","X-Zendesk-Zorg":"yes","CF-Cache-Status":"DYNAMIC","Report-To":"{\"endpoints\":[{\"url\":\"https:\\\/\\\/a.nel.cloudflare.com\\\/report\\\/v3?s=WhPNGryN2lnTT63PGIv4jkoAL%2BCJfcUd9U1L%2B%2F6RBPPdiAz7EXz0y4Oj84xA9dyzvnJHh4SVHLzJ9asKpj7DJczwb9mXhXY5Q8BverBn44Bu9RmF8gitXsMkSpAbWkL9gBwsgUTzlEcW4sPwEYwwSw%3D%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}","NEL":"{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}","Server":"cloudflare","CF-RAY":"7e0f675b4829d168-LHR"},"data":"{\"ticket\":{\"url\":\"https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/tickets\/83.json\",\"id\":83,\"external_id\":null,\"via\":{\"channel\":\"api\",\"source\":{\"from\":{},\"to\":{},\"rel\":null}},\"created_at\":\"2023-07-03T13:16:35Z\",\"updated_at\":\"2023-07-03T13:16:35Z\",\"type\":null,\"subject\":\"My printer is on fire!\",\"raw_subject\":\"My printer is on fire!\",\"description\":\"The smoke is very colorful.\",\"priority\":\"urgent\",\"status\":\"open\",\"recipient\":null,\"requester_id\":17145664265741,\"submitter_id\":17145664265741,\"assignee_id\":17145664265741,\"organization_id\":17145651654157,\"group_id\":17145664762125,\"collaborator_ids\":[],\"follower_ids\":[],\"email_cc_ids\":[],\"forum_topic_id\":null,\"problem_id\":null,\"has_incidents\":false,\"is_public\":true,\"due_at\":null,\"tags\":[],\"custom_fields\":[{\"id\":17146061349901,\"value\":null},{\"id\":17195718961677,\"value\":\"Check field works\"},{\"id\":17195752153741,\"value\":\"Check field works number 2\"}],\"satisfaction_rating\":null,\"sharing_agreement_ids\":[],\"custom_status_id\":17145678334989,\"fields\":[{\"id\":17146061349901,\"value\":null},{\"id\":17195718961677,\"value\":\"Check field works\"},{\"id\":17195752153741,\"value\":\"Check field works number 2\"}],\"followup_ids\":[],\"ticket_form_id\":17145664563725,\"brand_id\":17145651602957,\"allow_channelback\":false,\"allow_attachments\":true,\"from_messaging_channel\":false},\"audit\":{\"id\":17278400133517,\"ticket_id\":83,\"created_at\":\"2023-07-03T13:16:35Z\",\"author_id\":17145664265741,\"metadata\":{\"system\":{\"client\":\"GuzzleHttp\/7\",\"ip_address\":\"94.101.151.181\",\"location\":\"Herne Bay, ENG, United Kingdom\",\"latitude\":51.3724,\"longitude\":1.1561},\"custom\":{}},\"events\":[{\"id\":17278400133645,\"type\":\"Comment\",\"author_id\":17145664265741,\"body\":\"The smoke is very colorful.\",\"html_body\":\"

The smoke is very colorful.<\/p><\/div>\",\"plain_body\":\"The smoke is very colorful.\",\"public\":true,\"attachments\":[],\"audit_id\":17278400133517},{\"id\":17278400133773,\"type\":\"Create\",\"value\":\"urgent\",\"field_name\":\"priority\"},{\"id\":17278400133901,\"type\":\"Create\",\"value\":\"My printer is on fire!\",\"field_name\":\"subject\"},{\"id\":17278400134029,\"type\":\"Create\",\"value\":\"17145664265741\",\"field_name\":\"requester_id\"},{\"id\":17278400134157,\"type\":\"Create\",\"value\":\"Check field works\",\"field_name\":\"17195718961677\"},{\"id\":17278400134285,\"type\":\"Create\",\"value\":\"Check field works number 2\",\"field_name\":\"17195752153741\"},{\"id\":17278400134413,\"type\":\"Create\",\"value\":null,\"field_name\":\"type\"},{\"id\":17278400134541,\"type\":\"Create\",\"value\":\"open\",\"field_name\":\"status\"},{\"id\":17278400134669,\"type\":\"Create\",\"value\":\"17145664265741\",\"field_name\":\"assignee_id\"},{\"id\":17278400134797,\"type\":\"Create\",\"value\":\"17145664762125\",\"field_name\":\"group_id\"},{\"id\":17278400134925,\"type\":\"Create\",\"value\":\"17145651654157\",\"field_name\":\"organization_id\"},{\"id\":17278400135053,\"type\":\"Create\",\"value\":\"17145651602957\",\"field_name\":\"brand_id\"},{\"id\":17278400135181,\"type\":\"Create\",\"value\":\"17145664563725\",\"field_name\":\"ticket_form_id\"},{\"id\":17278400135309,\"type\":\"Create\",\"value\":\"17145678334989\",\"field_name\":\"custom_status_id\"},{\"id\":17278414083597,\"type\":\"AssociateAttValsEvent\",\"attribute_values\":[]}],\"via\":{\"channel\":\"api\",\"source\":{\"from\":{},\"to\":{},\"rel\":null}}}}"} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/details-incorrect-request.json b/tests/Fixtures/Saloon/details-incorrect-request.json new file mode 100644 index 0000000..b0205e8 --- /dev/null +++ b/tests/Fixtures/Saloon/details-incorrect-request.json @@ -0,0 +1,28 @@ +{ + "statusCode": 404, + "headers": { + "Date": "Tue, 08 Aug 2023 13:18:12 GMT", + "Content-Type": "application\/json; charset=utf-8", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "strict-transport-security": "max-age=0;", + "cache-control": "no-cache", + "x-zendesk-origin-server": "classic-app-server-759558d96c-8dfnl", + "x-request-id": [ + "7f380b3dda7803b9-LHR", + "7f380b3dda7803b9-LHR" + ], + "x-runtime": "0.022401", + "X-Zendesk-Zorg": "yes", + "CF-Cache-Status": "DYNAMIC", + "Set-Cookie": [ + "__cf_bm=oWgCGqj6tmJCV2tMr88g2GQEQAgQMOP2NxPrl.BGPGU-1691500692-0-AbZ4KZWi7MBVu5Kwo+FdPjZadDlf45E5fnGBMQ178FSegJupgGFsza+v0R5EOfn\/1YjMO58G5tROfJBVKC3QVG1QGgKuQLo\/LGyTrHwPfFdu; path=\/; expires=Tue, 08-Aug-23 13:48:12 GMT; domain=.zendesk.com; HttpOnly; Secure; SameSite=None", + "__cfruid=5549c1de53c611e745835a36b2ba231e201d856d-1691500692; path=\/; domain=.zendesk.com; HttpOnly; Secure; SameSite=None" + ], + "Report-To": "{\"endpoints\":[{\"url\":\"https:\\\/\\\/a.nel.cloudflare.com\\\/report\\\/v3?s=uwn7TtPaAUhwgPUZpwt32SiUOotc6%2FpIY4bkdsZzJUSM3ARWKjcnoi8yji1Yw34Rgx5Sd6t57EEqr29B8CKIgUywW2OtO6FIApa1%2BmjX5nmbn8ZX6Nm1I3RmUfny03lM2xuQdkTa2%2BL5pkuW9prNT8UQKCzS\"}],\"group\":\"cf-nel\",\"max_age\":604800}", + "NEL": "{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}", + "Server": "cloudflare", + "CF-RAY": "7f380b3dda7803b9-LHR" + }, + "data": "{\n \"error\": {\n \"title\": \"No help desk at codebarsolutionsagwrong.zendesk.com\",\n \"message\": \"There is no help desk configured at this address. This means that the address is available and that you can claim it at http:\/\/www.zendesk.com\/signup\"\n }\n}\n" +} diff --git a/tests/Fixtures/Saloon/single-ticket-request.json b/tests/Fixtures/Saloon/single-ticket-request.json new file mode 100644 index 0000000..7ec8913 --- /dev/null +++ b/tests/Fixtures/Saloon/single-ticket-request.json @@ -0,0 +1,37 @@ +{ + "statusCode": 200, + "headers": { + "Date": "Mon, 03 Jul 2023 11:31:04 GMT", + "Content-Type": "application\/json; charset=utf-8", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "x-zendesk-api-version": "v2", + "x-zendesk-application-version": "v17438", + "x-frame-options": "SAMEORIGIN", + "x-rate-limit": "400", + "rate-limit": "400", + "x-rate-limit-remaining": "398", + "rate-limit-remaining": "398", + "rate-limit-reset": "56", + "strict-transport-security": "max-age=31536000;", + "etag": "W\/\"f6a375c66420a3a7c52a087aa0f3bb47\"", + "cache-control": "max-age=0, private, must-revalidate", + "x-zendesk-origin-server": "classic-app-server-6fc8db697c-js56h", + "set-cookie": [ + "_zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca; path=\/; expires=Wed, 03 Jul 2024 07:05:41 GMT; secure; HttpOnly; SameSite=None", + "__cfruid=b15e262f7b806ba2db1221699a831cc39e74cd44-1688383864; path=\/; domain=.codebarsolutionsag.zendesk.com; HttpOnly; Secure; SameSite=None" + ], + "x-request-id": [ + "7e0ecccb0ef7414d-LHR", + "7e0ecccb0ef7414d-LHR" + ], + "x-runtime": "0.086786", + "X-Zendesk-Zorg": "yes", + "CF-Cache-Status": "DYNAMIC", + "Report-To": "{\"endpoints\":[{\"url\":\"https:\\\/\\\/a.nel.cloudflare.com\\\/report\\\/v3?s=7lMEsubYh03mZhbyhOB7legoUvkomiPxbsQOg0HD6GL0OxkjWkOR0%2BYeSVdaW9J2uI%2FTZdGSdesx8628LQHv%2BSSghfw776Er2QMrCaswrMMD6WyL4RTiVF5m%2F20KZSDfwoR4VeTsATquzrBuUeD0Gg%3D%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}", + "NEL": "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}", + "Server": "cloudflare", + "CF-RAY": "7e0ecccb0ef7414d-LHR" + }, + "data": "{\"ticket\":{\"url\":\"https:\/\/codebarsolutionsag.zendesk.com\/api\/v2\/tickets\/81.json\",\"id\":81,\"external_id\":null,\"via\":{\"channel\":\"api\",\"source\":{\"from\":{},\"to\":{},\"rel\":null}},\"created_at\":\"2023-07-03T11:16:03Z\",\"updated_at\":\"2023-07-03T11:16:05Z\",\"type\":null,\"subject\":\"My printer is on fire!\",\"raw_subject\":\"My printer is on fire!\",\"description\":\"The smoke is very colorful.\",\"priority\":\"urgent\",\"status\":\"open\",\"recipient\":null,\"requester_id\":17145664265741,\"submitter_id\":17145664265741,\"assignee_id\":17145664265741,\"organization_id\":17145651654157,\"group_id\":17145664762125,\"collaborator_ids\":[],\"follower_ids\":[],\"email_cc_ids\":[],\"forum_topic_id\":null,\"problem_id\":null,\"has_incidents\":false,\"is_public\":true,\"due_at\":null,\"tags\":[],\"custom_fields\":[{\"id\":17146061349901,\"value\":null},{\"id\":17195718961677,\"value\":\"Check field works\"},{\"id\":17195752153741,\"value\":\"Check field works number 2\"}],\"satisfaction_rating\":null,\"sharing_agreement_ids\":[],\"custom_status_id\":17145678334989,\"fields\":[{\"id\":17146061349901,\"value\":null},{\"id\":17195718961677,\"value\":\"Check field works\"},{\"id\":17195752153741,\"value\":\"Check field works number 2\"}],\"followup_ids\":[],\"ticket_form_id\":17145664563725,\"brand_id\":17145651602957,\"allow_channelback\":false,\"allow_attachments\":true,\"from_messaging_channel\":false}}" +} diff --git a/tests/Pest.php b/tests/Pest.php index cc8b9d4..b07669c 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -8,3 +8,12 @@ uses()->beforeEach(function () { Event::fake(); })->in(__DIR__); + +uses()->beforeEach(function () { + config([ + 'zendesk.subdomain' => 'subdomain', + 'zendesk.auth.method' => 'token', + 'zendesk.auth.email_address' => 'test@example.com', + 'zendesk.auth.api_token' => 'token', + ]); +})->in(__DIR__.'/Requests'); diff --git a/tests/Requests/AllTicketsRequestTest.php b/tests/Requests/AllTicketsRequestTest.php new file mode 100644 index 0000000..c64683f --- /dev/null +++ b/tests/Requests/AllTicketsRequestTest.php @@ -0,0 +1,42 @@ + MockResponse::fixture('all-tickets-request'), + ]); + + $connector = new ZendeskConnector; + $connector->withMockClient($mockClient); + + $response = $connector->send(new AllTicketsRequest()); + + $mockClient->assertSent(AllTicketsRequest::class); + + expect($response->dto()->count)->toBe(2) + ->and($response->dto()->tickets[1]['id'])->toBe(81) + ->and($response->dto()->tickets[1]['subject'])->toBe('My printer is on fire!') + ->and($response->dto()->tickets[1]['raw_subject'])->toBe('My printer is on fire!') + ->and($response->dto()->tickets[1]['description'])->toBe('The smoke is very colorful.') + ->and($response->dto()->tickets[1]['priority'])->toBe(TicketPriority::URGENT) + ->and($response->dto()->tickets[1]['custom_fields'][1]['id'])->toBe(17195718961677) + ->and($response->dto()->tickets[1]['custom_fields'][1]['value'])->toBe('Check field works') + ->and($response->dto()->tickets[1]['custom_fields'][2]['id'])->toBe(17195752153741) + ->and($response->dto()->tickets[1]['custom_fields'][2]['value'])->toBe('Check field works number 2') + ->and($response->dto()->tickets[0]['id'])->toBe(79) + ->and($response->dto()->tickets[0]['subject'])->toBe('Eine neue Anfrage der Bärtschi ist eingetroffen: Projekt #1.') + ->and($response->dto()->tickets[0]['raw_subject'])->toBe('Eine neue Anfrage der Bärtschi ist eingetroffen: Projekt #1.') + ->and($response->dto()->tickets[0]['description']) + ->toBe("Eine neue Anfrage der Bärtschi ist eingetroffen.\n\nAnfrage\nIdentifikation 1\nBemerkung asdasd\nURL https://app.pv.test/nova/resources/inquiries/1\nErstellt am 01.07.2023 14:21 Uhr\n**Kunde**\nKunden Bärtschi\nKunden ID 437046\nBenutzer ID 437046_ralph\nBenutzername Ralph Senn\n**Konfiguration**\nName Projekt #1\nDachform Sattel-/Pultdach\nMontage Aufdach\nHerkunft Asiatisch\nWechselrichter Typ String\n**Adresse**\nStrasse Mühlematten 12\nPLZ 4455\nOrt Zunzgen\nLand CH") + ->and($response->dto()->tickets[0]['priority'])->toBe(TicketPriority::URGENT) + ->and($response->dto()->tickets[0]['custom_fields'][1]['id'])->toBe(17195718961677) + ->and($response->dto()->tickets[0]['custom_fields'][1]['value'])->toBe(null) + ->and($response->dto()->tickets[0]['custom_fields'][2]['id'])->toBe(17195752153741) + ->and($response->dto()->tickets[0]['custom_fields'][2]['value'])->toBe(null); + +}); diff --git a/tests/Requests/CountTicketsRequestTest.php b/tests/Requests/CountTicketsRequestTest.php new file mode 100644 index 0000000..86feb66 --- /dev/null +++ b/tests/Requests/CountTicketsRequestTest.php @@ -0,0 +1,22 @@ + MockResponse::fixture('count-tickets-request'), + ]); + + $connector = new ZendeskConnector; + $connector->withMockClient($mockClient); + + $response = $connector->send(new CountTicketsRequest()); + + $mockClient->assertSent(CountTicketsRequest::class); + + expect($response->dto()->value)->toBe(4) + ->and($response->dto()->refreshed_at->toDateTimeString())->toBe('2023-07-03 13:19:53'); +}); diff --git a/tests/Requests/CreateAttachmentRequestTest.php b/tests/Requests/CreateAttachmentRequestTest.php new file mode 100644 index 0000000..0c7a590 --- /dev/null +++ b/tests/Requests/CreateAttachmentRequestTest.php @@ -0,0 +1,33 @@ + MockResponse::fixture('create-attachment-request'), + ]); + + $connector = new ZendeskConnector; + $connector->withMockClient($mockClient); + + $response = $connector->send( + new CreateAttachmentRequest( + fileName: 'test.png', + mimeType: 'image/png', + stream: fopen(__DIR__.'/../Fixtures/Files/test.png', 'r') + ) + ); + + $mockClient->assertSent(CreateAttachmentRequest::class); + + expect($response->dto()->token)->toBe('OPvgMbfg5Der4DYn66hTC31in') + ->and($response->dto()->attachment->content_type)->toBe('image/png') + ->and($response->dto()->attachment->size)->toBe(26271) + ->and($response->dto()->attachment->file_name)->toBe('test.png') + ->and($response->dto()->attachment->width)->toBe('640') + ->and($response->dto()->attachment->height)->toBe('360') + ->and($response->dto()->attachment->content_url)->toBe('https://codebarsolutionsag.zendesk.com/attachments/token/52HCaRgRZM38MvaUuUQsYzhUA/?name=test.png'); +}); diff --git a/tests/Requests/CreateSingleTicketRequestTest.php b/tests/Requests/CreateSingleTicketRequestTest.php new file mode 100644 index 0000000..22e8c43 --- /dev/null +++ b/tests/Requests/CreateSingleTicketRequestTest.php @@ -0,0 +1,46 @@ + MockResponse::fixture('create-single-ticket-request'), + ]); + + $connector = new ZendeskConnector; + $connector->withMockClient($mockClient); + + $response = $connector->send(new CreateSingleTicketRequest([ + 'comment' => CommentDTO::fromArray([ + 'body' => 'The smoke is very colorful.', + ]), + 'priority' => TicketPriority::URGENT, + 'subject' => 'My printer is on fire!', + 'custom_fields' => [ + [ + 'id' => 17195718961677, + 'value' => 'Check field works', + ], + [ + 'id' => 17195752153741, + 'value' => 'Check field works number 2', + ], + ], + ])); + + $mockClient->assertSent(CreateSingleTicketRequest::class); + + expect($response->dto()->subject)->toBe('My printer is on fire!') + ->and($response->dto()->raw_subject)->toBe('My printer is on fire!') + ->and($response->dto()->description)->toBe('The smoke is very colorful.') + ->and($response->dto()->priority)->toBe(TicketPriority::URGENT) + ->and($response->dto()->custom_fields[1]['id'])->toBe(17195718961677) + ->and($response->dto()->custom_fields[1]['value'])->toBe('Check field works') + ->and($response->dto()->custom_fields[2]['id'])->toBe(17195752153741) + ->and($response->dto()->custom_fields[2]['value'])->toBe('Check field works number 2'); +}); diff --git a/tests/Requests/SingleTicketRequestTest.php b/tests/Requests/SingleTicketRequestTest.php new file mode 100644 index 0000000..23c32f7 --- /dev/null +++ b/tests/Requests/SingleTicketRequestTest.php @@ -0,0 +1,30 @@ + MockResponse::fixture('single-ticket-request'), + ]); + + $connector = new ZendeskConnector; + $connector->withMockClient($mockClient); + + $response = $connector->send(new SingleTicketRequest(81)); + + $mockClient->assertSent(SingleTicketRequest::class); + + expect($response->dto()->id)->toBe(81) + ->and($response->dto()->subject)->toBe('My printer is on fire!') + ->and($response->dto()->raw_subject)->toBe('My printer is on fire!') + ->and($response->dto()->description)->toBe('The smoke is very colorful.') + ->and($response->dto()->priority)->toBe(TicketPriority::URGENT) + ->and($response->dto()->custom_fields[1]['id'])->toBe(17195718961677) + ->and($response->dto()->custom_fields[1]['value'])->toBe('Check field works') + ->and($response->dto()->custom_fields[2]['id'])->toBe(17195752153741) + ->and($response->dto()->custom_fields[2]['value'])->toBe('Check field works number 2'); +}); diff --git a/tests/TestCase.php b/tests/TestCase.php index 6b100a4..a4319ac 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -5,6 +5,7 @@ use CodebarAg\Zendesk\ZendeskServiceProvider; use Illuminate\Database\Eloquent\Factories\Factory; use Orchestra\Testbench\TestCase as Orchestra; +use Spatie\LaravelData\Support\DataConfig; class TestCase extends Orchestra { @@ -15,6 +16,11 @@ protected function setUp(): void Factory::guessFactoryNamesUsing( fn (string $modelName) => 'CodebarAg\\Zendesk\\Database\\Factories\\'.class_basename($modelName).'Factory' ); + + // Provide a config array to DataConfig + $this->app->when(DataConfig::class) + ->needs('$config') + ->give([]); } protected function getPackageProviders($app): array