Passed
Push — master ( 03a8f7...7b2ab5 )
by Gabriel
12:52
created

SendgridRestTransport::sendApiCall()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 13
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 18
rs 9.8333
1
<?php
2
declare(strict_types=1);
3
4
namespace Nip\Mail\Transport;
5
6
use Exception;
7
use Nip\Mail\Message;
8
use Psr\EventDispatcher\EventDispatcherInterface;
9
use Psr\Log\LoggerInterface;
10
use SendGrid;
11
use SendGrid\Mail\Attachment;
12
use SendGrid\Mail\Content;
13
use SendGrid\Mail\Mail;
14
use SendGrid\Mail\Personalization;
15
use SendGrid\Mail\To;
16
use Symfony\Component\Mailer\Exception\TransportException;
17
use Symfony\Component\Mailer\SentMessage;
18
use Symfony\Component\Mailer\Transport\AbstractTransport;
19
use Symfony\Component\Mime\Part\DataPart;
20
21
/**
22
 * Class SendgridTransport
23
 * @package Nip\Mail\Transport
24
 */
25
class SendgridRestTransport extends AbstractTransport
26
{
27
    /** @var string|null */
28
    protected ?string $apiKey;
29
30
    public function __construct(EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
31
    {
32
        parent::__construct($dispatcher, $logger);
33
    }
34
35
    /**
36
     * @return null|string
37
     */
38
    public function getApiKey(): ?string
39
    {
40
        return $this->apiKey;
41
    }
42
43
    /**
44
     * @throws SendGrid\Mail\TypeException
45
     */
46
    protected function doSend(SentMessage $message): void
47
    {
48
        $mail = new Mail();
49
50
        $message = $message->getOriginalMessage();
51
52
        /** @var Message $message */
53
        $this->populateSenders($message, $mail);
54
        $this->populatePersonalization($message, $mail);
55
        $this->populateContent($message, $mail);
56
        $this->populateCustomArg($message, $mail);
57
58
        $this->sendApiCall($mail);
59
    }
60
61
62
    /**
63
     * @param \Symfony\Component\Mime\Message $message
64
     * @param Mail $mail
65
     * @throws SendGrid\Mail\TypeException
66
     */
67
    protected function populateSenders(\Symfony\Component\Mime\Message $message, Mail $mail)
68
    {
69
        $addresses = $message->getFrom();
0 ignored issues
show
introduced by
The method getFrom() does not exist on Symfony\Component\Mime\Message. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

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

69
        /** @scrutinizer ignore-call */ 
70
        $addresses = $message->getFrom();
Loading history...
70
        foreach ($addresses as $address) {
71
            $mail->setFrom($address->getAddress(), $address->getName());
72
            $mail->setReplyTo($address->getAddress(), $address->getName());
73
        }
74
        $reply = $message->getReplyTo();
0 ignored issues
show
introduced by
The method getReplyTo() does not exist on Symfony\Component\Mime\Message. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

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

74
        /** @scrutinizer ignore-call */ 
75
        $reply = $message->getReplyTo();
Loading history...
75
        foreach ($reply as $address) {
76
            $mail->setReplyTo($address->getAddress(), $address->getName());
77
        }
78
    }
79
80
    /**
81
     * @param Message $message
82
     * @param Mail $mail
83
     * @throws SendGrid\Mail\TypeException
84
     */
85
    protected function populatePersonalization(Message $message, Mail $mail)
86
    {
87
        $emailsTos = $message->getTo();
88
        $personalizationIndex = 0;
89
        foreach ($emailsTos as $emailTo => $nameTo) {
90
            $personalization = $this->generatePersonalization($emailTo, $nameTo, $message, $personalizationIndex);
91
            $mail->addPersonalization($personalization);
92
            $personalizationIndex++;
93
        }
94
    }
95
96
    /**
97
     * @param $emailTo
98
     * @param $nameTo
99
     * @param Message $message
100
     * @param integer $i
101
     * @return Personalization
102
     * @throws SendGrid\Mail\TypeException
103
     */
104
    protected function generatePersonalization($emailTo, $nameTo, Message $message, $i): Personalization
105
    {
106
        $personalization = new Personalization();
107
108
        $email = new To($emailTo, $nameTo);
109
        $personalization->addTo($email);
110
111
        $personalization->setSubject($message->getSubject());
112
113
        $mergeTags = $message->getMergeTags();
114
        foreach ($mergeTags as $varKey => $value) {
115
            if (is_array($value)) {
116
                $value = $value[$i];
117
            }
118
            $value = (string)$value;
119
            $personalization->addSubstitution('{{' . $varKey . '}}', $value);
120
        }
121
122
        return $personalization;
123
    }
124
125
    /**
126
     * @param Message $message
127
     * @param Mail $mail
128
     * @throws SendGrid\Mail\TypeException
129
     */
130
    protected function populateContent(Message $message, Mail $mail)
131
    {
132
        foreach ($message->getAttachments() as $attachment) {
133
            $this->addAttachment($attachment, $mail);
134
        }
135
136
        $content = new Content("text/plain", $message->getTextBody());
0 ignored issues
show
Bug introduced by
It seems like $message->getTextBody() can also be of type resource; however, parameter $value of SendGrid\Mail\Content::__construct() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

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

136
        $content = new Content("text/plain", /** @scrutinizer ignore-type */ $message->getTextBody());
Loading history...
137
        $mail->addContent($content);
138
139
        $content = new Content("text/html", $message->getHtmlBody());
140
        $mail->addContent($content);
141
    }
142
143
    /**
144
     * @param DataPart $attachment
145
     * @param Mail $mail
146
     * @throws SendGrid\Mail\TypeException
147
     */
148
    protected function addAttachment(DataPart $attachment, $mail)
149
    {
150
        $headers = $attachment->getPreparedHeaders();
151
        $filename = $headers->getHeaderParameter('Content-Disposition', 'filename');
152
        $disposition = $headers->getHeaderBody('Content-Disposition');
153
154
        $sgAttachment = new Attachment();
155
        $sgAttachment->setContent(str_replace("\r\n", '', $attachment->bodyToString()));
156
        $sgAttachment->setType($headers->get('Content-Type')->getBody());
157
        $sgAttachment->setFilename($filename);
158
        $sgAttachment->setDisposition($disposition);
159
160
        if ('inline' === $disposition) {
161
            $sgAttachment->setContentID($filename);
162
        }
163
164
        $mail->addAttachment($sgAttachment);
165
    }
166
167
    /**
168
     * @param Message $message
169
     * @param $mail
170
     */
171
    protected function populateCustomArg(Message $message, $mail)
172
    {
173
        $args = $message->getCustomArgs();
174
        foreach ($args as $key => $value) {
175
            if ($key == 'category') {
176
                $mail->addCategory($value);
177
            } else {
178
                $mail->addCustomArg($key, (string)$value);
179
            }
180
        }
181
    }
182
183
    /**
184
     * @return bool
185
     * @throws TransportException
186
     */
187
    protected function sendApiCall($mail): bool
188
    {
189
        $sendGrid = $this->createApi();
190
        try {
191
            $response = $sendGrid->send($mail);
192
        } catch (Exception $exception) {
193
            throw new TransportException(
194
                'Error sending email Code [' . $exception->getMessage() . ']'
195
            );
196
        }
197
198
        if ($response->statusCode() == '202') {
199
            return true;
200
        } else {
201
            throw new TransportException(
202
                'Error sending email Code [' . $response->statusCode() . ']. '
203
                . 'HEADERS [' . print_r($response->headers())
0 ignored issues
show
Bug introduced by
Are you sure print_r($response->headers()) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

203
                . 'HEADERS [' . /** @scrutinizer ignore-type */ print_r($response->headers())
Loading history...
204
                . $response->body()
205
            );
206
        }
207
    }
208
209
    /**
210
     * @return SendGrid
211
     * @throws TransportException
212
     */
213
    protected function createApi(): SendGrid
214
    {
215
        if ($this->getApiKey() === null) {
216
            throw new TransportException('Cannot create instance of \SendGrid while API key is NULL');
217
        }
218
219
        return new SendGrid($this->getApiKey());
220
    }
221
222
    /**
223
     * @param string $apiKey
224
     * @return $this
225
     */
226
    public function setApiKey($apiKey): self
227
    {
228
        $this->apiKey = $apiKey;
229
230
        return $this;
231
    }
232
233
    public function __toString(): string
234
    {
235
        return 'sendgrid';
236
    }
237
}
238