diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59b0ee3..a18c640 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## [3.2.0] - 2025-06-13
+- Add Contact Lists API functionality
+- Add Email Templates API functionality
+
## [3.1.0] - 2025-05-27
- Add Contacts API functionality
diff --git a/examples/general/contacts.php b/examples/general/contacts.php
index 2cbc5e3..3a70ab2 100644
--- a/examples/general/contacts.php
+++ b/examples/general/contacts.php
@@ -27,6 +27,90 @@
}
+/**
+ * Get a specific Contact List by ID.
+ *
+ * GET https://mailtrap.io/api/accounts/{account_id}/contacts/lists/{list_id}
+ */
+try {
+ $contactListId = 1; // Replace 1 with the actual list ID
+ $response = $contacts->getContactList($contactListId);
+
+ // print the response body (array)
+ var_dump(ResponseHelper::toArray($response));
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
+
+
+/**
+ * Create a new Contact List.
+ *
+ * POST https://mailtrap.io/api/accounts/{account_id}/contacts/lists
+ */
+try {
+ $contactListName = 'New Contact List'; // Replace with your desired list name
+ $response = $contacts->createContactList($contactListName);
+
+ // print the response body (array)
+ var_dump(ResponseHelper::toArray($response));
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
+
+
+/**
+ * Update a Contact List by ID.
+ *
+ * PATCH https://mailtrap.io/api/accounts/{account_id}/contacts/lists/{list_id}
+ */
+try {
+ $contactListId = 1; // Replace 1 with the actual list ID
+ $newContactListName = 'Updated Contact List Name'; // Replace with your desired list name
+ $response = $contacts->updateContactList($contactListId, $newContactListName);
+
+ // print the response body (array)
+ var_dump(ResponseHelper::toArray($response));
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
+
+
+/**
+ * Delete a Contact List by ID.
+ *
+ * DELETE https://mailtrap.io/api/accounts/{account_id}/contacts/lists/{list_id}
+ */
+try {
+ $contactListId = 1; // Replace 1 with the actual list ID
+ $response = $contacts->deleteContactList($contactListId);
+
+ // Print the response status code
+ var_dump($response->getStatusCode());
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
+
+
+/**
+ * Get contact
+ *
+ * GET https://mailtrap.io/api/accounts/{account_id}/contacts/{id_or_email}
+ */
+try {
+ // Get contact by ID
+ $response = $contacts->getContactById('019706a8-0000-0000-0000-4f26816b467a');
+
+ // OR get contact by email
+ $response = $contacts->getContactByEmail('john.smith@example.com');
+
+ // print the response body (array)
+ var_dump(ResponseHelper::toArray($response));
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
+
+
/**
* Create a new Contact
*
@@ -97,8 +181,8 @@
// OR delete contact by email
$response = $contacts->deleteContactByEmail('john.smith@example.com');
- // print the response body (array)
- var_dump(ResponseHelper::toArray($response));
+ // Print the response status code
+ var_dump($response->getStatusCode());
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
}
diff --git a/examples/general/emailTemplates.php b/examples/general/emailTemplates.php
new file mode 100644
index 0000000..4cd48ef
--- /dev/null
+++ b/examples/general/emailTemplates.php
@@ -0,0 +1,108 @@
+emailTemplates($accountId);
+
+/**
+ * Get all Email Templates.
+ *
+ * GET https://mailtrap.io/api/accounts/{account_id}/email_templates
+ */
+try {
+ $response = $emailTemplates->getAllEmailTemplates();
+
+ // Print the response body (array)
+ var_dump(ResponseHelper::toArray($response));
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
+
+
+/**
+ * Get Email Template by ID.
+ *
+ * GET https://mailtrap.io/api/accounts/{account_id}/email_templates/{id}
+ */
+try {
+ $templateId = 12345; // Replace with a valid template ID
+ $response = $emailTemplates->getEmailTemplate($templateId);
+
+ // Print the response body (array)
+ var_dump(ResponseHelper::toArray($response));
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
+
+
+/**
+ * Create a new Email Template.
+ *
+ * POST https://mailtrap.io/api/accounts/{account_id}/email_templates
+ */
+try {
+ $response = $emailTemplates->createEmailTemplate(
+ EmailTemplate::init(
+ 'Welcome Email', // Name
+ 'Welcome to our service!', // Subject
+ 'Transactional', // Category
+ 'Welcome to our service!', // Text Body
+ '
Welcome to our service!
' // HTML Body
+ )
+ );
+
+ // Print the response body (array)
+ var_dump(ResponseHelper::toArray($response));
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
+
+
+/**
+ * Update an Email Template by ID.
+ *
+ * PATCH https://mailtrap.io/api/accounts/{account_id}/email_templates/{email_template_id}
+ */
+try {
+ $templateId = 12345; // Replace with a valid template ID
+ $response = $emailTemplates->updateEmailTemplate(
+ $templateId,
+ EmailTemplate::init(
+ 'Updated Welcome Email', // Name
+ 'Updated Subject', // Subject
+ 'Transactional', // Category
+ 'Updated Text Body', // Text Body
+ 'Updated HTML Body
', // HTML Body
+ )
+ );
+
+ // Print the response body (array)
+ var_dump(ResponseHelper::toArray($response));
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
+
+
+/**
+ * Delete an Email Template by ID.
+ *
+ * DELETE https://mailtrap.io/api/accounts/{account_id}/email_templates/{email_template_id}
+ */
+try {
+ $templateId = 12345; // Replace with a valid template ID
+ $response = $emailTemplates->deleteEmailTemplate($templateId);
+
+ // Print the response status code
+ var_dump($response->getStatusCode());
+} catch (Exception $e) {
+ echo 'Caught exception: ', $e->getMessage(), PHP_EOL;
+}
diff --git a/examples/general/users.php b/examples/general/users.php
index 1ed7063..d726fda 100644
--- a/examples/general/users.php
+++ b/examples/general/users.php
@@ -39,8 +39,8 @@
$response = $generalUsers->delete($accountAccessId);
- // print the response body (array)
- var_dump(ResponseHelper::toArray($response));
+ // Print the response status code
+ var_dump($response->getStatusCode());
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
diff --git a/examples/testing/inboxes.php b/examples/testing/inboxes.php
index 86aa962..a9425c6 100644
--- a/examples/testing/inboxes.php
+++ b/examples/testing/inboxes.php
@@ -72,8 +72,8 @@
$response = $sandboxInboxes->delete($inboxId);
- // print the response body (array)
- var_dump(ResponseHelper::toArray($response));
+ // Print the response status code
+ var_dump($response->getStatusCode());
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
diff --git a/examples/testing/messages.php b/examples/testing/messages.php
index 8a04ba2..daef741 100644
--- a/examples/testing/messages.php
+++ b/examples/testing/messages.php
@@ -197,8 +197,8 @@
$response = $sandboxMessages->delete($messageId);
- // print the response body (array)
- var_dump(ResponseHelper::toArray($response));
+ // Print the response status code
+ var_dump($response->getStatusCode());
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
diff --git a/examples/testing/projects.php b/examples/testing/projects.php
index 6eba93d..03f386f 100644
--- a/examples/testing/projects.php
+++ b/examples/testing/projects.php
@@ -89,8 +89,8 @@
$response = $sandboxProjects->delete($projectId);
- // print the response body (array)
- var_dump(ResponseHelper::toArray($response));
+ // Print the response status code
+ var_dump($response->getStatusCode());
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
diff --git a/src/Api/General/Contact.php b/src/Api/General/Contact.php
index 0ca0eb6..b16b13c 100644
--- a/src/Api/General/Contact.php
+++ b/src/Api/General/Contact.php
@@ -32,6 +32,87 @@ public function getAllContactLists(): ResponseInterface
);
}
+ /**
+ * Get a specific Contact List by ID.
+ *
+ * @param int $listId
+ * @return ResponseInterface
+ */
+ public function getContactList(int $listId): ResponseInterface
+ {
+ return $this->handleResponse(
+ $this->httpGet($this->getBasePath() . '/lists/' . $listId)
+ );
+ }
+
+ /**
+ * Create a new Contact List.
+ *
+ * @param string $name
+ * @return ResponseInterface
+ */
+ public function createContactList(string $name): ResponseInterface
+ {
+ return $this->handleResponse(
+ $this->httpPost(
+ path: $this->getBasePath() . '/lists',
+ body: ['name' => $name]
+ )
+ );
+ }
+
+ /**
+ * Update an existing Contact List by ID.
+ *
+ * @param int $listId
+ * @param string $name
+ * @return ResponseInterface
+ */
+ public function updateContactList(int $listId, string $name): ResponseInterface
+ {
+ return $this->handleResponse(
+ $this->httpPatch(
+ path: $this->getBasePath() . '/lists/' . $listId,
+ body: ['name' => $name]
+ )
+ );
+ }
+
+ /**
+ * Delete a Contact List by ID.
+ *
+ * @param int $listId
+ * @return ResponseInterface
+ */
+ public function deleteContactList(int $listId): ResponseInterface
+ {
+ return $this->handleResponse(
+ $this->httpDelete($this->getBasePath() . '/lists/' . $listId)
+ );
+ }
+
+ /**
+ * Get a Contact by ID (UUID)
+ *
+ * @param string $contactId
+ * @return ResponseInterface
+ */
+ public function getContactById(string $contactId): ResponseInterface
+ {
+ return $this->getContact($contactId);
+ }
+
+ /**
+ * Get a Contact by Email.
+ *
+ * @param string $email
+ * @return ResponseInterface
+ */
+ public function getContactByEmail(string $email): ResponseInterface
+ {
+ return $this->getContact($email);
+ }
+
/**
* Create a new Contact.
*
@@ -96,6 +177,19 @@ public function getAccountId(): int
return $this->accountId;
}
+ /**
+ * Get a Contact by ID or Email.
+ *
+ * @param string $idOrEmail
+ * @return ResponseInterface
+ */
+ private function getContact(string $idOrEmail): ResponseInterface
+ {
+ return $this->handleResponse(
+ $this->httpGet($this->getBasePath() . '/' . urlencode($idOrEmail))
+ );
+ }
+
/**
* Update an existing Contact.
*
diff --git a/src/Api/General/EmailTemplate.php b/src/Api/General/EmailTemplate.php
new file mode 100644
index 0000000..a037033
--- /dev/null
+++ b/src/Api/General/EmailTemplate.php
@@ -0,0 +1,97 @@
+handleResponse(
+ $this->httpGet($this->getBasePath())
+ );
+ }
+
+ /**
+ * Get an Email Template by ID.
+ *
+ * @param int $templateId
+ * @return ResponseInterface
+ */
+ public function getEmailTemplate(int $templateId): ResponseInterface
+ {
+ return $this->handleResponse(
+ $this->httpGet($this->getBasePath() . '/' . $templateId)
+ );
+ }
+
+ /**
+ * Create a new Email Template.
+ *
+ * @param EmailTemplateDTO $emailTemplate
+ * @return ResponseInterface
+ */
+ public function createEmailTemplate(EmailTemplateDTO $emailTemplate): ResponseInterface
+ {
+ return $this->handleResponse(
+ $this->httpPost(
+ path: $this->getBasePath(),
+ body: ['email_template' => $emailTemplate->toArray()]
+ )
+ );
+ }
+
+ /**
+ * Update an existing Email Template by ID.
+ *
+ * @param int $templateId
+ * @param EmailTemplateDTO $template
+ * @return ResponseInterface
+ */
+ public function updateEmailTemplate(int $templateId, EmailTemplateDTO $template): ResponseInterface
+ {
+ return $this->handleResponse(
+ $this->httpPatch(
+ path: $this->getBasePath() . '/' . $templateId,
+ body: ['email_template' => $template->toArray()]
+ )
+ );
+ }
+
+ /**
+ * Delete an Email Template by ID.
+ *
+ * @param int $templateId
+ * @return ResponseInterface
+ */
+ public function deleteEmailTemplate(int $templateId): ResponseInterface
+ {
+ return $this->handleResponse(
+ $this->httpDelete($this->getBasePath() . '/' . $templateId)
+ );
+ }
+
+ private function getBasePath(): string
+ {
+ return sprintf('%s/api/accounts/%s/email_templates', $this->getHost(), $this->accountId);
+ }
+}
diff --git a/src/DTO/Request/EmailTemplate.php b/src/DTO/Request/EmailTemplate.php
new file mode 100644
index 0000000..67276f1
--- /dev/null
+++ b/src/DTO/Request/EmailTemplate.php
@@ -0,0 +1,64 @@
+name;
+ }
+
+ public function getCategory(): string
+ {
+ return $this->category;
+ }
+
+ public function getSubject(): string
+ {
+ return $this->subject;
+ }
+
+ public function getBodyText(): string
+ {
+ return $this->bodyText;
+ }
+
+ public function getBodyHtml(): string
+ {
+ return $this->bodyHtml;
+ }
+
+ public function toArray(): array
+ {
+ return [
+ 'name' => $this->getName(),
+ 'category' => $this->getCategory(),
+ 'subject' => $this->getSubject(),
+ 'body_text' => $this->getBodyText(),
+ 'body_html' => $this->getBodyHtml(),
+ ];
+ }
+}
diff --git a/src/Exception/HttpClientException.php b/src/Exception/HttpClientException.php
index c98f0a3..a4bf085 100644
--- a/src/Exception/HttpClientException.php
+++ b/src/Exception/HttpClientException.php
@@ -29,6 +29,9 @@ public static function createFromResponse(ResponseInterface $response): HttpClie
$body = ResponseHelper::toArray($response);
} catch (JsonException|InvalidTypeException) {
$body['error'] = $response->getBody()->__toString();
+ if (empty($body['error'])) {
+ $body['error'] = 'No error info in the response body';
+ }
}
if (isset(self::ERROR_PREFIXES[$statusCode])) {
diff --git a/src/MailtrapGeneralClient.php b/src/MailtrapGeneralClient.php
index c5bfcba..1252a5c 100644
--- a/src/MailtrapGeneralClient.php
+++ b/src/MailtrapGeneralClient.php
@@ -5,10 +5,11 @@
namespace Mailtrap;
/**
- * @method Api\General\Account accounts()
- * @method Api\General\User users(int $accountId)
- * @method Api\General\Permission permissions(int $accountId)
- * @method Api\General\Contact contacts(int $accountId)
+ * @method Api\General\Account accounts()
+ * @method Api\General\User users(int $accountId)
+ * @method Api\General\Permission permissions(int $accountId)
+ * @method Api\General\Contact contacts(int $accountId)
+ * @method Api\General\EmailTemplate emailTemplates(int $accountId)
*
* Class MailtrapGeneralClient
*/
@@ -19,5 +20,6 @@ final class MailtrapGeneralClient extends AbstractMailtrapClient
'users' => Api\General\User::class,
'permissions' => Api\General\Permission::class,
'contacts' => Api\General\Contact::class,
+ 'emailTemplates' => Api\General\EmailTemplate::class,
];
}
diff --git a/tests/Api/General/ContactTest.php b/tests/Api/General/ContactTest.php
index 55216c7..6739b9b 100644
--- a/tests/Api/General/ContactTest.php
+++ b/tests/Api/General/ContactTest.php
@@ -28,7 +28,7 @@ protected function setUp(): void
parent::setUp();
$this->contact = $this->getMockBuilder(Contact::class)
- ->onlyMethods(['httpGet', 'httpPost', 'httpPut', 'httpDelete'])
+ ->onlyMethods(['httpGet', 'httpPost', 'httpPut', 'httpPatch', 'httpDelete'])
->setConstructorArgs([$this->getConfigMock(), self::FAKE_ACCOUNT_ID])
->getMock();
}
@@ -55,6 +55,153 @@ public function testGetAllContactLists(): void
$this->assertArrayHasKey('id', $responseData[0]);
}
+ public function testGetContactList(): void
+ {
+ $contactListId = 1;
+
+ $this->contact->expects($this->once())
+ ->method('httpGet')
+ ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/lists/' . $contactListId)
+ ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedContactLists()[0])));
+
+ $response = $this->contact->getContactList($contactListId);
+ $responseData = ResponseHelper::toArray($response);
+
+ $this->assertArrayHasKey('id', $responseData);
+ $this->assertEquals($contactListId, $responseData['id']);
+ }
+
+ public function testGetContactListNotFound(): void
+ {
+ $contactListId = 999; // Non-existent ID for testing
+
+ $this->contact->expects($this->once())
+ ->method('httpGet')
+ ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/lists/' . $contactListId)
+ ->willReturn(
+ new Response(404, ['Content-Type' => 'application/json'], json_encode(['error' => 'Not Found']))
+ );
+
+ $this->expectException(HttpClientException::class);
+ $this->expectExceptionMessage('Errors: Not Found.');
+
+ $this->contact->getContactList($contactListId);
+ }
+
+ public function testCreateContactList(): void
+ {
+ $contactListName = 'List 1';
+
+ $this->contact->expects($this->once())
+ ->method('httpPost')
+ ->with(
+ AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/lists',
+ [],
+ ['name' => $contactListName]
+ )
+ ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedContactLists()[0])));
+
+ $response = $this->contact->createContactList($contactListName);
+ $responseData = ResponseHelper::toArray($response);
+
+ $this->assertArrayHasKey('id', $responseData);
+ $this->assertEquals($contactListName, $responseData['name']);
+ }
+
+ public function testCreateContactListRateLimitExceeded(): void
+ {
+ $contactListName = 'List 1';
+
+ $this->contact->expects($this->once())
+ ->method('httpPost')
+ ->with(
+ AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/lists',
+ [],
+ ['name' => $contactListName]
+ )
+ ->willReturn(
+ new Response(429, ['Content-Type' => 'application/json'], json_encode(['errors' => 'Rate limit exceeded']))
+ );
+
+ $this->expectException(HttpClientException::class);
+ $this->expectExceptionMessage('Errors: Rate limit exceeded.');
+
+ $this->contact->createContactList($contactListName);
+ }
+
+ public function testUpdateContactList(): void
+ {
+ $contactListId = 2;
+ $newContactListName = 'List 2';
+
+ $this->contact->expects($this->once())
+ ->method('httpPatch')
+ ->with(
+ AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/lists/' . $contactListId,
+ [],
+ ['name' => $newContactListName]
+ )
+ ->willReturn(new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedContactLists()[1])));
+
+ $response = $this->contact->updateContactList($contactListId, $newContactListName);
+ $responseData = ResponseHelper::toArray($response);
+
+ $this->assertArrayHasKey('id', $responseData);
+ $this->assertEquals($newContactListName, $responseData['name']);
+ }
+
+ public function testDeleteContactList(): void
+ {
+ $contactListId = 1;
+
+ $this->contact->expects($this->once())
+ ->method('httpDelete')
+ ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/lists/' . $contactListId)
+ ->willReturn(new Response(204));
+
+ $response = $this->contact->deleteContactList($contactListId);
+
+ $this->assertEquals(204, $response->getStatusCode());
+ }
+
+ public function testGetContactById(): void
+ {
+ $contactId = '019706a8-9612-77be-8586-4f26816b467a';
+
+ $this->contact->expects($this->once())
+ ->method('httpGet')
+ ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/' . $contactId)
+ ->willReturn(
+ new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedContactData()))
+ );
+
+ $response = $this->contact->getContactById($contactId);
+ $responseData = ResponseHelper::toArray($response)['data'];
+
+ $this->assertInstanceOf(Response::class, $response);
+ $this->assertArrayHasKey('email', $responseData);
+ $this->assertEquals('test@example.com', $responseData['email']);
+ }
+
+ public function testGetContactByEmail(): void
+ {
+ $contactEmail = 'test@example.com';
+
+ $this->contact->expects($this->once())
+ ->method('httpGet')
+ ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/contacts/' . urlencode($contactEmail))
+ ->willReturn(
+ new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedContactData()))
+ );
+
+ $response = $this->contact->getContactByEmail($contactEmail);
+ $responseData = ResponseHelper::toArray($response)['data'];
+
+ $this->assertInstanceOf(Response::class, $response);
+ $this->assertArrayHasKey('email', $responseData);
+ $this->assertEquals($contactEmail, $responseData['email']);
+ }
+
public function testCreateContact(): void
{
$fakeEmail = 'test@example.com';
diff --git a/tests/Api/General/EmailTemplateTest.php b/tests/Api/General/EmailTemplateTest.php
new file mode 100644
index 0000000..e563057
--- /dev/null
+++ b/tests/Api/General/EmailTemplateTest.php
@@ -0,0 +1,212 @@
+emailTemplate = $this->getMockBuilder(EmailTemplate::class)
+ ->onlyMethods(['httpGet', 'httpPost', 'httpPatch', 'httpDelete'])
+ ->setConstructorArgs([$this->getConfigMock(), self::FAKE_ACCOUNT_ID])
+ ->getMock();
+ }
+
+ protected function tearDown(): void
+ {
+ $this->emailTemplate = null;
+ parent::tearDown();
+ }
+
+ public function testGetAllEmailTemplates(): void
+ {
+ $this->emailTemplate->expects($this->once())
+ ->method('httpGet')
+ ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/email_templates')
+ ->willReturn(
+ new Response(200, ['Content-Type' => 'application/json'], json_encode([$this->getExpectedEmailTemplateResponse()]))
+ );
+
+ $response = $this->emailTemplate->getAllEmailTemplates();
+ $responseData = ResponseHelper::toArray($response);
+
+ $this->assertCount(1, $responseData);
+ $this->assertArrayHasKey('id', $responseData[0]);
+ $this->assertEquals(1, $responseData[0]['id']);
+ }
+
+ public function testGetEmailTemplateById(): void
+ {
+ $templateId = 1;
+
+ $this->emailTemplate->expects($this->once())
+ ->method('httpGet')
+ ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/email_templates/' . $templateId)
+ ->willReturn(
+ new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedEmailTemplateResponse()))
+ );
+
+ $response = $this->emailTemplate->getEmailTemplate($templateId);
+ $responseData = ResponseHelper::toArray($response);
+
+ $this->assertArrayHasKey('id', $responseData);
+ $this->assertEquals($templateId, $responseData['id']);
+ }
+
+ public function testGetEmailTemplateFailsWithNotFoundError(): void
+ {
+ $templateId = 999; // Non-existent template ID
+ $expectedErrorResponse = [
+ 'error' => 'Not Found',
+ ];
+
+ $this->emailTemplate->expects($this->once())
+ ->method('httpGet')
+ ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/email_templates/' . $templateId)
+ ->willReturn(
+ new Response(404, ['Content-Type' => 'application/json'], json_encode($expectedErrorResponse))
+ );
+
+ $this->expectException(HttpClientException::class);
+ $this->expectExceptionMessage('The requested entity has not been found. Errors: Not Found.');
+
+ $this->emailTemplate->getEmailTemplate($templateId);
+ }
+
+ public function testCreateEmailTemplate(): void
+ {
+ $emailTemplateDTO = $this->getEmailTemplateDTO();
+
+ $this->emailTemplate->expects($this->once())
+ ->method('httpPost')
+ ->with(
+ AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/email_templates',
+ [],
+ ['email_template' => $emailTemplateDTO->toArray()]
+ )
+ ->willReturn(
+ new Response(201, ['Content-Type' => 'application/json'], json_encode($this->getExpectedEmailTemplateResponse()))
+ );
+
+ $response = $this->emailTemplate->createEmailTemplate($emailTemplateDTO);
+ $responseData = ResponseHelper::toArray($response);
+
+ $this->assertArrayHasKey('id', $responseData);
+ $this->assertEquals($emailTemplateDTO->getName(), $responseData['name']);
+ }
+
+ public function testCreateEmailTemplateFailsWithValidationErrors(): void
+ {
+ $invalidEmailTemplateDTO = EmailTemplateDTO::init(
+ '', // Invalid name
+ 'Promotional', // Valid category
+ '', // Invalid subject
+ 'Template Body',
+ 'Template Body
'
+ );
+
+ $expectedErrorResponse = [
+ 'errors' => [
+ 'name' => ["can't be blank"],
+ 'subject' => ["can't be blank"],
+ ],
+ ];
+
+ $this->emailTemplate->expects($this->once())
+ ->method('httpPost')
+ ->with(
+ AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/email_templates',
+ [],
+ ['email_template' => $invalidEmailTemplateDTO->toArray()]
+ )
+ ->willReturn(
+ new Response(422, ['Content-Type' => 'application/json'], json_encode($expectedErrorResponse))
+ );
+
+ $this->expectException(HttpClientException::class);
+ $this->expectExceptionMessage(
+ 'Errors: name -> can\'t be blank. subject -> can\'t be blank.'
+ );
+
+ $this->emailTemplate->createEmailTemplate($invalidEmailTemplateDTO);
+ }
+
+ public function testUpdateEmailTemplate(): void
+ {
+ $templateId = 1;
+ $emailTemplateDTO = $this->getEmailTemplateDTO();
+
+ $this->emailTemplate->expects($this->once())
+ ->method('httpPatch')
+ ->with(
+ AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/email_templates/' . $templateId,
+ [],
+ ['email_template' => $emailTemplateDTO->toArray()]
+ )
+ ->willReturn(
+ new Response(200, ['Content-Type' => 'application/json'], json_encode($this->getExpectedEmailTemplateResponse()))
+ );
+
+ $response = $this->emailTemplate->updateEmailTemplate($templateId, $emailTemplateDTO);
+ $responseData = ResponseHelper::toArray($response);
+
+ $this->assertArrayHasKey('id', $responseData);
+ $this->assertEquals($templateId, $responseData['id']);
+ }
+
+ public function testDeleteEmailTemplate(): void
+ {
+ $templateId = 1;
+
+ $this->emailTemplate->expects($this->once())
+ ->method('httpDelete')
+ ->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/email_templates/' . $templateId)
+ ->willReturn(new Response(204));
+
+ $response = $this->emailTemplate->deleteEmailTemplate($templateId);
+
+ $this->assertEquals(204, $response->getStatusCode());
+ }
+
+ private function getEmailTemplateDTO(): EmailTemplateDTO
+ {
+ return EmailTemplateDTO::init(
+ 'Template 1',
+ 'Test Category',
+ 'Test Subject',
+ 'Test Content',
+ 'Test Content
'
+ );
+ }
+
+ private function getExpectedEmailTemplateResponse(): array
+ {
+ return [
+ 'id' => 1,
+ 'uuid' => '019706a8-9612-77be-8586-4f26816b467a',
+ 'name' => 'Template 1',
+ 'category' => 'welcome',
+ 'subject' => 'Welcome to Mailtrap',
+ 'body_html' => 'Welcome to Mailtrap
',
+ 'body_text' => 'Welcome to Mailtrap',
+ ];
+ }
+}
diff --git a/tests/MailtrapGeneralClientTest.php b/tests/MailtrapGeneralClientTest.php
index b7b3821..8c66dbe 100644
--- a/tests/MailtrapGeneralClientTest.php
+++ b/tests/MailtrapGeneralClientTest.php
@@ -28,7 +28,7 @@ public function mapInstancesProvider(): iterable
{
foreach (MailtrapGeneralClient::API_MAPPING as $key => $item) {
yield match ($key) {
- 'permissions', 'users', 'contacts' => [new $item($this->getConfigMock(), self::FAKE_ACCOUNT_ID)],
+ 'permissions', 'users', 'contacts', 'emailTemplates' => [new $item($this->getConfigMock(), self::FAKE_ACCOUNT_ID)],
default => [new $item($this->getConfigMock())],
};
}