From aa1e12cbf6894f1660b855661e9ec8ff1b5dd537 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 26 Feb 2022 12:35:08 +0100 Subject: [PATCH] [symfony/mailjet-mailer] Fix invalid mailjet error managment --- .../Transport/MailjetApiTransportTest.php | 181 ++++++++++++++++++ .../Mailjet/Transport/MailjetApiTransport.php | 6 +- 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Mailjet/Tests/Transport/MailjetApiTransportTest.php b/src/Symfony/Component/Mailer/Bridge/Mailjet/Tests/Transport/MailjetApiTransportTest.php index c46515ef36772..64769031a8d69 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailjet/Tests/Transport/MailjetApiTransportTest.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailjet/Tests/Transport/MailjetApiTransportTest.php @@ -3,8 +3,12 @@ namespace Symfony\Component\Mailer\Bridge\Mailjet\Tests\Transport; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetApiTransport; use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\Exception\HttpTransportException; +use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; @@ -85,6 +89,183 @@ public function testPayloadFormat() $this->assertEquals('Qux', $replyTo['Name']); } + public function testSendSuccess() + { + $json = json_encode([ + 'Messages' => [ + 'foo' => 'bar', + ], + ]); + + $responseHeaders = [ + 'x-mj-request-guid' => ['baz'], + ]; + + $response = new MockResponse($json, ['response_headers' => $responseHeaders]); + + $client = new MockHttpClient($response); + + $transport = new MailjetApiTransport(self::USER, self::PASSWORD, $client); + + $email = new Email(); + $email + ->from('foo@example.com') + ->to('bar@example.com') + ->text('foobar'); + + $sentMessage = $transport->send($email); + $this->assertInstanceOf(SentMessage::class, $sentMessage); + $this->assertSame('baz', $sentMessage->getMessageId()); + } + + public function testSendWithDecodingException() + { + $response = new MockResponse('cannot-be-decoded'); + + $client = new MockHttpClient($response); + + $transport = new MailjetApiTransport(self::USER, self::PASSWORD, $client); + + $email = new Email(); + $email + ->from('foo@example.com') + ->to('bar@example.com') + ->text('foobar'); + + $this->expectExceptionObject( + new HttpTransportException('Unable to send an email: "cannot-be-decoded" (code 200).', $response) + ); + + $transport->send($email); + } + + public function testSendWithTransportException() + { + $response = new MockResponse('', ['error' => 'foo']); + + $client = new MockHttpClient($response); + + $transport = new MailjetApiTransport(self::USER, self::PASSWORD, $client); + + $email = new Email(); + $email + ->from('foo@example.com') + ->to('bar@example.com') + ->text('foobar'); + + $this->expectExceptionObject( + new HttpTransportException('Could not reach the remote Mailjet server.', $response) + ); + + $transport->send($email); + } + + public function testSendWithBadRequestResponse() + { + $json = json_encode([ + 'Messages' => [ + [ + 'Errors' => [ + [ + 'ErrorIdentifier' => '8e28ac9c-1fd7-41ad-825f-1d60bc459189', + 'ErrorCode' => 'mj-0005', + 'StatusCode' => 400, + 'ErrorMessage' => 'The To is mandatory but missing from the input', + 'ErrorRelatedTo' => ['To'], + ], + ], + 'Status' => 'error', + ], + ], + ]); + + $response = new MockResponse($json, ['http_code' => 400]); + + $client = new MockHttpClient($response); + + $transport = new MailjetApiTransport(self::USER, self::PASSWORD, $client); + + $email = new Email(); + $email + ->from('foo@example.com') + ->to('bar@example.com') + ->text('foobar'); + + $this->expectExceptionObject( + new HttpTransportException('Unable to send an email: "The To is mandatory but missing from the input" (code 400).', $response) + ); + + $transport->send($email); + } + + public function testSendWithNoErrorMessageBadRequestResponse() + { + $response = new MockResponse('response-content', ['http_code' => 400]); + + $client = new MockHttpClient($response); + + $transport = new MailjetApiTransport(self::USER, self::PASSWORD, $client); + + $email = new Email(); + $email + ->from('foo@example.com') + ->to('bar@example.com') + ->text('foobar'); + + $this->expectExceptionObject( + new HttpTransportException('Unable to send an email: "response-content" (code 400).', $response) + ); + + $transport->send($email); + } + + /** + * @dataProvider getMalformedResponse + */ + public function testSendWithMalformedResponse(array $body) + { + $json = json_encode($body); + + $response = new MockResponse($json); + + $client = new MockHttpClient($response); + + $transport = new MailjetApiTransport(self::USER, self::PASSWORD, $client); + + $email = new Email(); + $email + ->from('foo@example.com') + ->to('bar@example.com') + ->text('foobar'); + + $this->expectExceptionObject( + new HttpTransportException(sprintf('Unable to send an email: "%s" malformed api response.', $json), $response) + ); + + $transport->send($email); + } + + public function getMalformedResponse(): \Generator + { + yield 'Missing Messages key' => [ + [ + 'foo' => 'bar', + ], + ]; + + yield 'Messages is not an array' => [ + [ + 'Messages' => 'bar', + ], + ]; + + yield 'Messages is an empty array' => [ + [ + 'Messages' => [], + ], + ]; + } + public function testReplyTo() { $from = 'foo@example.com'; diff --git a/src/Symfony/Component/Mailer/Bridge/Mailjet/Transport/MailjetApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailjet/Transport/MailjetApiTransport.php index 1aa3dec0daf93..8440ecf9ab3f3 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailjet/Transport/MailjetApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailjet/Transport/MailjetApiTransport.php @@ -69,13 +69,15 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e $statusCode = $response->getStatusCode(); $result = $response->toArray(false); } catch (DecodingExceptionInterface $e) { - throw new HttpTransportException('Unable to send an email: '.$response->getContent(false).sprintf(' (code %d).', $statusCode), $response); + throw new HttpTransportException(sprintf('Unable to send an email: "%s" (code %d).', $response->getContent(false), $statusCode), $response); } catch (TransportExceptionInterface $e) { throw new HttpTransportException('Could not reach the remote Mailjet server.', $response, 0, $e); } if (200 !== $statusCode) { - throw new HttpTransportException('Unable to send an email: '.$result['Message'].sprintf(' (code %d).', $statusCode), $response); + $errorDetails = $result['Messages'][0]['Errors'][0]['ErrorMessage'] ?? $response->getContent(false); + + throw new HttpTransportException(sprintf('Unable to send an email: "%s" (code %d).', $errorDetails, $statusCode), $response); } // The response needs to contains a 'Messages' key that is an array