diff --git a/README.md b/README.md index 9f075d7..005b590 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,13 @@ You can find the [Mailtrap Java API reference](https://mailtrap.github.io/mailtr - [Sandbox send using template](examples/java/io/mailtrap/examples/testing/EmailExample.java) - [Batch](examples/java/io/mailtrap/examples/testing/BatchExample.java) +### Inbound Email API + +- [Folders](examples/java/io/mailtrap/examples/inbound/InboundFoldersExample.java) +- [Inboxes](examples/java/io/mailtrap/examples/inbound/InboundInboxesExample.java) +- [Messages](examples/java/io/mailtrap/examples/inbound/InboundMessagesExample.java) +- [Threads](examples/java/io/mailtrap/examples/inbound/InboundThreadsExample.java) + ### Bulk Sending API - [Bulk send](examples/java/io/mailtrap/examples/bulk/BulkSendExample.java) diff --git a/examples/java/io/mailtrap/examples/inbound/InboundFoldersExample.java b/examples/java/io/mailtrap/examples/inbound/InboundFoldersExample.java new file mode 100644 index 0000000..fb7eb86 --- /dev/null +++ b/examples/java/io/mailtrap/examples/inbound/InboundFoldersExample.java @@ -0,0 +1,41 @@ +package io.mailtrap.examples.inbound; + +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.request.inbound.CreateInboundFolderRequest; +import io.mailtrap.model.request.inbound.UpdateInboundFolderRequest; + +public class InboundFoldersExample { + + private static final String TOKEN = ""; + + public static void main(String[] args) { + final var config = new MailtrapConfig.Builder() + .token(TOKEN) + .build(); + + final var client = MailtrapClientFactory.createMailtrapClient(config); + final var folders = client.inboundApi().folders(); + + // List all inbound folders. + final var allFolders = folders.getList(); + System.out.println(allFolders); + + // Create an inbound folder. + final var created = folders.create( + CreateInboundFolderRequest.builder().name("Support").build()); + System.out.println(created); + + // Get an inbound folder by ID. + final var folder = folders.getById(created.getId()); + System.out.println(folder); + + // Rename an inbound folder. + final var updated = folders.update(created.getId(), + UpdateInboundFolderRequest.builder().name("Support (renamed)").build()); + System.out.println(updated); + + // Delete an inbound folder (removes the folder and all of its inboxes). + folders.delete(created.getId()); + } +} diff --git a/examples/java/io/mailtrap/examples/inbound/InboundInboxesExample.java b/examples/java/io/mailtrap/examples/inbound/InboundInboxesExample.java new file mode 100644 index 0000000..f4ae224 --- /dev/null +++ b/examples/java/io/mailtrap/examples/inbound/InboundInboxesExample.java @@ -0,0 +1,43 @@ +package io.mailtrap.examples.inbound; + +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.request.inbound.CreateInboundInboxRequest; +import io.mailtrap.model.request.inbound.UpdateInboundInboxRequest; + +public class InboundInboxesExample { + + private static final String TOKEN = ""; + private static final long FOLDER_ID = 1L; + + public static void main(String[] args) { + final var config = new MailtrapConfig.Builder() + .token(TOKEN) + .build(); + + final var client = MailtrapClientFactory.createMailtrapClient(config); + final var inboxes = client.inboundApi().inboxes(); + + // List inboxes in a folder. + final var allInboxes = inboxes.getList(FOLDER_ID); + System.out.println(allInboxes); + + // Create a Mailtrap-hosted inbox (omit the domain ID). To create a + // custom-domain catch-all inbox, pass .domainId(). + final var created = inboxes.create(FOLDER_ID, + CreateInboundInboxRequest.builder().name("Support inbox").build()); + System.out.println(created); + + // Get an inbox by ID. + final var inbox = inboxes.getById(FOLDER_ID, created.getId()); + System.out.println(inbox); + + // Rename an inbox. + final var updated = inboxes.update(FOLDER_ID, created.getId(), + UpdateInboundInboxRequest.builder().name("Support inbox (renamed)").build()); + System.out.println(updated); + + // Delete an inbox. + inboxes.delete(FOLDER_ID, created.getId()); + } +} diff --git a/examples/java/io/mailtrap/examples/inbound/InboundMessagesExample.java b/examples/java/io/mailtrap/examples/inbound/InboundMessagesExample.java new file mode 100644 index 0000000..752c4a4 --- /dev/null +++ b/examples/java/io/mailtrap/examples/inbound/InboundMessagesExample.java @@ -0,0 +1,63 @@ +package io.mailtrap.examples.inbound; + +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.request.emails.Address; +import io.mailtrap.model.request.inbound.InboundForwardRequest; +import io.mailtrap.model.request.inbound.InboundReplyRequest; + +import java.util.List; + +public class InboundMessagesExample { + + private static final String TOKEN = ""; + private static final long INBOX_ID = 1L; + + public static void main(String[] args) { + final var config = new MailtrapConfig.Builder() + .token(TOKEN) + .build(); + + final var client = MailtrapClientFactory.createMailtrapClient(config); + final var messages = client.inboundApi().messages(); + + // List received messages. Pass the previous page's last id to paginate + // (null for the first page). + final var page = messages.list(INBOX_ID, null); + System.out.println("Total: " + page.getTotalCount()); + page.getData().forEach(msg -> System.out.println(" " + msg.getId() + " " + msg.getSubject())); + + if (page.getData().isEmpty()) { + return; + } + final var messageId = page.getData().get(0).getId(); + + // Get a single message with its body and attachment download URLs. + final var message = messages.get(INBOX_ID, messageId); + System.out.println(message); + + // Reply to a message (sends a real email to the original sender). + final var reply = messages.reply(INBOX_ID, messageId, InboundReplyRequest.builder() + .subject("Re: Support request") + .text("Thanks for reaching out!") + .html("

Thanks for reaching out!

") + .build()); + System.out.println(reply); + + // Reply to a message and copy the original's other recipients. + final var replyAll = messages.replyAll(INBOX_ID, messageId, InboundReplyRequest.builder() + .text("Looping everyone in.") + .build()); + System.out.println(replyAll); + + // Forward a message to new recipients (at least one `to` is required). + final var forward = messages.forward(INBOX_ID, messageId, InboundForwardRequest.builder() + .to(List.of(new Address("colleague@example.com"))) + .text("Please take a look.") + .build()); + System.out.println(forward); + + // Delete a message. + messages.delete(INBOX_ID, messageId); + } +} diff --git a/examples/java/io/mailtrap/examples/inbound/InboundThreadsExample.java b/examples/java/io/mailtrap/examples/inbound/InboundThreadsExample.java new file mode 100644 index 0000000..37dc500 --- /dev/null +++ b/examples/java/io/mailtrap/examples/inbound/InboundThreadsExample.java @@ -0,0 +1,37 @@ +package io.mailtrap.examples.inbound; + +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; + +public class InboundThreadsExample { + + private static final String TOKEN = ""; + private static final long INBOX_ID = 1L; + + public static void main(String[] args) { + final var config = new MailtrapConfig.Builder() + .token(TOKEN) + .build(); + + final var client = MailtrapClientFactory.createMailtrapClient(config); + final var threads = client.inboundApi().threads(); + + // List conversation threads. Pass the previous page's last id to paginate + // (null for the first page). + final var page = threads.list(INBOX_ID, null); + System.out.println("Total: " + page.getTotalCount()); + page.getData().forEach(thread -> System.out.println(" " + thread.getId() + " " + thread.getSubject())); + + if (page.getData().isEmpty()) { + return; + } + final var threadId = page.getData().get(0).getId(); + + // Get a single thread with its messages embedded (oldest first). + final var thread = threads.get(INBOX_ID, threadId); + System.out.println(thread); + + // Delete a thread. + threads.delete(INBOX_ID, threadId); + } +} diff --git a/src/main/java/io/mailtrap/api/inbound/InboundFolders.java b/src/main/java/io/mailtrap/api/inbound/InboundFolders.java new file mode 100644 index 0000000..93dc75a --- /dev/null +++ b/src/main/java/io/mailtrap/api/inbound/InboundFolders.java @@ -0,0 +1,52 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.model.request.inbound.CreateInboundFolderRequest; +import io.mailtrap.model.request.inbound.UpdateInboundFolderRequest; +import io.mailtrap.model.response.inbound.InboundFolder; + +import java.util.List; + +/** + * Interface representing the Mailtrap Inbound Email API for managing folders. + */ +public interface InboundFolders { + + /** + * List all inbound folders in the account. + * + * @return the list of inbound folders + */ + List getList(); + + /** + * Get an inbound folder by ID. + * + * @param folderId the folder ID + * @return the folder + */ + InboundFolder getById(long folderId); + + /** + * Create a new inbound folder. + * + * @param request the create request + * @return the created folder + */ + InboundFolder create(CreateInboundFolderRequest request); + + /** + * Rename an inbound folder. + * + * @param folderId the folder ID + * @param request the update request + * @return the updated folder + */ + InboundFolder update(long folderId, UpdateInboundFolderRequest request); + + /** + * Delete an inbound folder along with all of its inboxes. + * + * @param folderId the folder ID + */ + void delete(long folderId); +} diff --git a/src/main/java/io/mailtrap/api/inbound/InboundFoldersImpl.java b/src/main/java/io/mailtrap/api/inbound/InboundFoldersImpl.java new file mode 100644 index 0000000..e948a86 --- /dev/null +++ b/src/main/java/io/mailtrap/api/inbound/InboundFoldersImpl.java @@ -0,0 +1,66 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.Constants; +import io.mailtrap.api.apiresource.ApiResource; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.http.RequestData; +import io.mailtrap.model.request.inbound.CreateInboundFolderRequest; +import io.mailtrap.model.request.inbound.UpdateInboundFolderRequest; +import io.mailtrap.model.response.inbound.InboundFolder; + +import java.util.List; + +public class InboundFoldersImpl extends ApiResource implements InboundFolders { + + public InboundFoldersImpl(final MailtrapConfig config) { + super(config); + this.apiHost = Constants.GENERAL_HOST; + } + + @Override + public List getList() { + return httpClient.getList( + apiHost + "/api/inbound/folders", + new RequestData(), + InboundFolder.class + ); + } + + @Override + public InboundFolder getById(final long folderId) { + return httpClient.get( + String.format(apiHost + "/api/inbound/folders/%d", folderId), + new RequestData(), + InboundFolder.class + ); + } + + @Override + public InboundFolder create(final CreateInboundFolderRequest request) { + return httpClient.post( + apiHost + "/api/inbound/folders", + request, + new RequestData(), + InboundFolder.class + ); + } + + @Override + public InboundFolder update(final long folderId, final UpdateInboundFolderRequest request) { + return httpClient.patch( + String.format(apiHost + "/api/inbound/folders/%d", folderId), + request, + new RequestData(), + InboundFolder.class + ); + } + + @Override + public void delete(final long folderId) { + httpClient.delete( + String.format(apiHost + "/api/inbound/folders/%d", folderId), + new RequestData(), + Void.class + ); + } +} diff --git a/src/main/java/io/mailtrap/api/inbound/InboundInboxes.java b/src/main/java/io/mailtrap/api/inbound/InboundInboxes.java new file mode 100644 index 0000000..51b47b3 --- /dev/null +++ b/src/main/java/io/mailtrap/api/inbound/InboundInboxes.java @@ -0,0 +1,58 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.model.request.inbound.CreateInboundInboxRequest; +import io.mailtrap.model.request.inbound.UpdateInboundInboxRequest; +import io.mailtrap.model.response.inbound.InboundInbox; + +import java.util.List; + +/** + * Interface representing the Mailtrap Inbound Email API for managing inboxes + * within a folder. + */ +public interface InboundInboxes { + + /** + * List all inboxes in an inbound folder. + * + * @param folderId the folder ID + * @return the list of inboxes + */ + List getList(long folderId); + + /** + * Get an inbound inbox by ID. + * + * @param folderId the folder ID + * @param inboxId the inbox ID + * @return the inbox + */ + InboundInbox getById(long folderId, long inboxId); + + /** + * Create a new inbound inbox in a folder. + * + * @param folderId the folder ID + * @param request the create request + * @return the created inbox + */ + InboundInbox create(long folderId, CreateInboundInboxRequest request); + + /** + * Rename an inbound inbox. + * + * @param folderId the folder ID + * @param inboxId the inbox ID + * @param request the update request + * @return the updated inbox + */ + InboundInbox update(long folderId, long inboxId, UpdateInboundInboxRequest request); + + /** + * Delete an inbound inbox. + * + * @param folderId the folder ID + * @param inboxId the inbox ID + */ + void delete(long folderId, long inboxId); +} diff --git a/src/main/java/io/mailtrap/api/inbound/InboundInboxesImpl.java b/src/main/java/io/mailtrap/api/inbound/InboundInboxesImpl.java new file mode 100644 index 0000000..50ded2b --- /dev/null +++ b/src/main/java/io/mailtrap/api/inbound/InboundInboxesImpl.java @@ -0,0 +1,66 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.Constants; +import io.mailtrap.api.apiresource.ApiResource; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.http.RequestData; +import io.mailtrap.model.request.inbound.CreateInboundInboxRequest; +import io.mailtrap.model.request.inbound.UpdateInboundInboxRequest; +import io.mailtrap.model.response.inbound.InboundInbox; + +import java.util.List; + +public class InboundInboxesImpl extends ApiResource implements InboundInboxes { + + public InboundInboxesImpl(final MailtrapConfig config) { + super(config); + this.apiHost = Constants.GENERAL_HOST; + } + + @Override + public List getList(final long folderId) { + return httpClient.getList( + String.format(apiHost + "/api/inbound/folders/%d/inboxes", folderId), + new RequestData(), + InboundInbox.class + ); + } + + @Override + public InboundInbox getById(final long folderId, final long inboxId) { + return httpClient.get( + String.format(apiHost + "/api/inbound/folders/%d/inboxes/%d", folderId, inboxId), + new RequestData(), + InboundInbox.class + ); + } + + @Override + public InboundInbox create(final long folderId, final CreateInboundInboxRequest request) { + return httpClient.post( + String.format(apiHost + "/api/inbound/folders/%d/inboxes", folderId), + request, + new RequestData(), + InboundInbox.class + ); + } + + @Override + public InboundInbox update(final long folderId, final long inboxId, final UpdateInboundInboxRequest request) { + return httpClient.patch( + String.format(apiHost + "/api/inbound/folders/%d/inboxes/%d", folderId, inboxId), + request, + new RequestData(), + InboundInbox.class + ); + } + + @Override + public void delete(final long folderId, final long inboxId) { + httpClient.delete( + String.format(apiHost + "/api/inbound/folders/%d/inboxes/%d", folderId, inboxId), + new RequestData(), + Void.class + ); + } +} diff --git a/src/main/java/io/mailtrap/api/inbound/InboundMessages.java b/src/main/java/io/mailtrap/api/inbound/InboundMessages.java new file mode 100644 index 0000000..53edcea --- /dev/null +++ b/src/main/java/io/mailtrap/api/inbound/InboundMessages.java @@ -0,0 +1,71 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.model.request.inbound.InboundForwardRequest; +import io.mailtrap.model.request.inbound.InboundReplyRequest; +import io.mailtrap.model.response.inbound.InboundMessage; +import io.mailtrap.model.response.inbound.InboundMessagesListResponse; +import io.mailtrap.model.response.inbound.InboundSendResult; + +/** + * Interface representing the Mailtrap Inbound Email API for received messages. + */ +public interface InboundMessages { + + /** + * List received messages in an inbox. + * + * @param inboxId the inbox ID + * @param lastId pagination cursor from a previous response ({@code null} for + * the first page) + * @return a page of messages + */ + InboundMessagesListResponse list(long inboxId, String lastId); + + /** + * Get a single message with its body and attachment download URLs. + * + * @param inboxId the inbox ID + * @param messageId the message ID + * @return the message + */ + InboundMessage get(long inboxId, String messageId); + + /** + * Delete a message. + * + * @param inboxId the inbox ID + * @param messageId the message ID + */ + void delete(long inboxId, String messageId); + + /** + * Reply to a message (to the original sender). Sends a real email. + * + * @param inboxId the inbox ID + * @param messageId the message ID + * @param request the reply body + * @return the send result + */ + InboundSendResult reply(long inboxId, String messageId, InboundReplyRequest request); + + /** + * Reply to a message and copy the original's other recipients. Sends a real email. + * + * @param inboxId the inbox ID + * @param messageId the message ID + * @param request the reply body + * @return the send result + */ + InboundSendResult replyAll(long inboxId, String messageId, InboundReplyRequest request); + + /** + * Forward a message to new recipients (at least one {@code to} is required). + * Sends a real email. + * + * @param inboxId the inbox ID + * @param messageId the message ID + * @param request the forward body + * @return the send result + */ + InboundSendResult forward(long inboxId, String messageId, InboundForwardRequest request); +} diff --git a/src/main/java/io/mailtrap/api/inbound/InboundMessagesImpl.java b/src/main/java/io/mailtrap/api/inbound/InboundMessagesImpl.java new file mode 100644 index 0000000..e2632fc --- /dev/null +++ b/src/main/java/io/mailtrap/api/inbound/InboundMessagesImpl.java @@ -0,0 +1,85 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.Constants; +import io.mailtrap.MailtrapValidator; +import io.mailtrap.api.apiresource.ApiResourceWithValidation; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.http.RequestData; +import io.mailtrap.model.request.inbound.InboundForwardRequest; +import io.mailtrap.model.request.inbound.InboundReplyRequest; +import io.mailtrap.model.response.inbound.InboundMessage; +import io.mailtrap.model.response.inbound.InboundMessagesListResponse; +import io.mailtrap.model.response.inbound.InboundSendResult; + +import java.util.Optional; + +import static io.mailtrap.http.RequestData.entry; + +public class InboundMessagesImpl extends ApiResourceWithValidation implements InboundMessages { + + public InboundMessagesImpl(final MailtrapConfig config, final MailtrapValidator mailtrapValidator) { + super(config, mailtrapValidator); + this.apiHost = Constants.GENERAL_HOST; + } + + @Override + public InboundMessagesListResponse list(final long inboxId, final String lastId) { + final var queryParams = RequestData.buildQueryParams( + entry("last_id", Optional.ofNullable(lastId)) + ); + return httpClient.get( + String.format(apiHost + "/api/inbound/inboxes/%d/messages", inboxId), + new RequestData(queryParams), + InboundMessagesListResponse.class + ); + } + + @Override + public InboundMessage get(final long inboxId, final String messageId) { + return httpClient.get( + String.format(apiHost + "/api/inbound/inboxes/%d/messages/%s", inboxId, messageId), + new RequestData(), + InboundMessage.class + ); + } + + @Override + public void delete(final long inboxId, final String messageId) { + httpClient.delete( + String.format(apiHost + "/api/inbound/inboxes/%d/messages/%s", inboxId, messageId), + new RequestData(), + Void.class + ); + } + + @Override + public InboundSendResult reply(final long inboxId, final String messageId, final InboundReplyRequest request) { + return httpClient.post( + String.format(apiHost + "/api/inbound/inboxes/%d/messages/%s/reply", inboxId, messageId), + request, + new RequestData(), + InboundSendResult.class + ); + } + + @Override + public InboundSendResult replyAll(final long inboxId, final String messageId, final InboundReplyRequest request) { + return httpClient.post( + String.format(apiHost + "/api/inbound/inboxes/%d/messages/%s/reply_all", inboxId, messageId), + request, + new RequestData(), + InboundSendResult.class + ); + } + + @Override + public InboundSendResult forward(final long inboxId, final String messageId, final InboundForwardRequest request) { + validateRequestBodyAndThrowException(request); + return httpClient.post( + String.format(apiHost + "/api/inbound/inboxes/%d/messages/%s/forward", inboxId, messageId), + request, + new RequestData(), + InboundSendResult.class + ); + } +} diff --git a/src/main/java/io/mailtrap/api/inbound/InboundThreads.java b/src/main/java/io/mailtrap/api/inbound/InboundThreads.java new file mode 100644 index 0000000..e0b3db7 --- /dev/null +++ b/src/main/java/io/mailtrap/api/inbound/InboundThreads.java @@ -0,0 +1,37 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.model.response.inbound.InboundThread; +import io.mailtrap.model.response.inbound.InboundThreadsListResponse; + +/** + * Interface representing the Mailtrap Inbound Email API for conversation threads. + */ +public interface InboundThreads { + + /** + * List conversation threads in an inbox. + * + * @param inboxId the inbox ID + * @param lastId pagination cursor from a previous response ({@code null} for + * the first page) + * @return a page of threads + */ + InboundThreadsListResponse list(long inboxId, String lastId); + + /** + * Get a single thread with its messages embedded (oldest first). + * + * @param inboxId the inbox ID + * @param threadId the thread ID + * @return the thread + */ + InboundThread get(long inboxId, String threadId); + + /** + * Delete a thread. + * + * @param inboxId the inbox ID + * @param threadId the thread ID + */ + void delete(long inboxId, String threadId); +} diff --git a/src/main/java/io/mailtrap/api/inbound/InboundThreadsImpl.java b/src/main/java/io/mailtrap/api/inbound/InboundThreadsImpl.java new file mode 100644 index 0000000..32843ba --- /dev/null +++ b/src/main/java/io/mailtrap/api/inbound/InboundThreadsImpl.java @@ -0,0 +1,50 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.Constants; +import io.mailtrap.api.apiresource.ApiResource; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.http.RequestData; +import io.mailtrap.model.response.inbound.InboundThread; +import io.mailtrap.model.response.inbound.InboundThreadsListResponse; + +import java.util.Optional; + +import static io.mailtrap.http.RequestData.entry; + +public class InboundThreadsImpl extends ApiResource implements InboundThreads { + + public InboundThreadsImpl(final MailtrapConfig config) { + super(config); + this.apiHost = Constants.GENERAL_HOST; + } + + @Override + public InboundThreadsListResponse list(final long inboxId, final String lastId) { + final var queryParams = RequestData.buildQueryParams( + entry("last_id", Optional.ofNullable(lastId)) + ); + return httpClient.get( + String.format(apiHost + "/api/inbound/inboxes/%d/threads", inboxId), + new RequestData(queryParams), + InboundThreadsListResponse.class + ); + } + + @Override + public InboundThread get(final long inboxId, final String threadId) { + return httpClient.get( + String.format(apiHost + "/api/inbound/inboxes/%d/threads/%s", inboxId, threadId), + new RequestData(), + InboundThread.class + ); + } + + @Override + public void delete(final long inboxId, final String threadId) { + httpClient.delete( + String.format(apiHost + "/api/inbound/inboxes/%d/threads/%s", inboxId, threadId), + new RequestData(), + Void.class + ); + } +} diff --git a/src/main/java/io/mailtrap/client/MailtrapClient.java b/src/main/java/io/mailtrap/client/MailtrapClient.java index bb9505f..d07244d 100644 --- a/src/main/java/io/mailtrap/client/MailtrapClient.java +++ b/src/main/java/io/mailtrap/client/MailtrapClient.java @@ -62,6 +62,12 @@ public class MailtrapClient { @Getter private final MailtrapOrganizationsApi organizationsApi; + /** + * API for Mailtrap.io Inbound Email functionality + */ + @Getter + private final MailtrapInboundApi inboundApi; + /** * Utility class which holds sending context (which API to use: Email Sending API, Bulk Sending API or * Email Testing API, inbox id for Email Testing API) to make it possible to perform send directly from MailtrapClient diff --git a/src/main/java/io/mailtrap/client/api/MailtrapInboundApi.java b/src/main/java/io/mailtrap/client/api/MailtrapInboundApi.java new file mode 100644 index 0000000..4cf1a09 --- /dev/null +++ b/src/main/java/io/mailtrap/client/api/MailtrapInboundApi.java @@ -0,0 +1,27 @@ +package io.mailtrap.client.api; + +import io.mailtrap.api.inbound.InboundFolders; +import io.mailtrap.api.inbound.InboundInboxes; +import io.mailtrap.api.inbound.InboundMessages; +import io.mailtrap.api.inbound.InboundThreads; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; + +/** + * Groups the token-scoped Inbound Email API resources (folders, inboxes, + * messages, threads). + */ +@Getter +@Accessors(fluent = true) +@RequiredArgsConstructor +public class MailtrapInboundApi { + + private final InboundFolders folders; + + private final InboundInboxes inboxes; + + private final InboundMessages messages; + + private final InboundThreads threads; +} diff --git a/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java b/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java index 9edf03a..b686bc4 100644 --- a/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java +++ b/src/main/java/io/mailtrap/factory/MailtrapClientFactory.java @@ -14,6 +14,10 @@ import io.mailtrap.api.contactlists.ContactListsImpl; import io.mailtrap.api.contacts.ContactsImpl; import io.mailtrap.api.emailtemplates.EmailTemplatesImpl; +import io.mailtrap.api.inbound.InboundFoldersImpl; +import io.mailtrap.api.inbound.InboundInboxesImpl; +import io.mailtrap.api.inbound.InboundMessagesImpl; +import io.mailtrap.api.inbound.InboundThreadsImpl; import io.mailtrap.api.inboxes.InboxesImpl; import io.mailtrap.api.messages.MessagesImpl; import io.mailtrap.api.permissions.PermissionsImpl; @@ -73,11 +77,21 @@ public static MailtrapClient createMailtrapClient(final MailtrapConfig config) { final var contactsApi = createContactsApi(config); final var emailTemplatesApi = createEmailTemplatesApi(config); final var organizationsApi = createOrganizationsApi(config); + final var inboundApi = createInboundApi(config); final var sendingContextHolder = configureSendingContext(config); return new MailtrapClient(sendingApi, testingApi, bulkSendingApi, generalApi, contactsApi, emailTemplatesApi, - organizationsApi, sendingContextHolder); + organizationsApi, inboundApi, sendingContextHolder); + } + + private static MailtrapInboundApi createInboundApi(final MailtrapConfig config) { + final var folders = new InboundFoldersImpl(config); + final var inboxes = new InboundInboxesImpl(config); + final var messages = new InboundMessagesImpl(config, VALIDATOR); + final var threads = new InboundThreadsImpl(config); + + return new MailtrapInboundApi(folders, inboxes, messages, threads); } private static MailtrapOrganizationsApi createOrganizationsApi(final MailtrapConfig config) { diff --git a/src/main/java/io/mailtrap/model/WebhookType.java b/src/main/java/io/mailtrap/model/WebhookType.java index d6a6a8b..7dc88dd 100644 --- a/src/main/java/io/mailtrap/model/WebhookType.java +++ b/src/main/java/io/mailtrap/model/WebhookType.java @@ -6,7 +6,8 @@ public enum WebhookType { EMAIL_SENDING("email_sending"), AUDIT_LOG("audit_log"), - CAMPAIGNS("campaigns"); + CAMPAIGNS("campaigns"), + INBOUND_RECEIVING("inbound_receiving"); private final String value; diff --git a/src/main/java/io/mailtrap/model/request/inbound/CreateInboundFolderRequest.java b/src/main/java/io/mailtrap/model/request/inbound/CreateInboundFolderRequest.java new file mode 100644 index 0000000..f82c569 --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/inbound/CreateInboundFolderRequest.java @@ -0,0 +1,16 @@ +package io.mailtrap.model.request.inbound; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.mailtrap.model.AbstractModel; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CreateInboundFolderRequest extends AbstractModel { + + private String name; +} diff --git a/src/main/java/io/mailtrap/model/request/inbound/CreateInboundInboxRequest.java b/src/main/java/io/mailtrap/model/request/inbound/CreateInboundInboxRequest.java new file mode 100644 index 0000000..06accb5 --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/inbound/CreateInboundInboxRequest.java @@ -0,0 +1,24 @@ +package io.mailtrap.model.request.inbound; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.AbstractModel; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +/** + * Omit {@code domainId} for a Mailtrap-hosted inbox; pass it to create a + * custom-domain (catch-all) inbox. + */ +@Getter +@Setter +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CreateInboundInboxRequest extends AbstractModel { + + private String name; + + @JsonProperty("domain_id") + private Long domainId; +} diff --git a/src/main/java/io/mailtrap/model/request/inbound/InboundForwardRequest.java b/src/main/java/io/mailtrap/model/request/inbound/InboundForwardRequest.java new file mode 100644 index 0000000..cccddff --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/inbound/InboundForwardRequest.java @@ -0,0 +1,60 @@ +package io.mailtrap.model.request.inbound; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.AbstractModel; +import io.mailtrap.model.request.emails.Address; +import io.mailtrap.model.request.emails.EmailAttachment; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Map; + +/** + * Body for forwarding an inbound message. Requires at least one {@code to} + * recipient. {@code from} is rejected for Mailtrap-hosted inboxes and required + * for custom-domain inboxes. + */ +@Getter +@Setter +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class InboundForwardRequest extends AbstractModel { + + @Valid + private Address from; + + @NotEmpty + @Valid + private List
to; + + @Valid + private List
cc; + + @Valid + private List
bcc; + + @JsonProperty("reply_to") + @Valid + private Address replyTo; + + private String subject; + + private String text; + + private String html; + + private String category; + + @Valid + private List attachments; + + private Map headers; + + @JsonProperty("custom_variables") + private Map customVariables; +} diff --git a/src/main/java/io/mailtrap/model/request/inbound/InboundReplyRequest.java b/src/main/java/io/mailtrap/model/request/inbound/InboundReplyRequest.java new file mode 100644 index 0000000..68be911 --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/inbound/InboundReplyRequest.java @@ -0,0 +1,57 @@ +package io.mailtrap.model.request.inbound; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.AbstractModel; +import io.mailtrap.model.request.emails.Address; +import io.mailtrap.model.request.emails.EmailAttachment; +import jakarta.validation.Valid; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Map; + +/** + * Body for replying to (or reply-all-ing) an inbound message. {@code from} is + * rejected for Mailtrap-hosted inboxes and required for custom-domain inboxes. + */ +@Getter +@Setter +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class InboundReplyRequest extends AbstractModel { + + @Valid + private Address from; + + @Valid + private List
to; + + @Valid + private List
cc; + + @Valid + private List
bcc; + + @JsonProperty("reply_to") + @Valid + private Address replyTo; + + private String subject; + + private String text; + + private String html; + + private String category; + + @Valid + private List attachments; + + private Map headers; + + @JsonProperty("custom_variables") + private Map customVariables; +} diff --git a/src/main/java/io/mailtrap/model/request/inbound/UpdateInboundFolderRequest.java b/src/main/java/io/mailtrap/model/request/inbound/UpdateInboundFolderRequest.java new file mode 100644 index 0000000..c95c42a --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/inbound/UpdateInboundFolderRequest.java @@ -0,0 +1,16 @@ +package io.mailtrap.model.request.inbound; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.mailtrap.model.AbstractModel; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class UpdateInboundFolderRequest extends AbstractModel { + + private String name; +} diff --git a/src/main/java/io/mailtrap/model/request/inbound/UpdateInboundInboxRequest.java b/src/main/java/io/mailtrap/model/request/inbound/UpdateInboundInboxRequest.java new file mode 100644 index 0000000..d05d96c --- /dev/null +++ b/src/main/java/io/mailtrap/model/request/inbound/UpdateInboundInboxRequest.java @@ -0,0 +1,16 @@ +package io.mailtrap.model.request.inbound; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.mailtrap.model.AbstractModel; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class UpdateInboundInboxRequest extends AbstractModel { + + private String name; +} diff --git a/src/main/java/io/mailtrap/model/request/webhooks/WebhookInput.java b/src/main/java/io/mailtrap/model/request/webhooks/WebhookInput.java index f6c339e..0921563 100644 --- a/src/main/java/io/mailtrap/model/request/webhooks/WebhookInput.java +++ b/src/main/java/io/mailtrap/model/request/webhooks/WebhookInput.java @@ -39,4 +39,7 @@ public class WebhookInput { @JsonProperty("domain_id") private Long domainId; + @JsonProperty("inbound_inbox_id") + private Long inboundInboxId; + } diff --git a/src/main/java/io/mailtrap/model/response/emaillogs/EmailLogMessage.java b/src/main/java/io/mailtrap/model/response/emaillogs/EmailLogMessage.java index a52535c..10ac127 100644 --- a/src/main/java/io/mailtrap/model/response/emaillogs/EmailLogMessage.java +++ b/src/main/java/io/mailtrap/model/response/emaillogs/EmailLogMessage.java @@ -21,6 +21,18 @@ public class EmailLogMessage { @JsonProperty("subject") private String subject; + @JsonProperty("rfc_message_id") + private String rfcMessageId; + + @JsonProperty("in_reply_to") + private String inReplyTo; + + @JsonProperty("references") + private List references; + + @JsonProperty("thread_id") + private String threadId; + @JsonProperty("from") private String from; diff --git a/src/main/java/io/mailtrap/model/response/emaillogs/EmailLogMessageSummary.java b/src/main/java/io/mailtrap/model/response/emaillogs/EmailLogMessageSummary.java index 92f0b64..1d00f03 100644 --- a/src/main/java/io/mailtrap/model/response/emaillogs/EmailLogMessageSummary.java +++ b/src/main/java/io/mailtrap/model/response/emaillogs/EmailLogMessageSummary.java @@ -6,6 +6,7 @@ import java.time.OffsetDateTime; import java.util.Collections; +import java.util.List; import java.util.Map; @Data @@ -20,6 +21,18 @@ public class EmailLogMessageSummary { @JsonProperty("subject") private String subject; + @JsonProperty("rfc_message_id") + private String rfcMessageId; + + @JsonProperty("in_reply_to") + private String inReplyTo; + + @JsonProperty("references") + private List references; + + @JsonProperty("thread_id") + private String threadId; + @JsonProperty("from") private String from; diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundAttachment.java b/src/main/java/io/mailtrap/model/response/inbound/InboundAttachment.java new file mode 100644 index 0000000..4db1121 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundAttachment.java @@ -0,0 +1,36 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.time.OffsetDateTime; + +/** + * Attachment metadata on a received message. {@code downloadUrl} and + * {@code downloadUrlExpiresAt} are only populated on get-by-id and thread responses. + */ +@Data +public class InboundAttachment { + + @JsonProperty("attachment_id") + private String attachmentId; + + private Integer size; + + private String filename; + + @JsonProperty("content_type") + private String contentType; + + @JsonProperty("content_disposition") + private String contentDisposition; + + @JsonProperty("content_id") + private String contentId; + + @JsonProperty("download_url") + private String downloadUrl; + + @JsonProperty("download_url_expires_at") + private OffsetDateTime downloadUrlExpiresAt; +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundFolder.java b/src/main/java/io/mailtrap/model/response/inbound/InboundFolder.java new file mode 100644 index 0000000..55f36d7 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundFolder.java @@ -0,0 +1,11 @@ +package io.mailtrap.model.response.inbound; + +import lombok.Data; + +@Data +public class InboundFolder { + + private long id; + + private String name; +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundInbox.java b/src/main/java/io/mailtrap/model/response/inbound/InboundInbox.java new file mode 100644 index 0000000..0203938 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundInbox.java @@ -0,0 +1,17 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class InboundInbox { + + private long id; + + private String name; + + private String address; + + @JsonProperty("domain_id") + private Long domainId; +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundMessage.java b/src/main/java/io/mailtrap/model/response/inbound/InboundMessage.java new file mode 100644 index 0000000..5968894 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundMessage.java @@ -0,0 +1,73 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; + +/** + * A received inbound message. The body and raw-message fields ({@code htmlBody}, + * {@code textBody}, {@code rawMessageUrl}, ...) and attachment download URLs are + * populated on get-by-id; list items carry only the summary fields. + */ +@Data +public class InboundMessage { + + private String id; + + @JsonProperty("inbox_id") + private Long inboxId; + + private String from; + + private List to; + + private List cc; + + private List bcc; + + @JsonProperty("reply_to") + private String replyTo; + + private String subject; + + @JsonProperty("rfc_message_id") + private String rfcMessageId; + + @JsonProperty("in_reply_to") + private String inReplyTo; + + private List references; + + private Map headers; + + private Integer size; + + @JsonProperty("html_size") + private Integer htmlSize; + + @JsonProperty("text_size") + private Integer textSize; + + @JsonProperty("received_at") + private OffsetDateTime receivedAt; + + @JsonProperty("thread_id") + private String threadId; + + private List attachments; + + @JsonProperty("raw_message_url") + private String rawMessageUrl; + + @JsonProperty("raw_message_expires_at") + private OffsetDateTime rawMessageExpiresAt; + + @JsonProperty("html_body") + private String htmlBody; + + @JsonProperty("text_body") + private String textBody; +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundMessageDirection.java b/src/main/java/io/mailtrap/model/response/inbound/InboundMessageDirection.java new file mode 100644 index 0000000..fc5ebd7 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundMessageDirection.java @@ -0,0 +1,34 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Direction of a message inside a thread: {@code inbound} (received) or + * {@code outbound} (sent as a reply, reply-all, or forward). + */ +public enum InboundMessageDirection { + INBOUND("inbound"), + OUTBOUND("outbound"); + + private final String value; + + InboundMessageDirection(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static InboundMessageDirection fromValue(String value) { + for (InboundMessageDirection direction : InboundMessageDirection.values()) { + if (direction.value.equalsIgnoreCase(value)) { + return direction; + } + } + throw new IllegalArgumentException("Unknown InboundMessageDirection value: " + value); + } +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundMessageVisibilityStatus.java b/src/main/java/io/mailtrap/model/response/inbound/InboundMessageVisibilityStatus.java new file mode 100644 index 0000000..169554f --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundMessageVisibilityStatus.java @@ -0,0 +1,34 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Visibility of a message inside a thread. {@code placeholder} entries omit most + * fields (e.g. a message the caller cannot fully see). + */ +public enum InboundMessageVisibilityStatus { + AVAILABLE("available"), + PLACEHOLDER("placeholder"); + + private final String value; + + InboundMessageVisibilityStatus(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static InboundMessageVisibilityStatus fromValue(String value) { + for (InboundMessageVisibilityStatus status : InboundMessageVisibilityStatus.values()) { + if (status.value.equalsIgnoreCase(value)) { + return status; + } + } + throw new IllegalArgumentException("Unknown InboundMessageVisibilityStatus value: " + value); + } +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundMessagesListResponse.java b/src/main/java/io/mailtrap/model/response/inbound/InboundMessagesListResponse.java new file mode 100644 index 0000000..659c766 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundMessagesListResponse.java @@ -0,0 +1,19 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +@Data +public class InboundMessagesListResponse { + + @JsonProperty("data") + private List data; + + @JsonProperty("total_count") + private int totalCount; + + @JsonProperty("last_id") + private String lastId; +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundSendResult.java b/src/main/java/io/mailtrap/model/response/inbound/InboundSendResult.java new file mode 100644 index 0000000..e448d36 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundSendResult.java @@ -0,0 +1,16 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * Result of a reply, reply-all, or forward (sends a real email). + */ +@Data +public class InboundSendResult { + + @JsonProperty("message_ids") + private List messageIds; +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundThread.java b/src/main/java/io/mailtrap/model/response/inbound/InboundThread.java new file mode 100644 index 0000000..5b442b4 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundThread.java @@ -0,0 +1,47 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.time.OffsetDateTime; +import java.util.List; + +/** + * A conversation thread. {@code messages} is populated on get-by-id; list items + * carry only the summary fields. + */ +@Data +public class InboundThread { + + private String id; + + private String subject; + + @JsonProperty("message_count") + private Integer messageCount; + + private Integer size; + + @JsonProperty("first_message_at") + private OffsetDateTime firstMessageAt; + + @JsonProperty("last_received_at") + private OffsetDateTime lastReceivedAt; + + @JsonProperty("last_sent_at") + private OffsetDateTime lastSentAt; + + @JsonProperty("last_activity_at") + private OffsetDateTime lastActivityAt; + + @JsonProperty("last_message_id") + private String lastMessageId; + + private List senders; + + private List recipients; + + private List attachments; + + private List messages; +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundThreadMessage.java b/src/main/java/io/mailtrap/model/response/inbound/InboundThreadMessage.java new file mode 100644 index 0000000..8b0a498 --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundThreadMessage.java @@ -0,0 +1,70 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.mailtrap.model.response.emaillogs.MessageStatus; +import lombok.Data; + +import java.time.OffsetDateTime; +import java.util.List; + +/** + * A message inside a thread. Only {@code visibilityStatus} and {@code direction} + * are guaranteed; {@code placeholder} entries omit the rest. + */ +@Data +public class InboundThreadMessage { + + @JsonProperty("visibility_status") + private InboundMessageVisibilityStatus visibilityStatus; + + private InboundMessageDirection direction; + + private String id; + + @JsonProperty("message_group_id") + private String messageGroupId; + + private String subject; + + @JsonProperty("rfc_message_id") + private String rfcMessageId; + + @JsonProperty("in_reply_to") + private String inReplyTo; + + private List references; + + private String from; + + private List to; + + private List cc; + + private List bcc; + + @JsonProperty("reply_to") + private String replyTo; + + @JsonProperty("created_at") + private OffsetDateTime createdAt; + + @JsonProperty("email_size") + private Integer emailSize; + + @JsonProperty("text_body") + private String textBody; + + @JsonProperty("html_body") + private String htmlBody; + + private List attachments; + + @JsonProperty("delivery_status") + private MessageStatus deliveryStatus; + + @JsonProperty("delivered_at") + private OffsetDateTime deliveredAt; + + @JsonProperty("bounced_at") + private OffsetDateTime bouncedAt; +} diff --git a/src/main/java/io/mailtrap/model/response/inbound/InboundThreadsListResponse.java b/src/main/java/io/mailtrap/model/response/inbound/InboundThreadsListResponse.java new file mode 100644 index 0000000..d51fc7a --- /dev/null +++ b/src/main/java/io/mailtrap/model/response/inbound/InboundThreadsListResponse.java @@ -0,0 +1,19 @@ +package io.mailtrap.model.response.inbound; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +@Data +public class InboundThreadsListResponse { + + @JsonProperty("data") + private List data; + + @JsonProperty("total_count") + private int totalCount; + + @JsonProperty("last_id") + private String lastId; +} diff --git a/src/main/java/io/mailtrap/model/response/sendingdomains/SendingDomainsResponse.java b/src/main/java/io/mailtrap/model/response/sendingdomains/SendingDomainsResponse.java index 37a3ef4..2790696 100644 --- a/src/main/java/io/mailtrap/model/response/sendingdomains/SendingDomainsResponse.java +++ b/src/main/java/io/mailtrap/model/response/sendingdomains/SendingDomainsResponse.java @@ -45,6 +45,12 @@ public class SendingDomainsResponse { @JsonProperty("alert_recipient_email") private String alertRecipientEmail; + @JsonProperty("inbound_enabled") + private boolean inboundEnabled; + + @JsonProperty("inbound_verified") + private boolean inboundVerified; + @JsonProperty("permissions") private SendingDomainPermission permission; diff --git a/src/main/java/io/mailtrap/model/response/webhooks/Webhook.java b/src/main/java/io/mailtrap/model/response/webhooks/Webhook.java index e29b1d3..fc9d176 100644 --- a/src/main/java/io/mailtrap/model/response/webhooks/Webhook.java +++ b/src/main/java/io/mailtrap/model/response/webhooks/Webhook.java @@ -30,6 +30,9 @@ public class Webhook { @JsonProperty("domain_id") private Long domainId; + @JsonProperty("inbound_inbox_id") + private Long inboundInboxId; + @JsonProperty("event_types") private List eventTypes; diff --git a/src/test/java/io/mailtrap/api/emaillogs/EmailLogsImplTest.java b/src/test/java/io/mailtrap/api/emaillogs/EmailLogsImplTest.java index 7d7ef93..1e6ea7b 100644 --- a/src/test/java/io/mailtrap/api/emaillogs/EmailLogsImplTest.java +++ b/src/test/java/io/mailtrap/api/emaillogs/EmailLogsImplTest.java @@ -81,6 +81,8 @@ void list_withNoParams_returnsPaginatedResponse() { assertEquals(3938, msg.getSendingDomainId()); assertEquals(2, msg.getOpensCount()); assertEquals(1, msg.getClicksCount()); + assertEquals("", msg.getRfcMessageId()); + assertEquals("thr_abc123", msg.getThreadId()); } @Test @@ -122,6 +124,10 @@ void get_byMessageId_returnsEmailLogMessage() { assertEquals(MessageStatus.DELIVERED, message.getStatus()); assertEquals("Welcome to our service", message.getSubject()); assertNotNull(message.getRawMessageUrl()); + assertEquals("", message.getRfcMessageId()); + assertEquals("", message.getInReplyTo()); + assertEquals(List.of(""), message.getReferences()); + assertEquals("thr_abc123", message.getThreadId()); assertNotNull(message.getEvents()); assertEquals(1, message.getEvents().size()); assertEquals(EmailLogEventType.CLICK, message.getEvents().get(0).getEventType()); diff --git a/src/test/java/io/mailtrap/api/inbound/InboundFoldersImplTest.java b/src/test/java/io/mailtrap/api/inbound/InboundFoldersImplTest.java new file mode 100644 index 0000000..d7f9d41 --- /dev/null +++ b/src/test/java/io/mailtrap/api/inbound/InboundFoldersImplTest.java @@ -0,0 +1,94 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.Constants; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.request.inbound.CreateInboundFolderRequest; +import io.mailtrap.model.request.inbound.UpdateInboundFolderRequest; +import io.mailtrap.model.response.inbound.InboundFolder; +import io.mailtrap.testutils.BaseTest; +import io.mailtrap.testutils.DataMock; +import io.mailtrap.testutils.TestHttpClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class InboundFoldersImplTest extends BaseTest { + + private static final long FOLDER_ID = 101L; + + private InboundFolders api; + + @BeforeEach + void init() { + final String foldersUrl = Constants.GENERAL_HOST + "/api/inbound/folders"; + final String folderUrl = foldersUrl + "/" + FOLDER_ID; + + final TestHttpClient httpClient = new TestHttpClient(List.of( + DataMock.build(foldersUrl, "GET", null, "api/inbound/listInboundFoldersResponse.json"), + DataMock.build(folderUrl, "GET", null, "api/inbound/getInboundFolderResponse.json"), + DataMock.build(foldersUrl, "POST", "api/inbound/createInboundFolderRequest.json", + "api/inbound/createInboundFolderResponse.json"), + DataMock.build(folderUrl, "PATCH", "api/inbound/updateInboundFolderRequest.json", + "api/inbound/updateInboundFolderResponse.json"), + DataMock.build(folderUrl, "DELETE", null, null) + )); + + final MailtrapConfig testConfig = new MailtrapConfig.Builder() + .httpClient(httpClient) + .token("dummy_token") + .build(); + + api = MailtrapClientFactory.createMailtrapClient(testConfig).inboundApi().folders(); + } + + @Test + void getList_returnsFolders() { + final List folders = api.getList(); + + assertNotNull(folders); + assertEquals(2, folders.size()); + assertEquals(101, folders.get(0).getId()); + assertEquals("Support", folders.get(0).getName()); + assertEquals("Sales", folders.get(1).getName()); + } + + @Test + void getById_returnsFolder() { + final InboundFolder folder = api.getById(FOLDER_ID); + + assertNotNull(folder); + assertEquals(101, folder.getId()); + assertEquals("Support", folder.getName()); + } + + @Test + void create_returnsCreatedFolder() { + final InboundFolder folder = api.create( + CreateInboundFolderRequest.builder().name("Support").build()); + + assertNotNull(folder); + assertEquals(103, folder.getId()); + assertEquals("Support", folder.getName()); + } + + @Test + void update_returnsUpdatedFolder() { + final InboundFolder folder = api.update(FOLDER_ID, + UpdateInboundFolderRequest.builder().name("Renamed folder").build()); + + assertNotNull(folder); + assertEquals(101, folder.getId()); + assertEquals("Renamed folder", folder.getName()); + } + + @Test + void delete_doesNotThrow() { + assertDoesNotThrow(() -> api.delete(FOLDER_ID)); + } +} diff --git a/src/test/java/io/mailtrap/api/inbound/InboundInboxesImplTest.java b/src/test/java/io/mailtrap/api/inbound/InboundInboxesImplTest.java new file mode 100644 index 0000000..42a66c7 --- /dev/null +++ b/src/test/java/io/mailtrap/api/inbound/InboundInboxesImplTest.java @@ -0,0 +1,97 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.Constants; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.request.inbound.CreateInboundInboxRequest; +import io.mailtrap.model.request.inbound.UpdateInboundInboxRequest; +import io.mailtrap.model.response.inbound.InboundInbox; +import io.mailtrap.testutils.BaseTest; +import io.mailtrap.testutils.DataMock; +import io.mailtrap.testutils.TestHttpClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +class InboundInboxesImplTest extends BaseTest { + + private static final long FOLDER_ID = 101L; + private static final long INBOX_ID = 201L; + + private InboundInboxes api; + + @BeforeEach + void init() { + final String inboxesUrl = Constants.GENERAL_HOST + "/api/inbound/folders/" + FOLDER_ID + "/inboxes"; + final String inboxUrl = inboxesUrl + "/" + INBOX_ID; + + final TestHttpClient httpClient = new TestHttpClient(List.of( + DataMock.build(inboxesUrl, "GET", null, "api/inbound/listInboundInboxesResponse.json"), + DataMock.build(inboxUrl, "GET", null, "api/inbound/getInboundInboxResponse.json"), + DataMock.build(inboxesUrl, "POST", "api/inbound/createInboundInboxRequest.json", + "api/inbound/createInboundInboxResponse.json"), + DataMock.build(inboxUrl, "PATCH", "api/inbound/updateInboundInboxRequest.json", + "api/inbound/updateInboundInboxResponse.json"), + DataMock.build(inboxUrl, "DELETE", null, null) + )); + + final MailtrapConfig testConfig = new MailtrapConfig.Builder() + .httpClient(httpClient) + .token("dummy_token") + .build(); + + api = MailtrapClientFactory.createMailtrapClient(testConfig).inboundApi().inboxes(); + } + + @Test + void getList_returnsInboxes() { + final List inboxes = api.getList(FOLDER_ID); + + assertNotNull(inboxes); + assertEquals(2, inboxes.size()); + assertEquals("support@inbound-mailtrap.io", inboxes.get(0).getAddress()); + assertNull(inboxes.get(0).getDomainId()); + assertEquals(6L, inboxes.get(1).getDomainId()); + } + + @Test + void getById_returnsInbox() { + final InboundInbox inbox = api.getById(FOLDER_ID, INBOX_ID); + + assertNotNull(inbox); + assertEquals(201, inbox.getId()); + assertEquals("Support inbox", inbox.getName()); + assertEquals("support@inbound-mailtrap.io", inbox.getAddress()); + } + + @Test + void create_withDomainId_returnsCreatedInbox() { + final InboundInbox inbox = api.create(FOLDER_ID, + CreateInboundInboxRequest.builder().name("Custom domain inbox").domainId(6L).build()); + + assertNotNull(inbox); + assertEquals(203, inbox.getId()); + assertEquals(6L, inbox.getDomainId()); + } + + @Test + void update_returnsUpdatedInbox() { + final InboundInbox inbox = api.update(FOLDER_ID, INBOX_ID, + UpdateInboundInboxRequest.builder().name("Renamed inbox").build()); + + assertNotNull(inbox); + assertEquals(201, inbox.getId()); + assertEquals("Renamed inbox", inbox.getName()); + } + + @Test + void delete_doesNotThrow() { + assertDoesNotThrow(() -> api.delete(FOLDER_ID, INBOX_ID)); + } +} diff --git a/src/test/java/io/mailtrap/api/inbound/InboundMessagesImplTest.java b/src/test/java/io/mailtrap/api/inbound/InboundMessagesImplTest.java new file mode 100644 index 0000000..3a7be8d --- /dev/null +++ b/src/test/java/io/mailtrap/api/inbound/InboundMessagesImplTest.java @@ -0,0 +1,152 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.Constants; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.exception.InvalidRequestBodyException; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.request.emails.Address; +import io.mailtrap.model.request.inbound.InboundForwardRequest; +import io.mailtrap.model.request.inbound.InboundReplyRequest; +import io.mailtrap.model.response.inbound.InboundMessage; +import io.mailtrap.model.response.inbound.InboundMessagesListResponse; +import io.mailtrap.model.response.inbound.InboundSendResult; +import io.mailtrap.testutils.BaseTest; +import io.mailtrap.testutils.DataMock; +import io.mailtrap.testutils.TestHttpClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class InboundMessagesImplTest extends BaseTest { + + private static final long INBOX_ID = 201L; + private static final String MESSAGE_ID = "msg_1"; + + private InboundMessages api; + + @BeforeEach + void init() { + final String messagesUrl = Constants.GENERAL_HOST + "/api/inbound/inboxes/" + INBOX_ID + "/messages"; + final String messageUrl = messagesUrl + "/" + MESSAGE_ID; + + final TestHttpClient httpClient = new TestHttpClient(List.of( + DataMock.build(messagesUrl, "GET", null, "api/inbound/listInboundMessagesResponse.json"), + DataMock.build(messagesUrl, "GET", null, "api/inbound/listInboundMessagesResponse.json", + Map.of("last_id", "msg_2")), + DataMock.build(messageUrl, "GET", null, "api/inbound/getInboundMessageResponse.json"), + DataMock.build(messageUrl, "DELETE", null, null), + DataMock.build(messageUrl + "/reply", "POST", "api/inbound/replyInboundMessageRequest.json", + "api/inbound/sendInboundMessageResponse.json"), + DataMock.build(messageUrl + "/reply_all", "POST", "api/inbound/replyAllInboundMessageRequest.json", + "api/inbound/sendInboundMessageResponse.json"), + DataMock.build(messageUrl + "/forward", "POST", "api/inbound/forwardInboundMessageRequest.json", + "api/inbound/sendInboundMessageResponse.json") + )); + + final MailtrapConfig testConfig = new MailtrapConfig.Builder() + .httpClient(httpClient) + .token("dummy_token") + .build(); + + api = MailtrapClientFactory.createMailtrapClient(testConfig).inboundApi().messages(); + } + + @Test + void list_withoutCursor_returnsPage() { + final InboundMessagesListResponse response = api.list(INBOX_ID, null); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + assertEquals(2, response.getTotalCount()); + assertEquals("msg_2", response.getLastId()); + assertEquals("customer@example.com", response.getData().get(0).getFrom()); + assertEquals("thr_1", response.getData().get(0).getThreadId()); + } + + @Test + void list_withCursor_returnsPage() { + final InboundMessagesListResponse response = api.list(INBOX_ID, "msg_2"); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + } + + @Test + void get_returnsMessageWithBodyAndAttachments() { + final InboundMessage message = api.get(INBOX_ID, MESSAGE_ID); + + assertNotNull(message); + assertEquals("msg_1", message.getId()); + assertEquals(201L, message.getInboxId()); + assertEquals("Support request", message.getSubject()); + assertEquals("

Hello, I need help.

", message.getHtmlBody()); + assertNotNull(message.getReceivedAt()); + assertNotNull(message.getAttachments()); + assertEquals(1, message.getAttachments().size()); + assertEquals("invoice.pdf", message.getAttachments().get(0).getFilename()); + assertEquals("https://example.com/download/att_1", message.getAttachments().get(0).getDownloadUrl()); + } + + @Test + void delete_doesNotThrow() { + assertDoesNotThrow(() -> api.delete(INBOX_ID, MESSAGE_ID)); + } + + @Test + void reply_returnsSendResult() { + final InboundReplyRequest request = InboundReplyRequest.builder() + .to(List.of(new Address("customer@example.com"))) + .subject("Re: Support request") + .text("Thanks for reaching out, we are on it!") + .build(); + + final InboundSendResult result = api.reply(INBOX_ID, MESSAGE_ID, request); + + assertNotNull(result); + assertEquals(List.of("0000000000000001"), result.getMessageIds()); + } + + @Test + void replyAll_returnsSendResult() { + final InboundReplyRequest request = InboundReplyRequest.builder() + .to(List.of(new Address("customer@example.com"))) + .cc(List.of(new Address("cc@example.com"))) + .subject("Re: Support request") + .text("Thanks everyone!") + .build(); + + final InboundSendResult result = api.replyAll(INBOX_ID, MESSAGE_ID, request); + + assertNotNull(result); + assertEquals(List.of("0000000000000001"), result.getMessageIds()); + } + + @Test + void forward_returnsSendResult() { + final InboundForwardRequest request = InboundForwardRequest.builder() + .to(List.of(new Address("teammate@example.com"))) + .text("FYI, please take a look.") + .build(); + + final InboundSendResult result = api.forward(INBOX_ID, MESSAGE_ID, request); + + assertNotNull(result); + assertEquals(List.of("0000000000000001"), result.getMessageIds()); + } + + @Test + void forward_withoutRecipients_throws() { + final InboundForwardRequest request = InboundForwardRequest.builder() + .text("FYI") + .build(); + + assertThrows(InvalidRequestBodyException.class, () -> api.forward(INBOX_ID, MESSAGE_ID, request)); + } +} diff --git a/src/test/java/io/mailtrap/api/inbound/InboundThreadsImplTest.java b/src/test/java/io/mailtrap/api/inbound/InboundThreadsImplTest.java new file mode 100644 index 0000000..2607c49 --- /dev/null +++ b/src/test/java/io/mailtrap/api/inbound/InboundThreadsImplTest.java @@ -0,0 +1,89 @@ +package io.mailtrap.api.inbound; + +import io.mailtrap.Constants; +import io.mailtrap.config.MailtrapConfig; +import io.mailtrap.factory.MailtrapClientFactory; +import io.mailtrap.model.response.emaillogs.MessageStatus; +import io.mailtrap.model.response.inbound.InboundMessageDirection; +import io.mailtrap.model.response.inbound.InboundThread; +import io.mailtrap.model.response.inbound.InboundThreadsListResponse; +import io.mailtrap.testutils.BaseTest; +import io.mailtrap.testutils.DataMock; +import io.mailtrap.testutils.TestHttpClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class InboundThreadsImplTest extends BaseTest { + + private static final long INBOX_ID = 201L; + private static final String THREAD_ID = "thr_1"; + + private InboundThreads api; + + @BeforeEach + void init() { + final String threadsUrl = Constants.GENERAL_HOST + "/api/inbound/inboxes/" + INBOX_ID + "/threads"; + final String threadUrl = threadsUrl + "/" + THREAD_ID; + + final TestHttpClient httpClient = new TestHttpClient(List.of( + DataMock.build(threadsUrl, "GET", null, "api/inbound/listInboundThreadsResponse.json"), + DataMock.build(threadsUrl, "GET", null, "api/inbound/listInboundThreadsResponse.json", + Map.of("last_id", "thr_2")), + DataMock.build(threadUrl, "GET", null, "api/inbound/getInboundThreadResponse.json"), + DataMock.build(threadUrl, "DELETE", null, null) + )); + + final MailtrapConfig testConfig = new MailtrapConfig.Builder() + .httpClient(httpClient) + .token("dummy_token") + .build(); + + api = MailtrapClientFactory.createMailtrapClient(testConfig).inboundApi().threads(); + } + + @Test + void list_withoutCursor_returnsPage() { + final InboundThreadsListResponse response = api.list(INBOX_ID, null); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + assertEquals(2, response.getTotalCount()); + assertEquals("thr_2", response.getLastId()); + assertEquals("Support request", response.getData().get(0).getSubject()); + assertEquals(3, response.getData().get(0).getMessageCount()); + } + + @Test + void list_withCursor_returnsPage() { + final InboundThreadsListResponse response = api.list(INBOX_ID, "thr_2"); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + } + + @Test + void get_returnsThreadWithMessages() { + final InboundThread thread = api.get(INBOX_ID, THREAD_ID); + + assertNotNull(thread); + assertEquals("thr_1", thread.getId()); + assertEquals("Support request", thread.getSubject()); + assertNotNull(thread.getMessages()); + assertEquals(2, thread.getMessages().size()); + assertEquals(InboundMessageDirection.INBOUND, thread.getMessages().get(0).getDirection()); + assertEquals(InboundMessageDirection.OUTBOUND, thread.getMessages().get(1).getDirection()); + assertEquals(MessageStatus.DELIVERED, thread.getMessages().get(1).getDeliveryStatus()); + } + + @Test + void delete_doesNotThrow() { + assertDoesNotThrow(() -> api.delete(INBOX_ID, THREAD_ID)); + } +} diff --git a/src/test/java/io/mailtrap/api/sendingdomains/SendingDomainsImplTest.java b/src/test/java/io/mailtrap/api/sendingdomains/SendingDomainsImplTest.java index 9d9d6ac..bea985a 100644 --- a/src/test/java/io/mailtrap/api/sendingdomains/SendingDomainsImplTest.java +++ b/src/test/java/io/mailtrap/api/sendingdomains/SendingDomainsImplTest.java @@ -80,6 +80,8 @@ void test_getSendingDomain() { assertNotNull(response); assertEquals("test.io", response.getDomainName()); assertEquals(6, response.getDnsRecords().size()); + assertTrue(response.isInboundEnabled()); + assertFalse(response.isInboundVerified()); } @Test diff --git a/src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java b/src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java index a9f3d7d..c754949 100644 --- a/src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java +++ b/src/test/java/io/mailtrap/api/webhooks/WebhooksImplTest.java @@ -38,6 +38,10 @@ public void init() { DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/webhooks", "POST", "api/webhooks/createWebhookRequest.json", "api/webhooks/createWebhookResponse.json"), + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/webhooks", + "POST", "api/webhooks/createInboundWebhookRequest.json", + "api/webhooks/createInboundWebhookResponse.json"), + DataMock.build(Constants.GENERAL_HOST + "/api/accounts/" + accountId + "/webhooks", "GET", null, "api/webhooks/listWebhooksResponse.json"), @@ -83,6 +87,24 @@ void test_createWebhook() { assertTrue(response.getData().isActive()); } + @Test + void test_createInboundWebhook() { + final CreateWebhookRequest request = new CreateWebhookRequest( + WebhookInput.builder() + .url("https://example.com/mailtrap/inbound") + .webhookType(WebhookType.INBOUND_RECEIVING) + .inboundInboxId(42L) + .build() + ); + + final CreateWebhookResponse response = api.createWebhook(accountId, request); + + assertNotNull(response); + assertEquals(7L, response.getData().getId()); + assertEquals(WebhookType.INBOUND_RECEIVING, response.getData().getWebhookType()); + assertEquals(42L, response.getData().getInboundInboxId()); + } + @Test void test_getAllWebhooks() { final ListWebhooksResponse response = api.getAllWebhooks(accountId); diff --git a/src/test/resources/api/emaillogs/getEmailLogMessageResponse.json b/src/test/resources/api/emaillogs/getEmailLogMessageResponse.json index 5a683fc..dadaea2 100644 --- a/src/test/resources/api/emaillogs/getEmailLogMessageResponse.json +++ b/src/test/resources/api/emaillogs/getEmailLogMessageResponse.json @@ -2,6 +2,10 @@ "message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "delivered", "subject": "Welcome to our service", + "rfc_message_id": "", + "in_reply_to": "", + "references": [""], + "thread_id": "thr_abc123", "from": "sender@example.com", "to": "recipient@example.com", "sent_at": "2025-01-15T10:30:00Z", diff --git a/src/test/resources/api/emaillogs/listEmailLogsResponse.json b/src/test/resources/api/emaillogs/listEmailLogsResponse.json index 026337c..ad0f53f 100644 --- a/src/test/resources/api/emaillogs/listEmailLogsResponse.json +++ b/src/test/resources/api/emaillogs/listEmailLogsResponse.json @@ -4,6 +4,10 @@ "message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "delivered", "subject": "Welcome to our service", + "rfc_message_id": "", + "in_reply_to": null, + "references": [], + "thread_id": "thr_abc123", "from": "sender@example.com", "to": "recipient@example.com", "sent_at": "2025-01-15T10:30:00Z", diff --git a/src/test/resources/api/inbound/createInboundFolderRequest.json b/src/test/resources/api/inbound/createInboundFolderRequest.json new file mode 100644 index 0000000..7ad92e9 --- /dev/null +++ b/src/test/resources/api/inbound/createInboundFolderRequest.json @@ -0,0 +1,3 @@ +{ + "name": "Support" +} diff --git a/src/test/resources/api/inbound/createInboundFolderResponse.json b/src/test/resources/api/inbound/createInboundFolderResponse.json new file mode 100644 index 0000000..8948af7 --- /dev/null +++ b/src/test/resources/api/inbound/createInboundFolderResponse.json @@ -0,0 +1,4 @@ +{ + "id": 103, + "name": "Support" +} diff --git a/src/test/resources/api/inbound/createInboundInboxRequest.json b/src/test/resources/api/inbound/createInboundInboxRequest.json new file mode 100644 index 0000000..5f6d1a7 --- /dev/null +++ b/src/test/resources/api/inbound/createInboundInboxRequest.json @@ -0,0 +1,4 @@ +{ + "name": "Custom domain inbox", + "domain_id": 6 +} diff --git a/src/test/resources/api/inbound/createInboundInboxResponse.json b/src/test/resources/api/inbound/createInboundInboxResponse.json new file mode 100644 index 0000000..4c0eaf2 --- /dev/null +++ b/src/test/resources/api/inbound/createInboundInboxResponse.json @@ -0,0 +1,6 @@ +{ + "id": 203, + "name": "Custom domain inbox", + "address": "catch-all@example.com", + "domain_id": 6 +} diff --git a/src/test/resources/api/inbound/forwardInboundMessageRequest.json b/src/test/resources/api/inbound/forwardInboundMessageRequest.json new file mode 100644 index 0000000..16e728c --- /dev/null +++ b/src/test/resources/api/inbound/forwardInboundMessageRequest.json @@ -0,0 +1,8 @@ +{ + "to": [ + { + "email": "teammate@example.com" + } + ], + "text": "FYI, please take a look." +} diff --git a/src/test/resources/api/inbound/getInboundFolderResponse.json b/src/test/resources/api/inbound/getInboundFolderResponse.json new file mode 100644 index 0000000..387e5ca --- /dev/null +++ b/src/test/resources/api/inbound/getInboundFolderResponse.json @@ -0,0 +1,4 @@ +{ + "id": 101, + "name": "Support" +} diff --git a/src/test/resources/api/inbound/getInboundInboxResponse.json b/src/test/resources/api/inbound/getInboundInboxResponse.json new file mode 100644 index 0000000..748dddf --- /dev/null +++ b/src/test/resources/api/inbound/getInboundInboxResponse.json @@ -0,0 +1,6 @@ +{ + "id": 201, + "name": "Support inbox", + "address": "support@inbound-mailtrap.io", + "domain_id": null +} diff --git a/src/test/resources/api/inbound/getInboundMessageResponse.json b/src/test/resources/api/inbound/getInboundMessageResponse.json new file mode 100644 index 0000000..94560a0 --- /dev/null +++ b/src/test/resources/api/inbound/getInboundMessageResponse.json @@ -0,0 +1,34 @@ +{ + "id": "msg_1", + "inbox_id": 201, + "from": "customer@example.com", + "to": ["support@inbound-mailtrap.io"], + "cc": ["cc@example.com"], + "reply_to": "customer@example.com", + "subject": "Support request", + "rfc_message_id": "", + "references": [""], + "headers": { + "X-Custom-Header": "value" + }, + "size": 2048, + "html_size": 1200, + "text_size": 400, + "received_at": "2026-07-30T10:15:00Z", + "thread_id": "thr_1", + "attachments": [ + { + "attachment_id": "att_1", + "size": 512, + "filename": "invoice.pdf", + "content_type": "application/pdf", + "content_disposition": "attachment", + "download_url": "https://example.com/download/att_1", + "download_url_expires_at": "2026-07-30T12:15:00Z" + } + ], + "raw_message_url": "https://example.com/raw/msg_1", + "raw_message_expires_at": "2026-07-30T12:15:00Z", + "html_body": "

Hello, I need help.

", + "text_body": "Hello, I need help." +} diff --git a/src/test/resources/api/inbound/getInboundThreadResponse.json b/src/test/resources/api/inbound/getInboundThreadResponse.json new file mode 100644 index 0000000..cf7c4b9 --- /dev/null +++ b/src/test/resources/api/inbound/getInboundThreadResponse.json @@ -0,0 +1,39 @@ +{ + "id": "thr_1", + "subject": "Support request", + "message_count": 2, + "size": 4096, + "first_message_at": "2026-07-30T10:15:00Z", + "last_received_at": "2026-07-30T10:15:00Z", + "last_sent_at": "2026-07-30T13:00:00Z", + "last_activity_at": "2026-07-30T13:00:00Z", + "last_message_id": "msg_3", + "senders": ["customer@example.com"], + "recipients": ["support@inbound-mailtrap.io"], + "messages": [ + { + "visibility_status": "available", + "direction": "inbound", + "id": "msg_1", + "subject": "Support request", + "from": "customer@example.com", + "to": ["support@inbound-mailtrap.io"], + "created_at": "2026-07-30T10:15:00Z", + "email_size": 2048, + "text_body": "Hello, I need help." + }, + { + "visibility_status": "available", + "direction": "outbound", + "id": "msg_3", + "subject": "Re: Support request", + "from": "support@inbound-mailtrap.io", + "to": ["customer@example.com"], + "created_at": "2026-07-30T13:00:00Z", + "email_size": 1024, + "text_body": "Thanks for reaching out!", + "delivery_status": "delivered", + "delivered_at": "2026-07-30T13:00:05Z" + } + ] +} diff --git a/src/test/resources/api/inbound/listInboundFoldersResponse.json b/src/test/resources/api/inbound/listInboundFoldersResponse.json new file mode 100644 index 0000000..f372e69 --- /dev/null +++ b/src/test/resources/api/inbound/listInboundFoldersResponse.json @@ -0,0 +1,10 @@ +[ + { + "id": 101, + "name": "Support" + }, + { + "id": 102, + "name": "Sales" + } +] diff --git a/src/test/resources/api/inbound/listInboundInboxesResponse.json b/src/test/resources/api/inbound/listInboundInboxesResponse.json new file mode 100644 index 0000000..714ebb2 --- /dev/null +++ b/src/test/resources/api/inbound/listInboundInboxesResponse.json @@ -0,0 +1,14 @@ +[ + { + "id": 201, + "name": "Support inbox", + "address": "support@inbound-mailtrap.io", + "domain_id": null + }, + { + "id": 202, + "name": "Catch-all", + "address": "catch-all@example.com", + "domain_id": 6 + } +] diff --git a/src/test/resources/api/inbound/listInboundMessagesResponse.json b/src/test/resources/api/inbound/listInboundMessagesResponse.json new file mode 100644 index 0000000..8b1d591 --- /dev/null +++ b/src/test/resources/api/inbound/listInboundMessagesResponse.json @@ -0,0 +1,26 @@ +{ + "data": [ + { + "id": "msg_1", + "inbox_id": 201, + "from": "customer@example.com", + "to": ["support@inbound-mailtrap.io"], + "subject": "Support request", + "size": 2048, + "received_at": "2026-07-30T10:15:00Z", + "thread_id": "thr_1" + }, + { + "id": "msg_2", + "inbox_id": 201, + "from": "another@example.com", + "to": ["support@inbound-mailtrap.io"], + "subject": "Question about billing", + "size": 1536, + "received_at": "2026-07-30T11:20:00Z", + "thread_id": "thr_2" + } + ], + "total_count": 2, + "last_id": "msg_2" +} diff --git a/src/test/resources/api/inbound/listInboundThreadsResponse.json b/src/test/resources/api/inbound/listInboundThreadsResponse.json new file mode 100644 index 0000000..3b03c78 --- /dev/null +++ b/src/test/resources/api/inbound/listInboundThreadsResponse.json @@ -0,0 +1,28 @@ +{ + "data": [ + { + "id": "thr_1", + "subject": "Support request", + "message_count": 3, + "size": 4096, + "first_message_at": "2026-07-30T10:15:00Z", + "last_activity_at": "2026-07-30T13:00:00Z", + "last_message_id": "msg_3", + "senders": ["customer@example.com"], + "recipients": ["support@inbound-mailtrap.io"] + }, + { + "id": "thr_2", + "subject": "Question about billing", + "message_count": 1, + "size": 1536, + "first_message_at": "2026-07-30T11:20:00Z", + "last_activity_at": "2026-07-30T11:20:00Z", + "last_message_id": "msg_2", + "senders": ["another@example.com"], + "recipients": ["support@inbound-mailtrap.io"] + } + ], + "total_count": 2, + "last_id": "thr_2" +} diff --git a/src/test/resources/api/inbound/replyAllInboundMessageRequest.json b/src/test/resources/api/inbound/replyAllInboundMessageRequest.json new file mode 100644 index 0000000..41064e0 --- /dev/null +++ b/src/test/resources/api/inbound/replyAllInboundMessageRequest.json @@ -0,0 +1,14 @@ +{ + "to": [ + { + "email": "customer@example.com" + } + ], + "cc": [ + { + "email": "cc@example.com" + } + ], + "subject": "Re: Support request", + "text": "Thanks everyone!" +} diff --git a/src/test/resources/api/inbound/replyInboundMessageRequest.json b/src/test/resources/api/inbound/replyInboundMessageRequest.json new file mode 100644 index 0000000..5a19089 --- /dev/null +++ b/src/test/resources/api/inbound/replyInboundMessageRequest.json @@ -0,0 +1,9 @@ +{ + "to": [ + { + "email": "customer@example.com" + } + ], + "subject": "Re: Support request", + "text": "Thanks for reaching out, we are on it!" +} diff --git a/src/test/resources/api/inbound/sendInboundMessageResponse.json b/src/test/resources/api/inbound/sendInboundMessageResponse.json new file mode 100644 index 0000000..0a0b828 --- /dev/null +++ b/src/test/resources/api/inbound/sendInboundMessageResponse.json @@ -0,0 +1,3 @@ +{ + "message_ids": ["0000000000000001"] +} diff --git a/src/test/resources/api/inbound/updateInboundFolderRequest.json b/src/test/resources/api/inbound/updateInboundFolderRequest.json new file mode 100644 index 0000000..6936835 --- /dev/null +++ b/src/test/resources/api/inbound/updateInboundFolderRequest.json @@ -0,0 +1,3 @@ +{ + "name": "Renamed folder" +} diff --git a/src/test/resources/api/inbound/updateInboundFolderResponse.json b/src/test/resources/api/inbound/updateInboundFolderResponse.json new file mode 100644 index 0000000..8ceaae9 --- /dev/null +++ b/src/test/resources/api/inbound/updateInboundFolderResponse.json @@ -0,0 +1,4 @@ +{ + "id": 101, + "name": "Renamed folder" +} diff --git a/src/test/resources/api/inbound/updateInboundInboxRequest.json b/src/test/resources/api/inbound/updateInboundInboxRequest.json new file mode 100644 index 0000000..99a5e47 --- /dev/null +++ b/src/test/resources/api/inbound/updateInboundInboxRequest.json @@ -0,0 +1,3 @@ +{ + "name": "Renamed inbox" +} diff --git a/src/test/resources/api/inbound/updateInboundInboxResponse.json b/src/test/resources/api/inbound/updateInboundInboxResponse.json new file mode 100644 index 0000000..99574b5 --- /dev/null +++ b/src/test/resources/api/inbound/updateInboundInboxResponse.json @@ -0,0 +1,6 @@ +{ + "id": 201, + "name": "Renamed inbox", + "address": "support@inbound-mailtrap.io", + "domain_id": null +} diff --git a/src/test/resources/api/sending_domains/sendingDomainResponse.json b/src/test/resources/api/sending_domains/sendingDomainResponse.json index 445dc18..ec6c619 100644 --- a/src/test/resources/api/sending_domains/sendingDomainResponse.json +++ b/src/test/resources/api/sending_domains/sendingDomainResponse.json @@ -61,6 +61,8 @@ "health_alerts_enabled": true, "critical_alerts_enabled": true, "alert_recipient_email": "john.doe@test.io", + "inbound_enabled": true, + "inbound_verified": false, "permissions": { "can_read": true, "can_update": true, diff --git a/src/test/resources/api/webhooks/createInboundWebhookRequest.json b/src/test/resources/api/webhooks/createInboundWebhookRequest.json new file mode 100644 index 0000000..9ae0641 --- /dev/null +++ b/src/test/resources/api/webhooks/createInboundWebhookRequest.json @@ -0,0 +1,7 @@ +{ + "webhook": { + "url": "https://example.com/mailtrap/inbound", + "webhook_type": "inbound_receiving", + "inbound_inbox_id": 42 + } +} diff --git a/src/test/resources/api/webhooks/createInboundWebhookResponse.json b/src/test/resources/api/webhooks/createInboundWebhookResponse.json new file mode 100644 index 0000000..8fc0bee --- /dev/null +++ b/src/test/resources/api/webhooks/createInboundWebhookResponse.json @@ -0,0 +1,11 @@ +{ + "data": { + "id": 7, + "url": "https://example.com/mailtrap/inbound", + "active": true, + "webhook_type": "inbound_receiving", + "payload_format": "json", + "inbound_inbox_id": 42, + "signing_secret": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" + } +}