Passed
Push — master ( d4c44b...76f0fa )
by Chris
01:53
created

PostmarkCourier   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 249
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 100
dl 0
loc 249
ccs 97
cts 97
cp 1
rs 10
c 0
b 0
f 0
wmc 26

12 Methods

Rating   Name   Duplication   Size   Complexity  
A buildTemplateData() 0 8 1
A buildHeaders() 0 9 2
A buildRecipients() 0 5 1
A sendNonTemplateEmail() 0 30 5
A supportsContent() 0 9 3
A supportedContent() 0 6 1
A __construct() 0 4 2
A buildReplyTo() 0 13 2
A sendTemplateEmail() 0 22 2
A logError() 0 8 1
A buildAttachments() 0 10 1
A deliver() 0 29 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Camuthig\Courier\Postmark;
6
7
use Courier\ConfirmingCourier;
8
use Courier\Exceptions\TransmissionException;
9
use Courier\Exceptions\UnsupportedContentException;
10
use Courier\SavesReceipts;
11
use PhpEmail\Address;
12
use PhpEmail\Attachment;
13
use PhpEmail\Content;
14
use PhpEmail\Email;
15
use Postmark\Models\DynamicResponseModel;
16
use Postmark\Models\PostmarkException;
17
use Postmark\PostmarkClient;
18
use Psr\Log\LoggerInterface;
19
use Psr\Log\NullLogger;
20
21
class PostmarkCourier implements ConfirmingCourier
22
{
23
    use SavesReceipts;
24
25
    /**
26
     * @var PostmarkClient
27
     */
28
    private $client;
29
30
    /**
31
     * @var LoggerInterface
32
     */
33
    private $logger;
34
35
    /**
36
     * @param PostmarkClient       $client
37
     * @param LoggerInterface|null $logger
38
     */
39 6
    public function __construct(PostmarkClient $client, LoggerInterface $logger = null)
40
    {
41 6
        $this->client = $client;
42 6
        $this->logger = $logger ?: new NullLogger();
43
    }
44
45
    /**
46
     * @return array
47
     */
48 6
    protected function supportedContent(): array
49
    {
50
        return [
51 6
            Content\EmptyContent::class,
52
            Content\Contracts\SimpleContent::class,
53
            Content\Contracts\TemplatedContent::class,
54
        ];
55
    }
56
57
    /**
58
     * Determine if the content is supported by this courier.
59
     *
60
     * @param Content $content
61
     *
62
     * @return bool
63
     */
64 6
    protected function supportsContent(Content $content): bool
65
    {
66 6
        foreach ($this->supportedContent() as $contentType) {
67 6
            if ($content instanceof $contentType) {
68 6
                return true;
69
            }
70
        }
71
72 1
        return false;
73
    }
74
75
    /**
76
     * @param Email $email
77
     *
78
     * @throws TransmissionException
79
     * @throws UnsupportedContentException
80
     *
81
     * @return void
82
     */
83 6
    public function deliver(Email $email): void
84
    {
85 6
        $content = $email->getContent();
86
87 6
        if (!$this->supportsContent($content)) {
88 1
            throw new UnsupportedContentException($content);
89
        }
90
91
        switch (true) {
92 5
            case $content instanceof Content\TemplatedContent:
93 2
                $response = $this->sendTemplateEmail($email);
94 1
                break;
95
96 3
            case $content instanceof Content\SimpleContent:
97 1
                $response = $this->sendNonTemplateEmail($email);
98 1
                break;
99
100 2
            case $content instanceof Content\EmptyContent:
101 2
                $response = $this->sendNonTemplateEmail($email);
102 1
                break;
103
104
            default:
105
                // Should never get here
106
                // @codeCoverageIgnoreStart
107
                throw new UnsupportedContentException($content);
108
                // @codeCoverageIgnoreEnd
109
        }
110
111 3
        $this->saveReceipt($email, $response['MessageID']);
112
    }
113
114
    /**
115
     * @param Email $email
116
     *
117
     * @return DynamicResponseModel
118
     */
119 2
    protected function sendTemplateEmail(Email $email): DynamicResponseModel
120
    {
121
        try {
122 2
            return $this->client->sendEmailWithTemplate(
123 2
                $email->getFrom()->toRfc2822(),
124 2
                $this->buildRecipients(...$email->getToRecipients()),
125 2
                (int) $email->getContent()->getTemplateId(),
0 ignored issues
show
Bug introduced by
The method getTemplateId() does not exist on PhpEmail\Content. It seems like you code against a sub-type of PhpEmail\Content such as PhpEmail\Content\Contracts\TemplatedContent. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
                (int) $email->getContent()->/** @scrutinizer ignore-call */ getTemplateId(),
Loading history...
126 2
                $this->buildTemplateData($email),
127 2
                false,
128 2
                null,
129 2
                true,
130 2
                $this->buildReplyTo($email),
131 2
                $this->buildRecipients(...$email->getCcRecipients()),
132 2
                $this->buildRecipients(...$email->getBccRecipients()),
133 2
                $this->buildHeaders($email),
134 2
                $this->buildAttachments($email),
135 2
                null
136
            );
137 1
        } catch (PostmarkException $pe) {
138 1
            $this->logError($pe);
139
140 1
            throw new TransmissionException($pe->postmarkApiErrorCode, $pe);
141
        }
142
    }
143
144
    /**
145
     * @param Email $email
146
     *
147
     * @return DynamicResponseModel
148
     */
149 3
    protected function sendNonTemplateEmail(Email $email): DynamicResponseModel
150
    {
151 3
        $content     = $email->getContent();
152 3
        $htmlContent = 'No message';
153 3
        $textContent = 'No message';
154 3
        if ($content instanceof Content\Contracts\SimpleContent) {
155 1
            $htmlContent = $content->getHtml() !== null ? $content->getHtml()->getBody() : null;
156 1
            $textContent = $content->getText() !== null ? $content->getText()->getBody() : null;
157
        }
158
159
        try {
160 3
            return $this->client->sendEmail(
161 3
                $email->getFrom()->toRfc2822(),
162 3
                $this->buildRecipients(...$email->getToRecipients()),
163 3
                $email->getSubject(),
164 3
                $htmlContent,
165 3
                $textContent,
166 3
                null,
167 3
                true,
168 3
                $this->buildReplyTo($email),
169 3
                $this->buildRecipients(...$email->getCcRecipients()),
170 3
                $this->buildRecipients(...$email->getBccRecipients()),
171 3
                $this->buildHeaders($email),
172 3
                $this->buildAttachments($email),
173 3
                null
174
            );
175 1
        } catch (PostmarkException $pe) {
176 1
            $this->logError($pe);
177
178 1
            throw new TransmissionException($pe->postmarkApiErrorCode ?? 500, $pe);
179
        }
180
    }
181
182 5
    protected function buildReplyTo(Email $email): ?string
183
    {
184
        /** @var Address|null $replyTo */
185 5
        $replyTo = null;
186
187 5
        if (!empty($email->getReplyTos())) {
188
            // The Postmark API only supports one "Reply To"
189 2
            $replyTos = $email->getReplyTos();
190 2
            $replyTo  = reset($replyTos);
191 2
            $replyTo  = $replyTo->toRfc2822();
192
        }
193
194 5
        return $replyTo;
195
    }
196
197
    /**
198
     * @param Address[] $addresses
199
     *
200
     * @return string
201
     */
202
    protected function buildRecipients(Address ...$addresses): string
203
    {
204 5
        return implode(',', array_map(function (Address $address) {
205 5
            return $address->toRfc2822();
206 5
        }, $addresses));
207
    }
208
209
    /**
210
     * @param Email $email
211
     *
212
     * @return array
213
     */
214
    protected function buildAttachments(Email $email): array
215
    {
216 5
        return array_map(function (Attachment $attachment) {
217
            return [
218 2
                'Name'        => $attachment->getName(),
219 2
                'Content'     => $attachment->getBase64Content(),
220 2
                'ContentType' => $attachment->getRfc2822ContentType(),
221 2
                'ContentID'   => $attachment->getContentId(),
222
            ];
223 5
        }, array_merge($email->getAttachments(), $email->getEmbedded()));
224
    }
225
226
    /**
227
     * @param Email $email
228
     *
229
     * @return array
230
     */
231 5
    protected function buildHeaders(Email $email): array
232
    {
233 5
        $headers = [];
234
235 5
        foreach ($email->getHeaders() as $header) {
236 2
            $headers[$header->getField()] = $header->getValue();
237
        }
238
239 5
        return $headers;
240
    }
241
242
    /**
243
     * @param Email $email
244
     *
245
     * @return array
246
     */
247 2
    protected function buildTemplateData(Email $email): array
248
    {
249 2
        $data = $email->getContent()->getTemplateData();
0 ignored issues
show
Bug introduced by
The method getTemplateData() does not exist on PhpEmail\Content. It seems like you code against a sub-type of PhpEmail\Content such as PhpEmail\Content\Contracts\TemplatedContent. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

249
        $data = $email->getContent()->/** @scrutinizer ignore-call */ getTemplateData();
Loading history...
250
251
        // Add the subject from the email for dynamic replacement
252 2
        $data['subject'] = $email->getSubject();
253
254 2
        return $data;
255
    }
256
257
    /**
258
     * @param PostmarkException $pe
259
     *
260
     * @return void
261
     */
262 2
    protected function logError(PostmarkException $pe): void
263
    {
264 2
        $this->logger->error(
265 2
            'Received status {httpCode} and API code {apiCode} from Postmark with message: {message}',
266
            [
267 2
                'httpCode' => $pe->httpStatusCode,
268 2
                'apiCode'  => $pe->postmarkApiErrorCode,
269 2
                'message'  => $pe->message,
270
            ]
271
        );
272
    }
273
}
274