Skip to content

Commit

Permalink
feat: parse ses error message when throwing exception
Browse files Browse the repository at this point in the history
fixes #16
  • Loading branch information
carlalexander committed Sep 22, 2023
1 parent 71d4517 commit 72e0f52
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 2 deletions.
21 changes: 19 additions & 2 deletions src/CloudProvider/Aws/SesClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ public function sendEmail(Email $email)
'Action' => 'SendRawEmail',
'RawMessage.Data' => base64_encode($email->toString()),
]));
$statusCode = $this->parseResponseStatusCode($response);

if (200 !== $this->parseResponseStatusCode($response)) {
throw new \RuntimeException('Unable to send email');
if (400 === $statusCode) {
throw new \RuntimeException(sprintf('SES API request failed: %s', $this->getErrorMessage($response['body'] ?? '')));
} elseif (200 !== $this->parseResponseStatusCode($response)) {
throw new \RuntimeException(sprintf('SES API request failed with status code %d', $statusCode));
}
}

Expand All @@ -51,4 +54,18 @@ protected function getService(): string
{
return 'ses';
}

/**
* Get the SES error message.
*/
private function getErrorMessage($body): string
{
$body = simplexml_load_string($body);

if (!$body instanceof \SimpleXMLElement) {
return '[unable to parse error message]';
}

return (string) $body->Error->Message;
}
}
136 changes: 136 additions & 0 deletions tests/Unit/CloudProvider/Aws/SesClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,140 @@ public function testSendEmail()

(new SesClient($http, 'aws-key', 'us-east-1', 'aws-secret'))->sendEmail($email);
}

public function testSendEmailWithSesError()
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('SES API request failed: Email address is not verified. The following identities failed the check in region US-EAST-1: WordPress <[email protected]>');

$email = $this->getEmailMock();
$email->expects($this->once())
->method('toString')
->willReturn('email');

$http = $this->getHttpClientMock();
$http->expects($this->once())
->method('request')
->with(
$this->identicalTo('https://email.us-east-1.amazonaws.com/'),
$this->identicalTo([
'headers' => [
'host' => 'email.us-east-1.amazonaws.com',
'x-amz-content-sha256' => '93c8df40dd7aabcd009385e2496d874342612b116f80638899066a8f6a2e72e6',
'x-amz-date' => '20200515T181004Z',
'authorization' => 'AWS4-HMAC-SHA256 Credential=aws-key/20200515/us-east-1/ses/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=105ad9051f2aaeb3471e626dfd368d2bccf87ea0626ace1ea0fbf459864eb62f',
],
'method' => 'POST',
'timeout' => 300,
'body' => 'Action=SendRawEmail&RawMessage.Data=ZW1haWw%3D',
])
)
->willReturn([
'body' => "<ErrorResponse xmlns=\"http://ses.amazonaws.com/doc/2010-12-01/\">\n <Error>\n <Type>Sender</Type>\n <Code>MessageRejected</Code>\n <Message>Email address is not verified. The following identities failed the check in region US-EAST-1: WordPress &lt;[email protected]&gt;</Message>\n </Error>\n <RequestId>da8072bc-3550-4d98-81f5-28169cc53c7b</RequestId>\n</ErrorResponse>\n",
'response' => ['code' => 400],
]);

$gmdate = $this->getFunctionMock($this->getNamespace(SesClient::class), 'gmdate');
$gmdate->expects($this->exactly(5))
->withConsecutive(
[$this->identicalTo('Ymd\THis\Z')],
[$this->identicalTo('Ymd')],
[$this->identicalTo('Ymd\THis\Z')],
[$this->identicalTo('Ymd')],
[$this->identicalTo('Ymd')]
)
->willReturnOnConsecutiveCalls('20200515T181004Z', '20200515', '20200515T181004Z', '20200515', '20200515');

(new SesClient($http, 'aws-key', 'us-east-1', 'aws-secret'))->sendEmail($email);
}

public function testSendEmailWithSesErrorAndNoBody()
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('SES API request failed: [unable to parse error message]');

$email = $this->getEmailMock();
$email->expects($this->once())
->method('toString')
->willReturn('email');

$http = $this->getHttpClientMock();
$http->expects($this->once())
->method('request')
->with(
$this->identicalTo('https://email.us-east-1.amazonaws.com/'),
$this->identicalTo([
'headers' => [
'host' => 'email.us-east-1.amazonaws.com',
'x-amz-content-sha256' => '93c8df40dd7aabcd009385e2496d874342612b116f80638899066a8f6a2e72e6',
'x-amz-date' => '20200515T181004Z',
'authorization' => 'AWS4-HMAC-SHA256 Credential=aws-key/20200515/us-east-1/ses/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=105ad9051f2aaeb3471e626dfd368d2bccf87ea0626ace1ea0fbf459864eb62f',
],
'method' => 'POST',
'timeout' => 300,
'body' => 'Action=SendRawEmail&RawMessage.Data=ZW1haWw%3D',
])
)
->willReturn([
'response' => ['code' => 400],
]);

$gmdate = $this->getFunctionMock($this->getNamespace(SesClient::class), 'gmdate');
$gmdate->expects($this->exactly(5))
->withConsecutive(
[$this->identicalTo('Ymd\THis\Z')],
[$this->identicalTo('Ymd')],
[$this->identicalTo('Ymd\THis\Z')],
[$this->identicalTo('Ymd')],
[$this->identicalTo('Ymd')]
)
->willReturnOnConsecutiveCalls('20200515T181004Z', '20200515', '20200515T181004Z', '20200515', '20200515');

(new SesClient($http, 'aws-key', 'us-east-1', 'aws-secret'))->sendEmail($email);
}

public function testSendEmailWithWrongStatusCode()
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('SES API request failed with status code 404');

$email = $this->getEmailMock();
$email->expects($this->once())
->method('toString')
->willReturn('email');

$http = $this->getHttpClientMock();
$http->expects($this->once())
->method('request')
->with(
$this->identicalTo('https://email.us-east-1.amazonaws.com/'),
$this->identicalTo([
'headers' => [
'host' => 'email.us-east-1.amazonaws.com',
'x-amz-content-sha256' => '93c8df40dd7aabcd009385e2496d874342612b116f80638899066a8f6a2e72e6',
'x-amz-date' => '20200515T181004Z',
'authorization' => 'AWS4-HMAC-SHA256 Credential=aws-key/20200515/us-east-1/ses/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=105ad9051f2aaeb3471e626dfd368d2bccf87ea0626ace1ea0fbf459864eb62f',
],
'method' => 'POST',
'timeout' => 300,
'body' => 'Action=SendRawEmail&RawMessage.Data=ZW1haWw%3D',
])
)
->willReturn([
'response' => ['code' => 404],
]);

$gmdate = $this->getFunctionMock($this->getNamespace(SesClient::class), 'gmdate');
$gmdate->expects($this->exactly(5))
->withConsecutive(
[$this->identicalTo('Ymd\THis\Z')],
[$this->identicalTo('Ymd')],
[$this->identicalTo('Ymd\THis\Z')],
[$this->identicalTo('Ymd')],
[$this->identicalTo('Ymd')]
)
->willReturnOnConsecutiveCalls('20200515T181004Z', '20200515', '20200515T181004Z', '20200515', '20200515');

(new SesClient($http, 'aws-key', 'us-east-1', 'aws-secret'))->sendEmail($email);
}
}

0 comments on commit 72e0f52

Please sign in to comment.