diff --git a/README.md b/README.md index 7b9ea08..3490a31 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ MailerSend Java SDK - [Send email with custom headers](#send-email-with-custom-headers) - [Send email with references](#send-email-with-references) - [Send email with precedence bulk](#send-email-with-precedence-bulk) - - [Send an email with RCPT TO recipients](#send-an-email-with-rcpt-to-recipients) - [Send bulk emails](#send-bulk-emails) - [Get bulk request status](#get-bulk-request-status) - [Emails](#emails) @@ -167,6 +166,7 @@ MailerSend Java SDK - [WhatsApp](#whatsapp) - [Send a WhatsApp message](#send-a-whatsapp-message) - [Send a WhatsApp message with personalization](#send-a-whatsapp-message-with-personalization) + - [Send a WhatsApp message with a media header and named variables](#send-a-whatsapp-message-with-a-media-header-and-named-variables) - [WhatsApp messages](#whatsapp-messages) - [Get a list of WhatsApp messages](#get-a-list-of-whatsapp-messages) - [Get a WhatsApp message](#get-a-whatsapp-message) @@ -219,6 +219,7 @@ Through the `MailerSendResponse` object you can get the ID of the sent message, ```java import com.mailersend.sdk.emails.Email; import com.mailersend.sdk.MailerSend; +import com.mailersend.sdk.Recipient; import com.mailersend.sdk.MailerSendResponse; import com.mailersend.sdk.exceptions.MailerSendException; @@ -302,6 +303,7 @@ public void sendEmail() { ```java import com.mailersend.sdk.emails.Email; import com.mailersend.sdk.MailerSend; +import com.mailersend.sdk.Recipient; import com.mailersend.sdk.MailerSendResponse; import com.mailersend.sdk.exceptions.MailerSendException; @@ -340,6 +342,7 @@ for raw HTML/text sends. Supported values: `de`, `en`, `es`, `fr`, `it`, `lt`, ` ```java import com.mailersend.sdk.emails.Email; import com.mailersend.sdk.MailerSend; +import com.mailersend.sdk.Recipient; import com.mailersend.sdk.MailerSendResponse; import com.mailersend.sdk.exceptions.MailerSendException; @@ -376,6 +379,7 @@ public void sendEmail() { ```java import com.mailersend.sdk.emails.Email; import com.mailersend.sdk.MailerSend; +import com.mailersend.sdk.Recipient; import com.mailersend.sdk.MailerSendResponse; import com.mailersend.sdk.exceptions.MailerSendException; @@ -428,6 +432,7 @@ import com.mailersend.sdk.emails.Email; import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.MailerSendResponse; import com.mailersend.sdk.exceptions.MailerSendException; +import java.io.File; public void sendEmail() { @@ -750,50 +755,6 @@ public void sendEmailWithPrecedenceBulk() { } ``` -### Send an email with RCPT TO recipients - -`rcptTo` is intended for SMTP source delivery and accepts a list of recipients. -When `to` is empty and `rcptTo` is provided, the addresses are forwarded as BCC. - -```java -import com.mailersend.sdk.emails.Email; -import com.mailersend.sdk.MailerSend; -import com.mailersend.sdk.MailerSendResponse; -import com.mailersend.sdk.exceptions.MailerSendException; - -public void sendEmailWithRcptTo() { - - Email email = new Email(); - - email.setFrom("name", "your email"); - - email.setSubject("Email subject"); - - email.setPlain("This is the text content"); - email.setHtml("
This is the HTML content
"); - - // Add RCPT TO recipients for SMTP relay delivery - email.addRcptTo("name", "rcpt@client.com"); - - // you can also add a Recipient object directly - Recipient rcptRecipient = new Recipient("name2", "rcpt2@client.com"); - email.addRcptTo(rcptRecipient); - - MailerSend ms = new MailerSend(); - - ms.setToken("Your API token"); - - try { - - MailerSendResponse response = ms.emails().send(email); - System.out.println(response.messageId); - } catch (MailerSendException e) { - - e.printStackTrace(); - } -} -``` - ### Send bulk emails ```java @@ -959,6 +920,7 @@ import com.mailersend.sdk.emails.EmailListItem; import com.mailersend.sdk.emails.EmailStatus; import com.mailersend.sdk.emails.EmailsList; import com.mailersend.sdk.exceptions.MailerSendException; +import java.util.Date; public void getFilteredEmails() { @@ -968,7 +930,7 @@ public void getFilteredEmails() { try { - Date dateFrom = DateUtils.addDays(new Date(), -7); // you'll need apache-commons for this + Date dateFrom = new Date(System.currentTimeMillis() - 7L * 24 * 60 * 60 * 1000); // 7 days ago Date dateTo = new Date(); EmailsList list = ms.emails() @@ -1321,6 +1283,7 @@ public void getActivities() { try { + // without dates, the first page (25 activities) of the last 7 days is returned ActivitiesList activities = ms.activities().getActivities("domain id"); for (Activity activity : activities.activities) { @@ -1383,6 +1346,7 @@ import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.MailerSendResponse; import com.mailersend.sdk.exceptions.MailerSendException; import com.mailersend.sdk.util.EventTypes; +import java.util.Date; public void getActivities() { @@ -1394,7 +1358,7 @@ public void getActivities() { int page = 1; int limit = 25; // also the default limit is 25 - Date dateFrom = DateUtils.addDays(new Date(), -30); // you'll need apache-commons for this + Date dateFrom = new Date(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000); // 30 days ago Date dateTo = new Date(); String events[] = {EventTypes.OPENED, EventTypes.SENT, EventTypes.SUPPRESSED}; // check com.mailsersend.sdk.util.EventTypes for a full list of events @@ -1522,7 +1486,7 @@ import com.mailersend.sdk.util.EventTypes; public void getAnalyticsByDate() { MailerSend ms = new MailerSend(); - ms.setToken(TestHelper.validToken); + ms.setToken("Your API token"); try { @@ -1531,7 +1495,7 @@ public void getAnalyticsByDate() { AnalyticsByDateList list = ms.analytics() .dateFrom(dateFrom) .dateTo(new Date()) - .domainId(TestHelper.domainId) + .domainId("domain id") .getByDate(new String[] {EventTypes.DELIVERED, EventTypes.OPENED, EventTypes.CLICKED}); System.out.println("\n\nAnalytics by date for domain:"); @@ -1563,7 +1527,7 @@ import com.mailersend.sdk.exceptions.MailerSendException; public void getOpensByCountry() { MailerSend ms = new MailerSend(); - ms.setToken(TestHelper.validToken); + ms.setToken("Your API token"); try { @@ -1572,7 +1536,7 @@ public void getOpensByCountry() { AnalyticsList list = ms.analytics() .dateFrom(dateFrom) .dateTo(new Date()) - .domainId(TestHelper.domainId) + .domainId("domain id") .getOpensByCountry(); System.out.println("\n\nOpens by country:"); @@ -1601,7 +1565,7 @@ import com.mailersend.sdk.exceptions.MailerSendException; public void getOpensByUserAgentName() { MailerSend ms = new MailerSend(); - ms.setToken(TestHelper.validToken); + ms.setToken("Your API token"); try { @@ -1638,7 +1602,7 @@ import com.mailersend.sdk.exceptions.MailerSendException; public void getOpensByUserAgentType() { MailerSend ms = new MailerSend(); - ms.setToken(TestHelper.validToken); + ms.setToken("Your API token"); try { @@ -1879,7 +1843,7 @@ public void UpdateDomainSettings() { try { Domain domain = ms.domains().updateDomainSettingsBuilder() - .customnTrackingEnabled(true) + .customTrackingEnabled(true) .sendPaused(false) .updateDomain("domain id"); @@ -2073,8 +2037,8 @@ public void DeleteSmtpUser() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailersend.sdk.messages.Message; import com.mailersend.sdk.messages.MessagesList; +import com.mailersend.sdk.messages.MessagesListItem; public void MessagesList() { @@ -2085,7 +2049,7 @@ public void MessagesList() { MessagesList list = ms.messages().getMessages(); - for (MessageListItem message : list.messages) { + for (MessagesListItem message : list.messages) { System.out.println(message.id); System.out.println(message.createdAt.toString()); @@ -2146,7 +2110,7 @@ public void getScheduledMessages() { try { // Use the STATUS_* constants to filter by status - ScheduledMessagesList messages = ms.scheduledmessages() + ScheduledMessagesList messages = ms.scheduledMessages() .status(ScheduledMessages.STATUS_SCHEDULED) .getScheduledMessages(); @@ -2177,7 +2141,7 @@ public void getScheduledMessage() { try { - ScheduledMessage message = ms.scheduledmessages().getScheduledMessage("message id"); + ScheduledMessage message = ms.scheduledMessages().getScheduledMessage("message id"); System.out.println(message.id); System.out.println(message.subject); @@ -2204,7 +2168,7 @@ public void deleteScheduledMessage() { try { - boolean result = ms.scheduledmessages().deleteScheduledMessage("message id"); + boolean result = ms.scheduledMessages().deleteScheduledMessage("message id"); System.out.println(result); @@ -2289,7 +2253,7 @@ import com.mailersend.sdk.tokens.TokenScopes; public void CreateToken() { MailerSend ms = new MailerSend(); - ms.setToken(TestHelper.validToken); + ms.setToken("Your API token"); try { @@ -2378,7 +2342,7 @@ import com.mailersend.sdk.tokens.Token; public void CreateToken() { MailerSend ms = new MailerSend(); - ms.setToken(TestHelper.validToken); + ms.setToken("Your API token"); try { MailerSendResponse response = ms.tokens().deleteToken("token to delete"); @@ -2564,7 +2528,7 @@ public void AddRecipientsToSuppressionList() { // hard bounces ms.recipients().suppressions().addBuilder().recipient("test@example.com"); - ms.recipients().suppressions().addBuilder().domainId(TestHelper.domainId); + ms.recipients().suppressions().addBuilder().domainId("domain id"); SuppressionList list = ms.recipients().suppressions().addBuilder().addRecipientsToHardBounces(); for (SuppressionItem item : list.items) { @@ -2575,7 +2539,7 @@ public void AddRecipientsToSuppressionList() { // spam complaints ms.recipients().suppressions().addBuilder().recipient("test@example.com"); - ms.recipients().suppressions().addBuilder().domainId(TestHelper.domainId); + ms.recipients().suppressions().addBuilder().domainId("domain id"); list = ms.recipients().suppressions().addBuilder().addRecipientsToSpamComplaints(); for (SuppressionItem item : list.items) { @@ -2586,7 +2550,7 @@ public void AddRecipientsToSuppressionList() { // unsubscribes ms.recipients().suppressions().addBuilder().recipient("test@example.com"); - ms.recipients().suppressions().addBuilder().domainId(TestHelper.domainId); + ms.recipients().suppressions().addBuilder().domainId("domain id"); list = ms.recipients().suppressions().addBuilder().addRecipientsToUnsubscribes(); for (SuppressionItem item : list.items) { @@ -2624,7 +2588,7 @@ public void DeleteRecipientsFromSuppressionList () { if (blocklist.items.length == 0) { - fail(); + return; } String itemId = blocklist.items[0].id; @@ -2639,7 +2603,7 @@ public void DeleteRecipientsFromSuppressionList () { if (hardBounces.items.length == 0) { - fail(); + return; } itemId = hardBounces.items[0].id; @@ -2654,7 +2618,7 @@ public void DeleteRecipientsFromSuppressionList () { if (spamComplaints.items.length == 0) { - fail(); + return; } itemId = spamComplaints.items[0].id; @@ -2669,7 +2633,7 @@ public void DeleteRecipientsFromSuppressionList () { if (unsubscribes.items.length == 0) { - fail(); + return; } itemId = unsubscribes.items[0].id; @@ -2821,6 +2785,7 @@ public void GetSingleWebhook() { import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; import com.mailersend.sdk.webhooks.Webhook; +import com.mailersend.sdk.webhooks.WebhookEvents; public void CreateWebhook() { @@ -3056,7 +3021,7 @@ public void deleteTemplate() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.emailverification.SingleEmailVerificationResponse; +import com.mailersend.sdk.emailverification.SingleEmailVerificationResponse; public void verifyEmail() { @@ -3081,7 +3046,7 @@ public void verifyEmail() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.emailverification.AsyncEmailVerificationResponse; +import com.mailersend.sdk.emailverification.AsyncEmailVerificationResponse; public void verifyEmailAsync() { @@ -3107,7 +3072,7 @@ public void verifyEmailAsync() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.emailverification.AsyncEmailVerificationResponse; +import com.mailersend.sdk.emailverification.AsyncEmailVerificationResponse; public void getVerifyEmailAsyncStatus() { @@ -3133,8 +3098,8 @@ public void getVerifyEmailAsyncStatus() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.emailverification.EmailVerificationList; -import com.mailsend.sdk.emailverification.EmailVerificationLists; +import com.mailersend.sdk.emailverification.EmailVerificationList; +import com.mailersend.sdk.emailverification.EmailVerificationLists; public void getLists() { @@ -3162,7 +3127,7 @@ public void getLists() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.emailverification.EmailVerificationList; +import com.mailersend.sdk.emailverification.EmailVerificationList; public void getList() { @@ -3187,7 +3152,7 @@ public void getList() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.emailverification.EmailVerificationList; +import com.mailersend.sdk.emailverification.EmailVerificationList; public void createList() { @@ -3217,7 +3182,7 @@ public void createList() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.emailverification.EmailVerificationList; +import com.mailersend.sdk.emailverification.EmailVerificationList; public void verifyList() { @@ -3242,8 +3207,8 @@ public void verifyList() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.emailverification.ListVerificationResults; -import com.mailsend.sdk.emailverification.VerificationResult; +import com.mailersend.sdk.emailverification.ListVerificationResults; +import com.mailersend.sdk.emailverification.VerificationResult; public void verifyList() { @@ -3273,8 +3238,8 @@ public void verifyList() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.sms.activities.SmsActivityList; -import com.mailsend.sdk.sms.activities.SmsActivity; +import com.mailersend.sdk.sms.activities.SmsActivityList; +import com.mailersend.sdk.sms.activities.SmsActivity; public void getActivities() { @@ -3302,7 +3267,7 @@ public void getActivities() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.sms.activities.SmsActivity; +import com.mailersend.sdk.sms.activities.SmsActivity; public void getActivity() { @@ -3330,8 +3295,8 @@ public void getActivity() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.sms.messages.SmsMessageList; -import com.mailsend.sdk.sms.messages.SmsMessage; +import com.mailersend.sdk.sms.messages.SmsMessageList; +import com.mailersend.sdk.sms.messages.SmsMessage; public void getSmsMessages() { @@ -3359,7 +3324,7 @@ public void getSmsMessages() { ```java import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.exceptions.MailerSendException; -import com.mailsend.sdk.sms.messages.SmsMessage; +import com.mailersend.sdk.sms.messages.SmsMessage; public void getSmsMessage() { @@ -4064,7 +4029,10 @@ public void getReportSources() { try { - DmarcReportSourcesList list = ms.dmarcMonitoring().getReportSources("your-monitor-id"); + DmarcReportSourcesList list = ms.dmarcMonitoring() + .dateFrom("2024-01-01 00:00:00") + .dateTo("2024-01-31 23:59:59") + .getReportSources("your-monitor-id"); for (DmarcReportSource source : list.sources) { System.out.println(source.reportSource); @@ -4645,6 +4613,7 @@ public void inviteCustomUser() { .role("Custom User") .addPermission("read-suppressions") .addPermission("manage-suppressions") + .addPermission("read-own-templates") .addDomain("domain-id") .addTemplate("template-id") .requiresPeriodicPasswordChange(true) @@ -4901,6 +4870,49 @@ public void sendWhatsAppWithPersonalization() { } ``` +### Send a WhatsApp message with a media header and named variables + +`setMediaHeader(link)` or `setMediaHeader(link, filename)` fills an image, video or document header; `setNamedHeader(Map)` and `setNamedBody(Map)` fill templates that use named variables. A header or body setter replaces the previous one, so the last call wins. + +```java +import java.util.LinkedHashMap; +import java.util.Map; + +import com.mailersend.sdk.MailerSend; +import com.mailersend.sdk.exceptions.MailerSendException; +import com.mailersend.sdk.whatsapp.WhatsAppPersonalization; + +public void sendWhatsAppWithMediaHeaderAndNamedVariables() { + + MailerSend ms = new MailerSend(); + ms.setToken("mailersend token"); + + try { + + MappostDeserialize.
diff --git a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitor.java b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitor.java index 7b4dcc7..06d3d19 100644 --- a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitor.java +++ b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitor.java @@ -12,6 +12,7 @@ import java.time.temporal.TemporalAccessor; import java.util.Date; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import com.mailersend.sdk.MailerSendResponse; @@ -38,8 +39,17 @@ public class BlocklistMonitor extends MailerSendResponse { @SerializedName("notify") public boolean notify; + /** + * True when the address is listed on at least one blocklist. Derived from {@link #blocklistedCount}. + */ + public transient boolean blocklisted; + + /** + * The number of blocklists the address is listed on. + */ @SerializedName("blocklisted") - public boolean blocklisted; + @JsonAdapter(BlocklistedCountAdapter.class) + public Integer blocklistedCount; @SerializedName("last_checks_count") public int lastChecksCount; @@ -88,6 +98,14 @@ public class BlocklistMonitor extends MailerSendResponse { */ public void postDeserialize() { parseDates(); + + blocklisted = blocklistedCount != null && blocklistedCount > 0; + + if (children != null) { + for (BlocklistMonitor child : children) { + child.postDeserialize(); + } + } } private void parseDates() { diff --git a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorCreateBuilder.java b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorCreateBuilder.java index e4de1ac..6ef4f0f 100644 --- a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorCreateBuilder.java +++ b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorCreateBuilder.java @@ -105,6 +105,8 @@ public BlocklistMonitor createMonitor(String address) throws MailerSendException BlocklistMonitorResponse response = api.postRequest(endpoint, json, BlocklistMonitorResponse.class); + response.monitor.postDeserialize(); + return response.monitor; } } diff --git a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorUpdateBuilder.java b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorUpdateBuilder.java index 8c369d7..5190f13 100644 --- a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorUpdateBuilder.java +++ b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorUpdateBuilder.java @@ -103,6 +103,8 @@ public BlocklistMonitor updateMonitor(String monitorId) throws MailerSendExcepti BlocklistMonitorResponse response = api.putRequest(endpoint, json, BlocklistMonitorResponse.class); + response.monitor.postDeserialize(); + return response.monitor; } } diff --git a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitoring.java b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitoring.java index d90daae..62fd1bb 100644 --- a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitoring.java +++ b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitoring.java @@ -136,7 +136,11 @@ public BlocklistMonitorsList getMonitors() throws MailerSendException { sortByFilter = null; orderFilter = null; - return api.getRequest(endpoint, BlocklistMonitorsList.class); + BlocklistMonitorsList response = api.getRequest(endpoint, BlocklistMonitorsList.class); + + response.postDeserialize(); + + return response; } /** @@ -154,6 +158,8 @@ public BlocklistMonitor getMonitor(String monitorId) throws MailerSendException BlocklistMonitorResponse response = api.getRequest(endpoint, BlocklistMonitorResponse.class); + response.monitor.postDeserialize(); + return response.monitor; } diff --git a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorsList.java b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorsList.java index 1f8d8bd..21d1e37 100644 --- a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorsList.java +++ b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistMonitorsList.java @@ -17,4 +17,15 @@ public class BlocklistMonitorsList extends PaginatedResponse { @SerializedName("data") public BlocklistMonitor[] monitors; + + /** + * Called after deserialization to post-process each monitor. + */ + public void postDeserialize() { + if (monitors != null) { + for (BlocklistMonitor monitor : monitors) { + monitor.postDeserialize(); + } + } + } } diff --git a/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistedCountAdapter.java b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistedCountAdapter.java new file mode 100644 index 0000000..f3ca1b2 --- /dev/null +++ b/src/main/java/com/mailersend/sdk/blocklistmonitoring/BlocklistedCountAdapter.java @@ -0,0 +1,35 @@ +/************************************************* + * MailerSend Java SDK + * https://github.com/mailersend/mailersend-java + * + * @author MailerSendDomain class.
@@ -92,21 +90,7 @@ public void postDeserialize() { */ private void parseDates() { - TemporalAccessor ta; - Instant instant; - - if (createdAtStr != null && !createdAtStr.isBlank()) { - - ta = DateTimeFormatter.ISO_INSTANT.parse(createdAtStr); - instant = Instant.from(ta); - createdAt = Date.from(instant); - } - - if (updatedAtStr != null && !updatedAtStr.isBlank()) { - - ta = DateTimeFormatter.ISO_INSTANT.parse(updatedAtStr); - instant = Instant.from(ta); - updatedAt = Date.from(instant); - } + createdAt = DateParser.parse(createdAtStr); + updatedAt = DateParser.parse(updatedAtStr); } } diff --git a/src/main/java/com/mailersend/sdk/domains/DomainAddBuilder.java b/src/main/java/com/mailersend/sdk/domains/DomainAddBuilder.java index eb79a1e..a0b7047 100644 --- a/src/main/java/com/mailersend/sdk/domains/DomainAddBuilder.java +++ b/src/main/java/com/mailersend/sdk/domains/DomainAddBuilder.java @@ -109,6 +109,11 @@ public Domain addDomain(String name) throws MailerSendException { SingleDomainResponse response = api.postRequest(endpoint, json, SingleDomainResponse.class); + if (response.domain != null) { + + response.domain.postDeserialize(); + } + return response.domain; } } diff --git a/src/main/java/com/mailersend/sdk/domains/DomainDnsRecords.java b/src/main/java/com/mailersend/sdk/domains/DomainDnsRecords.java index 30f49fd..2c68ab6 100644 --- a/src/main/java/com/mailersend/sdk/domains/DomainDnsRecords.java +++ b/src/main/java/com/mailersend/sdk/domains/DomainDnsRecords.java @@ -27,6 +27,7 @@ public class DomainDnsRecords { * @deprecated Use {@link #dkimMs1} and {@link #dkimMs2} instead. */ @SerializedName("dkim") + @Deprecated public DomainDnsAttribute dkim; @SerializedName("dkim_ms1") diff --git a/src/main/java/com/mailersend/sdk/domains/DomainSettingsUpdateBuilder.java b/src/main/java/com/mailersend/sdk/domains/DomainSettingsUpdateBuilder.java index 2718e09..e1b01a2 100644 --- a/src/main/java/com/mailersend/sdk/domains/DomainSettingsUpdateBuilder.java +++ b/src/main/java/com/mailersend/sdk/domains/DomainSettingsUpdateBuilder.java @@ -165,12 +165,12 @@ public DomainSettingsUpdateBuilder trackContent(boolean trackContent) { } /** - *customnTrackingEnabled.
+ * Set custom tracking on/off * * @param customTrackingEnabled a boolean. * @return a {@link com.mailersend.sdk.domains.DomainSettingsUpdateBuilder} object. */ - public DomainSettingsUpdateBuilder customnTrackingEnabled(boolean customTrackingEnabled) { + public DomainSettingsUpdateBuilder customTrackingEnabled(boolean customTrackingEnabled) { settings.customTrackingEnabled = customTrackingEnabled; @@ -178,6 +178,20 @@ public DomainSettingsUpdateBuilder customnTrackingEnabled(boolean customTracking } + /** + * Set custom tracking on/off + * + * @param customTrackingEnabled a boolean. + * @return a {@link com.mailersend.sdk.domains.DomainSettingsUpdateBuilder} object. + * @deprecated Use {@link #customTrackingEnabled(boolean)} instead. + */ + @Deprecated + public DomainSettingsUpdateBuilder customnTrackingEnabled(boolean customTrackingEnabled) { + + return customTrackingEnabled(customTrackingEnabled); + } + + /** * Set custom subdomain tracking on/off * @@ -245,6 +259,11 @@ public Domain updateDomain(String domainId) throws MailerSendException { SingleDomainResponse response = api.putRequest(endpoint, json, SingleDomainResponse.class); + if (response.domain != null) { + + response.domain.postDeserialize(); + } + return response.domain; } diff --git a/src/main/java/com/mailersend/sdk/domains/Domains.java b/src/main/java/com/mailersend/sdk/domains/Domains.java index ff20d9e..25270b9 100644 --- a/src/main/java/com/mailersend/sdk/domains/Domains.java +++ b/src/main/java/com/mailersend/sdk/domains/Domains.java @@ -150,6 +150,8 @@ public DomainsList getDomains() throws MailerSendException { DomainsList response = api.getRequest(endpoint, DomainsList.class); + response.postDeserialize(); + return response; } @@ -170,6 +172,11 @@ public Domain getDomain(String domainId) throws MailerSendException { SingleDomainResponse response = api.getRequest(endpoint, SingleDomainResponse.class); + if (response.domain != null) { + + response.domain.postDeserialize(); + } + return response.domain; } @@ -272,10 +279,10 @@ private String prepareParamsUrl() { if (verifiedOnly == 0) { - params.add("verified=false"); + params.add("verified=0"); } else if (verifiedOnly == 1) { - params.add("verified=true"); + params.add("verified=1"); } String requestParams = ""; diff --git a/src/main/java/com/mailersend/sdk/domains/DomainsList.java b/src/main/java/com/mailersend/sdk/domains/DomainsList.java index f7b4b86..0bcdbaa 100644 --- a/src/main/java/com/mailersend/sdk/domains/DomainsList.java +++ b/src/main/java/com/mailersend/sdk/domains/DomainsList.java @@ -21,4 +21,22 @@ public class DomainsList extends PaginatedResponse { @SerializedName("data") public Domain[] domains; + + + /** + * Is called to perform any actions after the deserialization of the response + * Do not call directly + */ + public void postDeserialize() { + + if (domains == null) { + + return; + } + + for (Domain domain : domains) { + + domain.postDeserialize(); + } + } } diff --git a/src/main/java/com/mailersend/sdk/emails/Email.java b/src/main/java/com/mailersend/sdk/emails/Email.java index b9f3d77..6f059f0 100644 --- a/src/main/java/com/mailersend/sdk/emails/Email.java +++ b/src/main/java/com/mailersend/sdk/emails/Email.java @@ -19,6 +19,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; import com.google.gson.TypeAdapter; import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; @@ -49,8 +50,12 @@ public class Email { @SerializedName("reply_to") public Recipient replyTo; + /** + * @deprecated The email API does not accept RCPT TO recipients, so they are not sent. + */ @SerializedName("rcpt_to") - public ArrayListpostDeserialize.
diff --git a/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationListEmails.java b/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationListEmails.java new file mode 100644 index 0000000..b47b63e --- /dev/null +++ b/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationListEmails.java @@ -0,0 +1,28 @@ +/************************************************* + * MailerSend Java SDK + * https://github.com/mailersend/mailersend-java + * + * @author MailerSenddomainId.
* + *Only sent by addRoute(), the update endpoint does not accept it.
+ * * @param domainId a {@link java.lang.String} object. * @return a {@link com.mailersend.sdk.inboundroutes.InboundRouteBuilder} object. */ @@ -82,7 +85,9 @@ public InboundRouteBuilder inboundDomain(String domain) { * * @param address a {@link java.lang.String} object. * @return a {@link com.mailersend.sdk.inboundroutes.InboundRouteBuilder} object. + * @deprecated The API does not accept an inbound address, so it is not sent. */ + @Deprecated public InboundRouteBuilder inboundAddress(String address) { builderBody.inboundAddress = address; return this; @@ -93,7 +98,9 @@ public InboundRouteBuilder inboundAddress(String address) { * * @param subdomain a {@link java.lang.String} object. * @return a {@link com.mailersend.sdk.inboundroutes.InboundRouteBuilder} object. + * @deprecated The API does not accept an inbound subdomain, so it is not sent. */ + @Deprecated public InboundRouteBuilder inboundSubdomain(String subdomain) { builderBody.inboundSubdomain = subdomain; @@ -297,6 +304,8 @@ public InboundRoute addRoute() throws MailerSendException { SingleInboundRouteResponse response = api.postRequest(endpoint, json, SingleInboundRouteResponse.class); + response.route.postDeserialize(); + return response.route; } @@ -321,12 +330,17 @@ public InboundRoute updateRoute(String inboundRouteId) throws MailerSendExceptio .addDeserializationExclusionStrategy(new JsonSerializationDeserializationStrategy(true)) .create(); - String json = gson.toJson(builderBody); + JsonObject body = gson.toJsonTree(builderBody).getAsJsonObject(); + body.remove("domain_id"); + + String json = gson.toJson(body); builderBody.reset(); SingleInboundRouteResponse response = api.putRequest(endpoint, json, SingleInboundRouteResponse.class); + response.route.postDeserialize(); + return response.route; } } diff --git a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRouteBuilderBody.java b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRouteBuilderBody.java index 150f1aa..8f65b96 100644 --- a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRouteBuilderBody.java +++ b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRouteBuilderBody.java @@ -22,11 +22,19 @@ public class InboundRouteBuilderBody { @SerializedName("inbound_domain") public String inboundDomain; + /** + * @deprecated The API does not accept an inbound address, so it is not sent. + */ @SerializedName("inbound_address") - public String inboundAddress; + @Deprecated + public transient String inboundAddress; + /** + * @deprecated The API does not accept an inbound subdomain, so it is not sent. + */ @SerializedName("inbound_subdomain") - public String inboundSubdomain; + @Deprecated + public transient String inboundSubdomain; @SerializedName("inbound_priority") public int inboundPriority; diff --git a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoutes.java b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoutes.java index ac193c4..e19b447 100644 --- a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoutes.java +++ b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoutes.java @@ -100,6 +100,8 @@ public InboundRoutesList getRoutes() throws MailerSendException { InboundRoutesList response = api.getRequest(endpoint, InboundRoutesList.class); + response.postDeserialize(); + return response; } @@ -118,6 +120,8 @@ public InboundRoute getRoute(String routeId) throws MailerSendException { SingleInboundRouteResponse response = api.getRequest(endpoint, SingleInboundRouteResponse.class); + response.route.postDeserialize(); + return response.route; } diff --git a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoutesList.java b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoutesList.java index b07a7ba..32e918e 100644 --- a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoutesList.java +++ b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoutesList.java @@ -13,4 +13,21 @@ public class InboundRoutesList extends PaginatedResponse { @SerializedName("data") public InboundRoute[] routes; + + + /** + * Is called to perform any actions after the deserialization of the response + * Do not call directly + */ + protected void postDeserialize() { + + if (routes == null) { + return; + } + + for (InboundRoute route : routes) { + + route.postDeserialize(); + } + } } diff --git a/src/main/java/com/mailersend/sdk/recipients/BlocklistDomain.java b/src/main/java/com/mailersend/sdk/recipients/BlocklistDomain.java index b8152b6..f601a28 100644 --- a/src/main/java/com/mailersend/sdk/recipients/BlocklistDomain.java +++ b/src/main/java/com/mailersend/sdk/recipients/BlocklistDomain.java @@ -7,12 +7,10 @@ **************************************************/ package com.mailersend.sdk.recipients; -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.time.temporal.TemporalAccessor; import java.util.Date; import com.google.gson.annotations.SerializedName; +import com.mailersend.sdk.util.DateParser; /** *BlocklistDomain class.
@@ -44,21 +42,7 @@ public class BlocklistDomain { */ protected void parseDates() { - TemporalAccessor ta; - Instant instant; - - if (createdAtString != null && !createdAtString.isBlank()) { - - ta = DateTimeFormatter.ISO_INSTANT.parse(createdAtString); - instant = Instant.from(ta); - createdAt = Date.from(instant); - } - - if (updatedAtString != null && !updatedAtString.isBlank()) { - - ta = DateTimeFormatter.ISO_INSTANT.parse(updatedAtString); - instant = Instant.from(ta); - updatedAt = Date.from(instant); - } + createdAt = DateParser.parse(createdAtString); + updatedAt = DateParser.parse(updatedAtString); } } diff --git a/src/main/java/com/mailersend/sdk/recipients/BlocklistItem.java b/src/main/java/com/mailersend/sdk/recipients/BlocklistItem.java index 94ddd7c..00cb5ec 100644 --- a/src/main/java/com/mailersend/sdk/recipients/BlocklistItem.java +++ b/src/main/java/com/mailersend/sdk/recipients/BlocklistItem.java @@ -7,12 +7,10 @@ **************************************************/ package com.mailersend.sdk.recipients; -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.time.temporal.TemporalAccessor; import java.util.Date; import com.google.gson.annotations.SerializedName; +import com.mailersend.sdk.util.DateParser; /** *BlocklistItem class.
@@ -50,22 +48,8 @@ public class BlocklistItem { */ protected void parseDates() { - TemporalAccessor ta; - Instant instant; - - if (createdAtString != null && !createdAtString.isBlank()) { - - ta = DateTimeFormatter.ISO_INSTANT.parse(createdAtString); - instant = Instant.from(ta); - createdAt = Date.from(instant); - } - - if (updatedAtString != null && !updatedAtString.isBlank()) { - - ta = DateTimeFormatter.ISO_INSTANT.parse(updatedAtString); - instant = Instant.from(ta); - updatedAt = Date.from(instant); - } + createdAt = DateParser.parse(createdAtString); + updatedAt = DateParser.parse(updatedAtString); if (domain != null) { diff --git a/src/main/java/com/mailersend/sdk/recipients/BlocklistListResponse.java b/src/main/java/com/mailersend/sdk/recipients/BlocklistListResponse.java index a57610d..a2affac 100644 --- a/src/main/java/com/mailersend/sdk/recipients/BlocklistListResponse.java +++ b/src/main/java/com/mailersend/sdk/recipients/BlocklistListResponse.java @@ -20,4 +20,22 @@ public class BlocklistListResponse extends PaginatedResponse { @SerializedName("data") public BlocklistItem[] items; + + + /** + * Is called to perform any actions after the deserialization of the response + * Do not call directly + */ + public void postDeserialize() { + + if (items == null) { + + return; + } + + for (BlocklistItem item : items) { + + item.parseDates(); + } + } } diff --git a/src/main/java/com/mailersend/sdk/recipients/Recipient.java b/src/main/java/com/mailersend/sdk/recipients/Recipient.java index 3f036d9..acaea14 100644 --- a/src/main/java/com/mailersend/sdk/recipients/Recipient.java +++ b/src/main/java/com/mailersend/sdk/recipients/Recipient.java @@ -7,14 +7,12 @@ **************************************************/ package com.mailersend.sdk.recipients; -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.time.temporal.TemporalAccessor; import java.util.Date; import com.google.gson.annotations.SerializedName; import com.mailersend.sdk.domains.Domain; import com.mailersend.sdk.util.ApiEmail; +import com.mailersend.sdk.util.DateParser; /** *Recipient class.
@@ -59,29 +57,9 @@ public class Recipient { */ protected void parseDates() { - TemporalAccessor ta; - Instant instant; - - if (createdAtString != null && !createdAtString.isBlank()) { - - ta = DateTimeFormatter.ISO_INSTANT.parse(createdAtString); - instant = Instant.from(ta); - createdAt = Date.from(instant); - } - - if (updatedAtString != null && !updatedAtString.isBlank()) { - - ta = DateTimeFormatter.ISO_INSTANT.parse(updatedAtString); - instant = Instant.from(ta); - updatedAt = Date.from(instant); - } - - if (deletedAtString != null && !deletedAtString.isBlank()) { - - ta = DateTimeFormatter.ISO_INSTANT.parse(deletedAtString); - instant = Instant.from(ta); - deletedAt = Date.from(instant); - } + createdAt = DateParser.parse(createdAtString); + updatedAt = DateParser.parse(updatedAtString); + deletedAt = DateParser.parse(deletedAtString); if (domain != null) { diff --git a/src/main/java/com/mailersend/sdk/recipients/Recipients.java b/src/main/java/com/mailersend/sdk/recipients/Recipients.java index ce870f9..7b8bbf0 100644 --- a/src/main/java/com/mailersend/sdk/recipients/Recipients.java +++ b/src/main/java/com/mailersend/sdk/recipients/Recipients.java @@ -119,6 +119,8 @@ public ApiRecipientsList getRecipients() throws MailerSendException { ApiRecipientsList response = api.getRequest(endpoint, ApiRecipientsList.class); + response.postProcessing(); + return response; } @@ -139,6 +141,11 @@ public Recipient getRecipient(String recipientId) throws MailerSendException { RecipientResponse response = api.getRequest(endpoint, RecipientResponse.class); + if (response.recipient != null) { + + response.recipient.parseDates(); + } + return response.recipient; } diff --git a/src/main/java/com/mailersend/sdk/recipients/SuppressionAddBuilder.java b/src/main/java/com/mailersend/sdk/recipients/SuppressionAddBuilder.java index 8534df4..a4f3b73 100644 --- a/src/main/java/com/mailersend/sdk/recipients/SuppressionAddBuilder.java +++ b/src/main/java/com/mailersend/sdk/recipients/SuppressionAddBuilder.java @@ -104,6 +104,8 @@ public BlocklistItem[] addToBlocklist() throws MailerSendException { BlocklistListResponse response = api.postRequest(endpoint, json, BlocklistListResponse.class); + response.postDeserialize(); + return response.items; } @@ -178,6 +180,8 @@ private SuppressionList addRecipientsToSuppressionList(String endpoint) throws M SuppressionList response = api.postRequest(endpoint, json, SuppressionList.class); + response.postDeserialize(); + return response; } diff --git a/src/main/java/com/mailersend/sdk/recipients/SuppressionItem.java b/src/main/java/com/mailersend/sdk/recipients/SuppressionItem.java index 34a27e5..3abb428 100644 --- a/src/main/java/com/mailersend/sdk/recipients/SuppressionItem.java +++ b/src/main/java/com/mailersend/sdk/recipients/SuppressionItem.java @@ -29,6 +29,14 @@ public class SuppressionItem { @SerializedName("created_at") public String createdAt; + /** Only returned by the on-hold list, null otherwise */ + @SerializedName("on_hold_until") + public String onHoldUntil; + + /** Only returned by the on-hold list, null otherwise */ + @SerializedName("email") + public String email; + @SerializedName("recipient") public Recipient recipient; } diff --git a/src/main/java/com/mailersend/sdk/recipients/SuppressionList.java b/src/main/java/com/mailersend/sdk/recipients/SuppressionList.java index 175357f..8a8c71a 100644 --- a/src/main/java/com/mailersend/sdk/recipients/SuppressionList.java +++ b/src/main/java/com/mailersend/sdk/recipients/SuppressionList.java @@ -20,4 +20,25 @@ public class SuppressionList extends PaginatedResponse { @SerializedName("data") public SuppressionItem[] items; + + + /** + * Is called to perform any actions after the deserialization of the response + * Do not call directly + */ + public void postDeserialize() { + + if (items == null) { + + return; + } + + for (SuppressionItem item : items) { + + if (item.recipient != null) { + + item.recipient.parseDates(); + } + } + } } diff --git a/src/main/java/com/mailersend/sdk/recipients/Suppressions.java b/src/main/java/com/mailersend/sdk/recipients/Suppressions.java index 66cf790..c9b5dff 100644 --- a/src/main/java/com/mailersend/sdk/recipients/Suppressions.java +++ b/src/main/java/com/mailersend/sdk/recipients/Suppressions.java @@ -114,6 +114,8 @@ public BlocklistListResponse getBlocklist() throws MailerSendException { BlocklistListResponse response = api.getRequest(endpoint, BlocklistListResponse.class); + response.postDeserialize(); + return response; } @@ -173,6 +175,8 @@ private SuppressionList getSuppressionList(String endpoint) throws MailerSendExc SuppressionList response = api.getRequest(endpoint, SuppressionList.class); + response.postDeserialize(); + return response; } diff --git a/src/main/java/com/mailersend/sdk/senderidentities/SenderIdentities.java b/src/main/java/com/mailersend/sdk/senderidentities/SenderIdentities.java index ddd78de..5ce64b6 100644 --- a/src/main/java/com/mailersend/sdk/senderidentities/SenderIdentities.java +++ b/src/main/java/com/mailersend/sdk/senderidentities/SenderIdentities.java @@ -7,6 +7,8 @@ **************************************************/ package com.mailersend.sdk.senderidentities; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.stream.IntStream; @@ -291,7 +293,7 @@ private String prepareParamsUrl() { } if (queryFilter != null && !queryFilter.isBlank()) { - params.add("query=".concat(queryFilter)); + params.add("query=".concat(urlEncode(queryFilter))); } if (orderByFilter != null && !orderByFilter.isBlank()) { @@ -311,4 +313,15 @@ private String prepareParamsUrl() { return requestParams; } + + + /** + * Url encodes a query parameter value + * @param value + * @return + */ + private String urlEncode(String value) { + + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } } diff --git a/src/main/java/com/mailersend/sdk/sms/SmsInfo.java b/src/main/java/com/mailersend/sdk/sms/SmsInfo.java index 7c70d40..443086a 100644 --- a/src/main/java/com/mailersend/sdk/sms/SmsInfo.java +++ b/src/main/java/com/mailersend/sdk/sms/SmsInfo.java @@ -34,6 +34,9 @@ public class SmsInfo { @SerializedName("text") public String text; + @SerializedName("compiled_text") + public String compiledText; + @SerializedName("status") public String status; @@ -43,6 +46,9 @@ public class SmsInfo { @SerializedName("error_type") public String errorType; + @SerializedName("error_code") + public Integer errorCode; + @SerializedName("error_description") public String errorDescription; diff --git a/src/main/java/com/mailersend/sdk/sms/inboundroutes/SmsInboundRouteBuilderBody.java b/src/main/java/com/mailersend/sdk/sms/inboundroutes/SmsInboundRouteBuilderBody.java index c42285e..03d17a4 100644 --- a/src/main/java/com/mailersend/sdk/sms/inboundroutes/SmsInboundRouteBuilderBody.java +++ b/src/main/java/com/mailersend/sdk/sms/inboundroutes/SmsInboundRouteBuilderBody.java @@ -30,5 +30,5 @@ public class SmsInboundRouteBuilderBody { public String forwardUrl; @SerializedName("enabled") - public boolean enabled; + public Boolean enabled = null; } diff --git a/src/main/java/com/mailersend/sdk/sms/messages/SmsMessage.java b/src/main/java/com/mailersend/sdk/sms/messages/SmsMessage.java index da42115..bb3b38a 100644 --- a/src/main/java/com/mailersend/sdk/sms/messages/SmsMessage.java +++ b/src/main/java/com/mailersend/sdk/sms/messages/SmsMessage.java @@ -43,8 +43,16 @@ public class SmsMessage { public Date createdAt; + /** + * The first entry of {@link #smsList}. + * + * @deprecated The API returns a list of SMS. Use {@link #smsList} instead. + */ + @Deprecated + public transient SmsInfo sms; + @SerializedName("sms") - public SmsInfo sms; + public SmsInfo[] smsList; /** *postDeserialize.
@@ -59,5 +67,15 @@ public void postDeserialize() { instant = Instant.from(ta); createdAt = Date.from(instant); } + + if (smsList != null) { + for (SmsInfo s : smsList) { + s.postDeserialize(); + } + + if (smsList.length > 0) { + sms = smsList[0]; + } + } } } diff --git a/src/main/java/com/mailersend/sdk/sms/recipients/SmsRecipient.java b/src/main/java/com/mailersend/sdk/sms/recipients/SmsRecipient.java index db38d2d..d3bb457 100644 --- a/src/main/java/com/mailersend/sdk/sms/recipients/SmsRecipient.java +++ b/src/main/java/com/mailersend/sdk/sms/recipients/SmsRecipient.java @@ -32,6 +32,14 @@ public class SmsRecipient { @SerializedName("status") public String status; + @SerializedName("opted_out_at") + private String optedOutAtStr; + + public Date optedOutAt; + + @SerializedName("opt_out_source") + public String optOutSource; + @SerializedName("created_at") private String createdAtStr; @@ -54,6 +62,16 @@ public void postDeserialize() { createdAt = Date.from(instant); } + if (optedOutAtStr != null && !optedOutAtStr.isBlank()) { + + TemporalAccessor ta; + Instant instant; + + ta = DateTimeFormatter.ISO_INSTANT.parse(optedOutAtStr); + instant = Instant.from(ta); + optedOutAt = Date.from(instant); + } + if (sms != null) { for (SmsInfo s : sms) { s.postDeserialize(); diff --git a/src/main/java/com/mailersend/sdk/smtpusers/SmtpUser.java b/src/main/java/com/mailersend/sdk/smtpusers/SmtpUser.java index baf3a38..4f0346c 100644 --- a/src/main/java/com/mailersend/sdk/smtpusers/SmtpUser.java +++ b/src/main/java/com/mailersend/sdk/smtpusers/SmtpUser.java @@ -7,8 +7,11 @@ **************************************************/ package com.mailersend.sdk.smtpusers; +import java.util.Date; + import com.google.gson.annotations.SerializedName; import com.mailersend.sdk.MailerSendResponse; +import com.mailersend.sdk.util.DateParser; /** *SmtpUser class.
@@ -42,6 +45,21 @@ public class SmtpUser extends MailerSendResponse { @SerializedName("port") public int port; - @SerializedName("domain_id") + @SerializedName("domain") public String domainId; + + @SerializedName("created_at") + private String createdAtStr; + + public Date createdAt; + + + /** + * Is called to perform any actions after the deserialization of the response + * Do not call directly + */ + public void postDeserialize() { + + createdAt = DateParser.parse(createdAtStr); + } } diff --git a/src/main/java/com/mailersend/sdk/smtpusers/SmtpUserBuilder.java b/src/main/java/com/mailersend/sdk/smtpusers/SmtpUserBuilder.java index a9719fd..c8103d3 100644 --- a/src/main/java/com/mailersend/sdk/smtpusers/SmtpUserBuilder.java +++ b/src/main/java/com/mailersend/sdk/smtpusers/SmtpUserBuilder.java @@ -93,6 +93,11 @@ public SmtpUser createSmtpUser(String domainId) throws MailerSendException { SingleSmtpUserResponse response = api.postRequest(endpoint, json, SingleSmtpUserResponse.class); + if (response.smtpUser != null) { + + response.smtpUser.postDeserialize(); + } + return response.smtpUser; } @@ -124,6 +129,11 @@ public SmtpUser updateSmtpUser(String domainId, String smtpUserId) throws Mailer SingleSmtpUserResponse response = api.putRequest(endpoint, json, SingleSmtpUserResponse.class); + if (response.smtpUser != null) { + + response.smtpUser.postDeserialize(); + } + return response.smtpUser; } } diff --git a/src/main/java/com/mailersend/sdk/smtpusers/SmtpUsers.java b/src/main/java/com/mailersend/sdk/smtpusers/SmtpUsers.java index f6fbfc9..39f68e0 100644 --- a/src/main/java/com/mailersend/sdk/smtpusers/SmtpUsers.java +++ b/src/main/java/com/mailersend/sdk/smtpusers/SmtpUsers.java @@ -98,6 +98,8 @@ public SmtpUsersList getSmtpUsers(String domainId) throws MailerSendException { SmtpUsersList response = api.getRequest(endpoint, SmtpUsersList.class); + response.postDeserialize(); + return response; } @@ -119,6 +121,11 @@ public SmtpUser getSmtpUser(String domainId, String smtpUserId) throws MailerSen SingleSmtpUserResponse response = api.getRequest(endpoint, SingleSmtpUserResponse.class); + if (response.smtpUser != null) { + + response.smtpUser.postDeserialize(); + } + return response.smtpUser; } diff --git a/src/main/java/com/mailersend/sdk/smtpusers/SmtpUsersList.java b/src/main/java/com/mailersend/sdk/smtpusers/SmtpUsersList.java index 2d656ac..4adebb7 100644 --- a/src/main/java/com/mailersend/sdk/smtpusers/SmtpUsersList.java +++ b/src/main/java/com/mailersend/sdk/smtpusers/SmtpUsersList.java @@ -20,4 +20,22 @@ public class SmtpUsersList extends MailerSendResponse { @SerializedName("data") public SmtpUser[] smtpUsers; + + + /** + * Is called to perform any actions after the deserialization of the response + * Do not call directly + */ + public void postDeserialize() { + + if (smtpUsers == null) { + + return; + } + + for (SmtpUser smtpUser : smtpUsers) { + + smtpUser.postDeserialize(); + } + } } diff --git a/src/main/java/com/mailersend/sdk/templates/Template.java b/src/main/java/com/mailersend/sdk/templates/Template.java index 2eca098..cead049 100644 --- a/src/main/java/com/mailersend/sdk/templates/Template.java +++ b/src/main/java/com/mailersend/sdk/templates/Template.java @@ -57,9 +57,21 @@ public class Template { @SerializedName("category") public TemplateCategory category; + @SerializedName("categories") + public TemplateCategory[] categories; + @SerializedName("domain") public TemplateDomain domain; + @SerializedName("translations") + public TemplateTranslation[] translations; + + @SerializedName("html") + public String html; + + @SerializedName("text") + public String text; + @SerializedName("template_stats") public TemplateStats stats; @@ -81,6 +93,14 @@ protected void postDeserialize() { stats.postDeserialize(); } + + if (translations != null) { + + for (TemplateTranslation translation : translations) { + + translation.postDeserialize(); + } + } } diff --git a/src/main/java/com/mailersend/sdk/templates/TemplateBuilder.java b/src/main/java/com/mailersend/sdk/templates/TemplateBuilder.java index 4712277..6edcce1 100644 --- a/src/main/java/com/mailersend/sdk/templates/TemplateBuilder.java +++ b/src/main/java/com/mailersend/sdk/templates/TemplateBuilder.java @@ -9,6 +9,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.MailerSendApi; import com.mailersend.sdk.exceptions.MailerSendException; @@ -92,7 +93,9 @@ public TemplateBuilder text(String text) { * * @param subject a {@link java.lang.String} object. * @return a {@link com.mailersend.sdk.templates.TemplateBuilder} object. + * @deprecated The templates API does not accept a subject, so it is not sent. */ + @Deprecated public TemplateBuilder subject(String subject) { builderBody.subject = subject; @@ -162,6 +165,7 @@ public TemplateBuilder tags(String[] tags) throws MailerSendException { /** * Set whether plain text should be auto-generated from the HTML. + * Only sent by updateTemplate(), the create endpoint does not accept it. * * @param autoGenerate a boolean. * @return a {@link com.mailersend.sdk.templates.TemplateBuilder} object. @@ -202,7 +206,10 @@ public Template createTemplate() throws MailerSendException { .addDeserializationExclusionStrategy(new JsonSerializationDeserializationStrategy(true)) .create(); - String json = gson.toJson(builderBody); + JsonObject body = gson.toJsonTree(builderBody).getAsJsonObject(); + body.remove("auto_generate"); + + String json = gson.toJson(body); builderBody.reset(); diff --git a/src/main/java/com/mailersend/sdk/templates/TemplateBuilderBody.java b/src/main/java/com/mailersend/sdk/templates/TemplateBuilderBody.java index fa585e1..c90c805 100644 --- a/src/main/java/com/mailersend/sdk/templates/TemplateBuilderBody.java +++ b/src/main/java/com/mailersend/sdk/templates/TemplateBuilderBody.java @@ -25,7 +25,7 @@ class TemplateBuilderBody { public String text; @SerializedName("subject") - public String subject; + public transient String subject; @SerializedName("domain_id") public String domainId; diff --git a/src/main/java/com/mailersend/sdk/templates/TemplateDomain.java b/src/main/java/com/mailersend/sdk/templates/TemplateDomain.java index 45d5977..43101e0 100644 --- a/src/main/java/com/mailersend/sdk/templates/TemplateDomain.java +++ b/src/main/java/com/mailersend/sdk/templates/TemplateDomain.java @@ -7,6 +7,13 @@ **************************************************/ package com.mailersend.sdk.templates; +import java.lang.reflect.Type; + +import com.google.gson.Gson; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import com.mailersend.sdk.domains.Domain; @@ -18,6 +25,28 @@ */ public class TemplateDomain extends Domain { + /** + * Null when the API returns the totals as an empty list, which it does for the domain of a template + */ @SerializedName("totals") + @JsonAdapter(TotalsDeserializer.class) public TemplateDomainTotals totals; + + + /** + * Reads the totals object, or null for the empty list the API returns when the totals are not loaded + */ + static class TotalsDeserializer implements JsonDeserializerTemplateTranslation class.
+ * + * @author mailersend + * @version $Id: $Id + */ +public class TemplateTranslation { + + @SerializedName("language") + public String language; + + @SerializedName("language_name") + public String languageName; + + @SerializedName("status") + public String status; + + @SerializedName("total_email_sent") + public int totalEmailSent; + + public Date lastEmailSentAt; + + @SerializedName("last_email_sent_at") + private String lastEmailSentAtStr; + + public Date translatedAt; + + @SerializedName("translated_at") + private String translatedAtStr; + + public Date createdAt; + + @SerializedName("created_at") + private String createdAtStr; + + public Date updatedAt; + + @SerializedName("updated_at") + private String updatedAtStr; + + + /** + * Is called to perform any actions after the deserialization of the response + * Do not call directly + */ + protected void postDeserialize() { + + lastEmailSentAt = DateParser.parse(lastEmailSentAtStr); + translatedAt = DateParser.parse(translatedAtStr); + createdAt = DateParser.parse(createdAtStr); + updatedAt = DateParser.parse(updatedAtStr); + } +} diff --git a/src/main/java/com/mailersend/sdk/tokens/Token.java b/src/main/java/com/mailersend/sdk/tokens/Token.java index 0600d5b..9cdd2cd 100644 --- a/src/main/java/com/mailersend/sdk/tokens/Token.java +++ b/src/main/java/com/mailersend/sdk/tokens/Token.java @@ -34,6 +34,9 @@ public class Token { @SerializedName("scopes") public String[] scopes; + @SerializedName("domain") + public TokenDomain domain; + @SerializedName("created_at") private String createdAtStr; diff --git a/src/main/java/com/mailersend/sdk/tokens/TokenAdd.java b/src/main/java/com/mailersend/sdk/tokens/TokenAdd.java index 7b1e14f..0e690f5 100644 --- a/src/main/java/com/mailersend/sdk/tokens/TokenAdd.java +++ b/src/main/java/com/mailersend/sdk/tokens/TokenAdd.java @@ -38,6 +38,18 @@ public class TokenAdd { @SerializedName("domain") public TokenDomain domain; + @SerializedName("has_full") + public boolean hasFull; + + @SerializedName("has_only_sending") + public boolean hasOnlySending; + + @SerializedName("preview") + public String preview; + + @SerializedName("expires_at") + public String expiresAt; + @SerializedName("created_at") private String createdAtStr; diff --git a/src/main/java/com/mailersend/sdk/tokens/TokenListResponse.java b/src/main/java/com/mailersend/sdk/tokens/TokenListResponse.java index e7e2334..fc80854 100644 --- a/src/main/java/com/mailersend/sdk/tokens/TokenListResponse.java +++ b/src/main/java/com/mailersend/sdk/tokens/TokenListResponse.java @@ -8,7 +8,7 @@ package com.mailersend.sdk.tokens; import com.google.gson.annotations.SerializedName; -import com.mailersend.sdk.MailerSendResponse; +import com.mailersend.sdk.util.PaginatedResponse; /** *TokenListResponse class.
@@ -16,7 +16,7 @@ * @author mailersend * @version $Id: $Id */ -public class TokenListResponse extends MailerSendResponse { +public class TokenListResponse extends PaginatedResponse { @SerializedName("data") public Token[] tokens; diff --git a/src/main/java/com/mailersend/sdk/tokens/TokenUpdateRequestBody.java b/src/main/java/com/mailersend/sdk/tokens/TokenUpdateRequestBody.java new file mode 100644 index 0000000..0aac793 --- /dev/null +++ b/src/main/java/com/mailersend/sdk/tokens/TokenUpdateRequestBody.java @@ -0,0 +1,22 @@ +/************************************************* + * MailerSend Java SDK + * https://github.com/mailersend/mailersend-java + * + * @author MailerSendTokens class.
@@ -176,17 +179,16 @@ public Token updateToken(String tokenId, String name, String status) throws Mail MailerSendApi api = new MailerSendApi(); api.setToken(apiObjectReference.getToken()); - ArrayListPROCESSED="processed" */
+ /**
+ * Constant PROCESSED="processed"
+ *
+ * @deprecated The API does not accept processed as an event filter.
+ */
+ @Deprecated
public static final String PROCESSED = "processed";
/** Constant QUEUED="queued" */
diff --git a/src/main/java/com/mailersend/sdk/webhooks/WebhookEvents.java b/src/main/java/com/mailersend/sdk/webhooks/WebhookEvents.java
index 2e869d7..f3a269e 100644
--- a/src/main/java/com/mailersend/sdk/webhooks/WebhookEvents.java
+++ b/src/main/java/com/mailersend/sdk/webhooks/WebhookEvents.java
@@ -48,6 +48,9 @@ public class WebhookEvents {
/** Constant ACTIVITY_SPAM_COMPLAINT="activity.spam_complaint" */
public static String ACTIVITY_SPAM_COMPLAINT = "activity.spam_complaint";
+ /** Constant ACTIVITY_SUPPRESSED="activity.suppressed" */
+ public static String ACTIVITY_SUPPRESSED = "activity.suppressed";
+
/** Constant ACTIVITY_SURVEY_OPENED="activity.survey_opened" */
public static String ACTIVITY_SURVEY_OPENED = "activity.survey_opened";
@@ -66,9 +69,15 @@ public class WebhookEvents {
/** Constant INBOUND_FORWARD_FAILED="inbound_forward.failed" */
public static String INBOUND_FORWARD_FAILED = "inbound_forward.failed";
+ /** Constant INBOUND_MESSAGE_REJECTED="inbound_message.rejected" */
+ public static String INBOUND_MESSAGE_REJECTED = "inbound_message.rejected";
+
/** Constant EMAIL_SINGLE_VERIFIED="email_single.verified" */
public static String EMAIL_SINGLE_VERIFIED = "email_single.verified";
+ /** Constant EMAIL_LIST_CREATED="email_list.created" */
+ public static String EMAIL_LIST_CREATED = "email_list.created";
+
/** Constant EMAIL_LIST_VERIFIED="email_list.verified" */
public static String EMAIL_LIST_VERIFIED = "email_list.verified";
@@ -94,13 +103,16 @@ public class WebhookEvents {
"activity.clicked_unique",
"activity.unsubscribed",
"activity.spam_complaint",
+ "activity.suppressed",
"activity.survey_opened",
"activity.survey_submitted",
"sender_identity.verified",
"maintenance.start",
"maintenance.end",
"inbound_forward.failed",
+ "inbound_message.rejected",
"email_single.verified",
+ "email_list.created",
"email_list.verified",
"bulk_email.completed",
"recipient.on_hold_added",
diff --git a/src/main/java/com/mailersend/sdk/webhooks/WebhooksBuilder.java b/src/main/java/com/mailersend/sdk/webhooks/WebhooksBuilder.java
index a0d627c..6e0096f 100644
--- a/src/main/java/com/mailersend/sdk/webhooks/WebhooksBuilder.java
+++ b/src/main/java/com/mailersend/sdk/webhooks/WebhooksBuilder.java
@@ -11,6 +11,7 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
import com.mailersend.sdk.MailerSend;
import com.mailersend.sdk.MailerSendApi;
import com.mailersend.sdk.exceptions.MailerSendException;
@@ -98,6 +99,7 @@ public WebhooksBuilder clearEvents() {
/**
* Set whether the webhook is editable
+ * Only sent by createWebhook(), the update endpoint does not accept it.
*
* @param editable a boolean.
* @return a {@link com.mailersend.sdk.webhooks.WebhooksBuilder} object.
@@ -215,7 +217,7 @@ public Webhook updateWebhook(String webhookId) throws MailerSendException {
if (webhookId == null || webhookId.isBlank()) {
- throw new MailerSendException("Domain ID cannot be empty");
+ throw new MailerSendException("Webhook ID cannot be empty");
}
String endpoint = "/webhooks/".concat(webhookId);
@@ -241,7 +243,10 @@ public Webhook updateWebhook(String webhookId) throws MailerSendException {
.addDeserializationExclusionStrategy(new JsonSerializationDeserializationStrategy(true))
.create();
- String json = gson.toJson(builderBody);
+ JsonObject body = gson.toJsonTree(builderBody).getAsJsonObject();
+ body.remove("editable");
+
+ String json = gson.toJson(body);
// reset the body object's values so that it can be reused
builderBody.reset();
diff --git a/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppMediaHeader.java b/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppMediaHeader.java
new file mode 100644
index 0000000..a1e5b96
--- /dev/null
+++ b/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppMediaHeader.java
@@ -0,0 +1,36 @@
+/*************************************************
+ * MailerSend Java SDK
+ * https://github.com/mailersend/mailersend-java
+ *
+ * @author MailerSend WhatsAppMediaHeader class.
+ * + * @author mailersend + * @version $Id: $Id + */ +public class WhatsAppMediaHeader { + + @SerializedName("link") + public String link; + + @SerializedName("filename") + public String filename; + + /** + *Constructor for WhatsAppMediaHeader.
+ * + * @param link the public https URL of the image, video or document to show in the header + * @param filename the file name shown for a document header, or null + */ + public WhatsAppMediaHeader(String link, String filename) { + this.link = link; + this.filename = filename; + } +} diff --git a/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalization.java b/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalization.java index 80f28e7..15c19fd 100644 --- a/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalization.java +++ b/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalization.java @@ -7,6 +7,8 @@ **************************************************/ package com.mailersend.sdk.whatsapp; +import java.util.Map; + import com.google.gson.annotations.SerializedName; /** @@ -40,6 +42,45 @@ public WhatsAppPersonalization(String to) { */ public WhatsAppPersonalization setHeader(String[] header) { this.data.header = header; + this.data.mediaHeader = null; + this.data.namedHeader = null; + return this; + } + + /** + *setMediaHeader.
+ * + * @param link the public https URL of the image, video or document to show in the header + * @return a {@link com.mailersend.sdk.whatsapp.WhatsAppPersonalization} object. + */ + public WhatsAppPersonalization setMediaHeader(String link) { + return setMediaHeader(link, null); + } + + /** + *setMediaHeader.
+ * + * @param link the public https URL of the image, video or document to show in the header + * @param filename the file name shown for a document header + * @return a {@link com.mailersend.sdk.whatsapp.WhatsAppPersonalization} object. + */ + public WhatsAppPersonalization setMediaHeader(String link, String filename) { + this.data.mediaHeader = new WhatsAppMediaHeader(link, filename); + this.data.header = null; + this.data.namedHeader = null; + return this; + } + + /** + *setNamedHeader.
+ * + * @param header header variable values keyed by variable name + * @return a {@link com.mailersend.sdk.whatsapp.WhatsAppPersonalization} object. + */ + public WhatsAppPersonalization setNamedHeader(MapsetNamedBody.
+ * + * @param body body variable values keyed by variable name + * @return a {@link com.mailersend.sdk.whatsapp.WhatsAppPersonalization} object. + */ + public WhatsAppPersonalization setNamedBody(MapThis is the HTML content
'" + + "}"), client.lastBody()); + } + + + /** + * Tests that bulkSend() sends the send_at of each email as a unix timestamp + */ + @Test + public void testBulkSendSendsTheSendAtOfEachEmail() throws MailerSendException { + + Email email = TestHelper.createBasicEmail(true); + email.setSendAt(new Date(SEND_AT * 1000)); + + Email email2 = TestHelper.createBasicEmail(true); + email2.setSendAt(new Date((SEND_AT + 3600) * 1000)); + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(202, + "{\"message\":\"The bulk email is being processed.\",\"bulk_email_id\":\"614470d1588b866d0454f3e2\"}", + Map.of()); + + ms.emails().bulkSend(new Email[] { email, email2 }); + + JsonArray body = client.lastBody().getAsJsonArray(); + + assertEquals(String.valueOf(SEND_AT), body.get(0).getAsJsonObject().get("send_at").getAsString()); + assertEquals(String.valueOf(SEND_AT + 3600), body.get(1).getAsJsonObject().get("send_at").getAsString()); + assertFalse(body.get(0).getAsJsonObject().has("sendAt")); + } + + + /** 2025-08-27 01:00:00 UTC */ + private static final long SEND_AT = 1756256400L; } diff --git a/src/test/java/com/mailersend/sdk/tests/EmailVerificationTests.java b/src/test/java/com/mailersend/sdk/tests/EmailVerificationTests.java index c40a4dd..d079b11 100644 --- a/src/test/java/com/mailersend/sdk/tests/EmailVerificationTests.java +++ b/src/test/java/com/mailersend/sdk/tests/EmailVerificationTests.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; @@ -16,6 +17,7 @@ import com.mailersend.sdk.exceptions.MailerSendException; import com.mailersend.sdk.vcr.VcrRecorder; import com.mailersend.sdk.emailverification.AsyncEmailVerificationResponse; +import com.mailersend.sdk.emailverification.EmailVerificationBuilder; import com.mailersend.sdk.emailverification.EmailVerificationList; import com.mailersend.sdk.emailverification.EmailVerificationLists; import com.mailersend.sdk.emailverification.ListVerificationResults; @@ -347,4 +349,71 @@ public void testGetVerifyEmailAsyncStatusWithInvalidIdThrows404() { assertEquals(404, ex.code); } + + + /** + * Behavior: the shared builder sends only the emails added since the last create(). + * The fixture only matches a second body of {"name":"Second list","emails":["info3@example.com"]}. + */ + @Test + public void testBuilderResetsEmailsAfterCreate() { + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + try { + + EmailVerificationBuilder builder = ms.emailVerification().builder(); + + builder.name("Test email verification") + .addEmail("info@example.com") + .addEmail("info1@example.com") + .addEmail("info2@example.com") + .create(); + + EmailVerificationList second = builder.name("Second list") + .addEmail("info3@example.com") + .create(); + + assertEquals("Second list", second.name); + + } catch (MailerSendException e) { + + e.printStackTrace(); + fail(); + } + } + + + /** + * Behavior: GET /email-verification/{id}?detailed=true&page=2&limit=10 returns the paginated emails + * and the not_verified statistic. + */ + @Test + public void testGetListDetailed() { + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + try { + + EmailVerificationList list = ms.emailVerification() + .detailed(true) + .getListPage(2) + .getListLimit(10) + .getList(TestHelper.emailVerificationListId); + + assertEquals(1, list.statistics.notVerified); + assertEquals(2, list.emails.results.length); + assertEquals("info@example.com", list.emails.results[0].address); + assertEquals("valid", list.emails.results[0].result); + assertEquals(2, list.emails.pagination.currentPage); + assertEquals(10, list.emails.pagination.perPage); + assertEquals(12, list.emails.pagination.total); + assertNull(list.emails.links.next); + + } catch (MailerSendException e) { + + e.printStackTrace(); + fail(); + } + } } diff --git a/src/test/java/com/mailersend/sdk/tests/InboundRoutesTest.java b/src/test/java/com/mailersend/sdk/tests/InboundRoutesTest.java index 50cdcb3..0e4fe0e 100644 --- a/src/test/java/com/mailersend/sdk/tests/InboundRoutesTest.java +++ b/src/test/java/com/mailersend/sdk/tests/InboundRoutesTest.java @@ -1,12 +1,17 @@ package com.mailersend.sdk.tests; +import static com.mailersend.sdk.tests.CapturingHttpClient.json; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; +import java.time.Instant; +import java.util.Date; import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; @@ -29,6 +34,20 @@ public class InboundRoutesTest { + private static final String INBOUND_URL = "https://api.mailersend.com/v1/inbound"; + + /** The inbound route of the docs, with dns_checked_at set */ + private static final String ROUTE = "{\"id\":\"v57gn2j67qlr6z4d\",\"name\":\"Test name\"," + + "\"address\":\"imxbfnpzn7cbhak9lef5@inbound.mailersend.net\",\"domain\":\"test.mailersend.com\"," + + "\"dns_checked_at\":\"2026-01-15T10:20:30.000000Z\",\"enabled\":true," + + "\"filters\":[{\"type\":\"catch_recipient\",\"key\":null,\"comparer\":\"equal\",\"value\":\"test\"}," + + "{\"type\":\"match_all\",\"key\":null,\"comparer\":null,\"value\":null}]," + + "\"forwards\":[{\"type\":\"webhook\",\"value\":\"https://yourapp.com/hook\",\"secret\":\"jYhafQeTiZgw0qWclkUA7cbqTG3Zfh2j\"}]," + + "\"priority\":100,\"mxValues\":{\"priority\":\"10\",\"target\":\"inbound.mailersend.net\"}," + + "\"catch_type\":\"one\",\"match_type\":\"one\",\"include_attachments\":false}"; + + private static final String ROUTE_RESPONSE = "{\"data\":" + ROUTE + "}"; + @BeforeEach public void setupEach(TestInfo info) throws IOException { @@ -636,4 +655,153 @@ public void update_route_also_validates_catch_type_enum() { assertEquals("catch_type must be one of: all, one", e.getMessage()); } + + + /** + * Tests that addRoute() posts every documented field to the inbound endpoint, without the + * inbound address, the inbound subdomain and the forward secret and id that the API does not + * accept, and parses the created route + */ + @Test + @SuppressWarnings("deprecation") + public void addRouteSendsEveryDocumentedField() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(201, ROUTE_RESPONSE); + + Forward forward = new Forward(); + forward.id = "forward-id"; + forward.type = "webhook"; + forward.value = "https://yourapp.com/hook"; + forward.secret = "asdfgh"; + + Filter catchFilter = new Filter(); + catchFilter.comparer = "equal"; + catchFilter.value = "test"; + + InboundRoute route = ms.inboundRoutes().builder() + .domainId(TestHelper.domainId) + .name("Test name") + .domainEnabled(true) + .inboundDomain("inbound.test-sdk.com") + .inboundAddress("ignored@inbound.test-sdk.com") + .inboundSubdomain("ignored") + .inboundPriority(10) + .catchFilter(new CatchFilter("catch_recipient", new Filter[] { catchFilter })) + .catchType("one") + .matchFilter("match_header", new InboundFilter[] { new InboundFilter("equal", "yes", "X-Test") }) + .matchType("one") + .forwards(new Forward[] { forward }) + .includeAttachments(false) + .addRoute(); + + assertEquals("POST", client.lastRequest().method()); + assertEquals(INBOUND_URL, client.lastUrl()); + assertEquals(json("{" + + "'domain_id':'" + TestHelper.domainId + "'," + + "'name':'Test name'," + + "'domain_enabled':true," + + "'inbound_domain':'inbound.test-sdk.com'," + + "'inbound_priority':10," + + "'catch_filter':{'type':'catch_recipient','filters':[{'comparer':'equal','value':'test'}]}," + + "'catch_type':'one'," + + "'match_filter':{'type':'match_header','filters':[{'comparer':'equal','value':'yes','key':'X-Test'}]}," + + "'match_type':'one'," + + "'forwards':[{'type':'webhook','value':'https://yourapp.com/hook'}]," + + "'include_attachments':false" + + "}"), client.lastBody()); + assertEquals("v57gn2j67qlr6z4d", route.id); + assertEquals(Date.from(Instant.parse("2026-01-15T10:20:30Z")), route.dnsCheckedAt); + } + + + /** + * Tests that updateRoute() puts the documented fields to the route's endpoint, without the + * domain id that the update endpoint does not accept, and parses the updated route + */ + @Test + public void updateRouteSendsTheDocumentedFields() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, ROUTE_RESPONSE); + + Forward forward = new Forward(); + forward.type = "email"; + forward.value = "forward@test-sdk.com"; + + InboundRoute route = ms.inboundRoutes().builder() + .domainId(TestHelper.domainId) + .name("Test name") + .domainEnabled(true) + .inboundDomain("inbound.test-sdk.com") + .inboundPriority(10) + .catchFilter(new CatchFilter("catch_all", null)) + .catchType("all") + .matchFilter("match_sender", new InboundFilter[] { new InboundFilter("ends-with", "@test-sdk.com") }) + .matchType("all") + .forwards(new Forward[] { forward }) + .includeAttachments(true) + .updateRoute("v57gn2j67qlr6z4d"); + + assertEquals("PUT", client.lastRequest().method()); + assertEquals(INBOUND_URL + "/v57gn2j67qlr6z4d", client.lastUrl()); + assertEquals(json("{" + + "'name':'Test name'," + + "'domain_enabled':true," + + "'inbound_domain':'inbound.test-sdk.com'," + + "'inbound_priority':10," + + "'catch_filter':{'type':'catch_all'}," + + "'catch_type':'all'," + + "'match_filter':{'type':'match_sender','filters':[{'comparer':'ends-with','value':'@test-sdk.com'}]}," + + "'match_type':'all'," + + "'forwards':[{'type':'email','value':'forward@test-sdk.com'}]," + + "'include_attachments':true" + + "}"), client.lastBody()); + assertEquals(Date.from(Instant.parse("2026-01-15T10:20:30Z")), route.dnsCheckedAt); + } + + + /** + * Tests that getRoute() requests the route's endpoint and parses the catch type, the match type, + * the dns check date and the forward secret + */ + @Test + public void getRouteParsesTheRoute() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, ROUTE_RESPONSE); + + InboundRoute route = ms.inboundRoutes().getRoute("v57gn2j67qlr6z4d"); + + assertEquals("GET", client.lastRequest().method()); + assertEquals(INBOUND_URL + "/v57gn2j67qlr6z4d", client.lastUrl()); + assertEquals("one", route.catchType); + assertEquals("one", route.matchType); + assertFalse(route.includeAttachments); + assertEquals("jYhafQeTiZgw0qWclkUA7cbqTG3Zfh2j", route.forwards[0].secret); + assertEquals(Date.from(Instant.parse("2026-01-15T10:20:30Z")), route.dnsCheckedAt); + } + + + /** + * Tests that getRoutes() returns no routes instead of throwing when the response has no data + */ + @Test + public void getRoutesWithoutDataReturnsNoRoutes() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(200, "{}"); + + InboundRoutesList list = ms.inboundRoutes().getRoutes(); + + assertNull(list.routes); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/RecipientsTest.java b/src/test/java/com/mailersend/sdk/tests/RecipientsTest.java index c3d8054..670f2c3 100644 --- a/src/test/java/com/mailersend/sdk/tests/RecipientsTest.java +++ b/src/test/java/com/mailersend/sdk/tests/RecipientsTest.java @@ -7,9 +7,12 @@ **************************************************/ package com.mailersend.sdk.tests; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; +import java.time.Instant; +import java.util.Date; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -131,4 +134,23 @@ public void testDeleteRecipient() { } } + + + /** + * Tests that the deleted_at of a soft-deleted recipient, which the API returns as "2020-06-10 10:10:13", is parsed as UTC + */ + @Test + public void testSingleRecipientParsesSpaceSeparatedDeletedAt() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, "{\"data\":{\"id\":\"62a2120ba4920c48900f2761\",\"email\":\"john@mailerlite.com\",\"created_at\":\"2020-06-10T10:09:56.000000Z\",\"updated_at\":\"2020-06-10T10:10:13.000000Z\",\"deleted_at\":\"2020-06-10 10:10:13\"}}"); + + Recipient recipient = ms.recipients().getRecipient("62a2120ba4920c48900f2761"); + + assertEquals("GET", client.lastRequest().method()); + assertEquals("https://api.mailersend.com/v1/recipients/62a2120ba4920c48900f2761", client.lastUrl()); + assertEquals(Date.from(Instant.parse("2020-06-10T10:10:13Z")), recipient.deletedAt); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/RecpientsSuppressionListsTest.java b/src/test/java/com/mailersend/sdk/tests/RecpientsSuppressionListsTest.java index d08b85c..98c27a4 100644 --- a/src/test/java/com/mailersend/sdk/tests/RecpientsSuppressionListsTest.java +++ b/src/test/java/com/mailersend/sdk/tests/RecpientsSuppressionListsTest.java @@ -8,10 +8,13 @@ package com.mailersend.sdk.tests; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; +import java.time.Instant; +import java.util.Date; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -571,4 +574,47 @@ public void testDeleteSpamComplaintsAllItems() { fail(); } } + + + /** + * Tests that the on-hold list items carry the on hold until date and the email address + */ + @Test + public void testOnHoldListReadsOnHoldFields() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(200, "{\"data\":[{\"id\":\"64c760a33e2db0f6340f4710\",\"created_at\":\"2023-07-15T19:22:37.579000Z\"," + + "\"on_hold_until\":\"2023-07-18T19:22:37.579000Z\",\"email\":\"shanna.fadel@example.net\"," + + "\"recipient\":{\"id\":\"64c760a33e2db0f6340f470f\",\"email\":\"shanna.fadel@example.net\",\"created_at\":\"2023-07-15T19:22:37.579000Z\"," + + "\"updated_at\":\"2023-07-15T19:22:37.579000Z\",\"deleted_at\":\"\"}}],\"links\":{},\"meta\":{\"current_page\":1}}"); + + SuppressionList onHoldList = ms.recipients().suppressions().getOnHoldList(); + + SuppressionItem item = onHoldList.items[0]; + + assertEquals("2023-07-18T19:22:37.579000Z", item.onHoldUntil); + assertEquals("shanna.fadel@example.net", item.email); + assertEquals(Date.from(Instant.parse("2023-07-15T19:22:37.579Z")), item.recipient.createdAt); + assertNull(item.readableReason); + } + + + /** + * Tests that the dates of the blocklist items are parsed + */ + @Test + public void testBlocklistDatesAreParsed() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(200, "{\"data\":[{\"id\":\"62ff8e0c7fb2fa3f0b0d1f07\",\"type\":\"pattern\",\"pattern\":\".*@example.com\"," + + "\"created_at\":\"2022-08-19T13:23:19.349000Z\",\"updated_at\":\"2022-08-19T13:23:19.349000Z\"}],\"links\":{},\"meta\":{\"current_page\":1}}"); + + BlocklistListResponse blocklist = ms.recipients().suppressions().getBlocklist(); + + assertEquals(Date.from(Instant.parse("2022-08-19T13:23:19.349Z")), blocklist.items[0].createdAt); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/SenderIdentitiesTest.java b/src/test/java/com/mailersend/sdk/tests/SenderIdentitiesTest.java index a12410c..5764ef8 100644 --- a/src/test/java/com/mailersend/sdk/tests/SenderIdentitiesTest.java +++ b/src/test/java/com/mailersend/sdk/tests/SenderIdentitiesTest.java @@ -351,4 +351,37 @@ public void testResendConfirmation() { fail(); } } + + + /** + * Test that every list filter is sent and query values are URL-encoded - behavior: + * GET /identities?page=2&limit=10&domain_id=...&query=test%2B1%40example.com&order_by=email&order=desc + */ + @Test + public void testGetIdentitiesWithFilters() { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + try { + + SenderIdentitiesList list = ms.senderIdentities() + .page(2) + .limit(10) + .domainId(TestHelper.domainId) + .query("test+1@example.com") + .orderBy("email") + .order("desc") + .getIdentities(); + + assertEquals(200, list.responseStatusCode); + assertEquals(2, list.meta.currentPage); + assertEquals("test+1@example.com", list.identities[0].email); + + } catch (MailerSendException e) { + + e.printStackTrace(); + fail(); + } + } } diff --git a/src/test/java/com/mailersend/sdk/tests/SmsInboundRoutesTests.java b/src/test/java/com/mailersend/sdk/tests/SmsInboundRoutesTests.java index 8f7c773..ee2e435 100644 --- a/src/test/java/com/mailersend/sdk/tests/SmsInboundRoutesTests.java +++ b/src/test/java/com/mailersend/sdk/tests/SmsInboundRoutesTests.java @@ -1,6 +1,7 @@ package com.mailersend.sdk.tests; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; @@ -18,6 +19,8 @@ public class SmsInboundRoutesTests { + private static final String smsInboundRouteId = "83yxj6lj5gdo2rme"; + @BeforeEach public void setupEach(TestInfo info) throws IOException { @@ -142,4 +145,57 @@ public void testDeleteSmsInboundRoute() { fail(); } } + + + /** + * Tests that a route created without enabled does not send enabled + */ + @Test + public void testAddSmsInboundRouteWithoutEnabled() { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + try { + + SmsInboundRoute route = ms.sms().inboundRoutes().builder() + .smsNumberId(TestHelper.smsPhoneNumberId) + .name("Test inbound route") + .forwardUrl("https://example.com") + .filter("equal", "START") + .addSmsInboundRoute(); + + assertEquals(smsInboundRouteId, route.id); + assertTrue(route.enabled); + + } catch (MailerSendException e) { + + fail(); + } + } + + + /** + * Tests that an update without enabled does not send enabled + */ + @Test + public void testUpdateSmsInboundRouteWithoutEnabled() { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + try { + + SmsInboundRoute route = ms.sms().inboundRoutes().builder() + .name("Test inbound route renamed") + .updateSmsInboundRoute(smsInboundRouteId); + + assertEquals("Test inbound route renamed", route.name); + assertTrue(route.enabled); + + } catch (MailerSendException e) { + + fail(); + } + } } diff --git a/src/test/java/com/mailersend/sdk/tests/SmsMessagesTest.java b/src/test/java/com/mailersend/sdk/tests/SmsMessagesTest.java index 21af411..1f7aa9d 100644 --- a/src/test/java/com/mailersend/sdk/tests/SmsMessagesTest.java +++ b/src/test/java/com/mailersend/sdk/tests/SmsMessagesTest.java @@ -9,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; @@ -81,4 +82,27 @@ public void testGetSingleSmsMessage() { fail(); } } + + + /** + * Tests that a single SMS message reads the list of SMS the API returns + */ + @Test + @SuppressWarnings("deprecation") + public void testGetSingleSmsMessageReadsTheSmsList() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(200, "{\"data\":{\"id\":\"sms-msg-id-test-1\",\"from\":\"+18133657349\",\"to\":[\"+18332552485\"],\"text\":\"test sms\"," + + "\"sms\":[{\"id\":\"sms-id-1\",\"from\":\"+18133657349\",\"to\":\"+18332552485\",\"text\":\"test sms\",\"compiled_text\":\"test sms\"," + + "\"status\":\"failed\",\"error_type\":\"provider_error\",\"error_code\":30007,\"error_description\":\"Carrier violation\"}]}}"); + + SmsMessage message = ms.sms().messages().getMessage("sms-msg-id-test-1"); + + assertEquals(1, message.smsList.length); + assertEquals("test sms", message.smsList[0].compiledText); + assertEquals(Integer.valueOf(30007), message.smsList[0].errorCode); + assertSame(message.smsList[0], message.sms); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/SmsRecipientsTest.java b/src/test/java/com/mailersend/sdk/tests/SmsRecipientsTest.java index 3378d89..93010b1 100644 --- a/src/test/java/com/mailersend/sdk/tests/SmsRecipientsTest.java +++ b/src/test/java/com/mailersend/sdk/tests/SmsRecipientsTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; +import java.time.Instant; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -134,4 +135,23 @@ public void testSmsRecipientsWithSmsNumberIdFilter() { fail(); } } + + + /** + * Tests that the opt out date and source of an SMS recipient are read + */ + @Test + public void testSmsRecipientReadsOptOut() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(200, "{\"data\":{\"id\":\"627e7503d30078fb2208cc83\",\"number\":\"+1234567890\",\"status\":\"opt_out\"," + + "\"opted_out_at\":\"2022-01-02T12:00:00.000000Z\",\"opt_out_source\":\"api\",\"created_at\":\"2022-05-13T15:10:59.444000Z\"}}"); + + SmsRecipient recipient = ms.sms().recipients().getRecipient("627e7503d30078fb2208cc83"); + + assertEquals("api", recipient.optOutSource); + assertEquals(Instant.parse("2022-01-02T12:00:00Z"), recipient.optedOutAt.toInstant()); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/SmsWebhooksTests.java b/src/test/java/com/mailersend/sdk/tests/SmsWebhooksTests.java index aa12e52..2551272 100644 --- a/src/test/java/com/mailersend/sdk/tests/SmsWebhooksTests.java +++ b/src/test/java/com/mailersend/sdk/tests/SmsWebhooksTests.java @@ -1,5 +1,6 @@ package com.mailersend.sdk.tests; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -138,4 +139,23 @@ public void testDeleteSmsWebhook() { fail(); } } + + + /** + * Tests that an update without events does not send an empty events list, which would remove the webhook's events + */ + @Test + public void testUpdateSmsWebhookWithoutEventsLeavesThemOut() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, "{\"data\":{\"id\":\"jpr9084zjgw63dnx\",\"name\":\"sms updated webhook\",\"events\":[\"sms.sent\"],\"enabled\":true}}"); + + ms.sms().webhooks().builder() + .name("sms updated webhook") + .updateWebhook("jpr9084zjgw63dnx"); + + assertFalse(client.lastBody().getAsJsonObject().has("events")); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/SmtpUsersTest.java b/src/test/java/com/mailersend/sdk/tests/SmtpUsersTest.java index 5c6b7ea..5e170f2 100644 --- a/src/test/java/com/mailersend/sdk/tests/SmtpUsersTest.java +++ b/src/test/java/com/mailersend/sdk/tests/SmtpUsersTest.java @@ -14,6 +14,8 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; +import java.time.Instant; +import java.util.Date; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -233,4 +235,23 @@ public void testInvalidTokenFailsWith401OnListSmtpUsers() { assertEquals(401, e.code); } + + + /** + * Tests that the domain and the creation date of an SMTP user are read + */ + @Test + public void testSmtpUserReadsDomainAndCreatedAt() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(200, "{\"data\":{\"id\":\"0wkg2zj19ovx5py7\",\"name\":\"Support SMTP\",\"enabled\":true," + + "\"domain\":\"jpzkmgq7e5vl059v\",\"created_at\":\"2024-02-20T09:39:44.000000Z\"}}"); + + SmtpUser smtpUser = ms.smtpUsers().getSmtpUser(TestHelper.domainId, TestHelper.smtpUserId); + + assertEquals(TestHelper.domainId, smtpUser.domainId); + assertEquals(Date.from(Instant.parse("2024-02-20T09:39:44Z")), smtpUser.createdAt); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/TemplatesTest.java b/src/test/java/com/mailersend/sdk/tests/TemplatesTest.java index feba0c0..8fca5b2 100644 --- a/src/test/java/com/mailersend/sdk/tests/TemplatesTest.java +++ b/src/test/java/com/mailersend/sdk/tests/TemplatesTest.java @@ -1,10 +1,14 @@ package com.mailersend.sdk.tests; +import static com.mailersend.sdk.tests.CapturingHttpClient.json; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; +import java.time.Instant; +import java.util.Date; import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; @@ -20,11 +24,22 @@ import com.mailersend.sdk.exceptions.MailerSendException; import com.mailersend.sdk.templates.Template; import com.mailersend.sdk.templates.TemplateItem; +import com.mailersend.sdk.templates.TemplateTranslation; import com.mailersend.sdk.templates.TemplatesList; import com.mailersend.sdk.vcr.VcrRecorder; public class TemplatesTest { + private static final String TEMPLATES_URL = "https://api.mailersend.com/v1/templates"; + + private static final String TEMPLATE_ID = "x8emy5o5world01w"; + + /** The create and update response of the docs */ + private static final String TEMPLATE_RESPONSE = "{\"data\":{\"id\":\"x8emy5o5world01w\",\"name\":\"My Template\"," + + "\"description\":null,\"type\":\"html\"," + + "\"image_path\":\"http://mailersend.com/images/templates/placeholder.png\"," + + "\"created_at\":\"2021-06-22T15:38:51.000000Z\",\"updated_at\":\"2021-06-22T15:38:51.000000Z\"}}"; + @BeforeEach public void setupEach(TestInfo info) throws IOException { @@ -298,4 +313,127 @@ public void testDeleteTemplateWithEmptyId() { assertEquals("Template ID cannot be empty", e.getMessage()); } + + /** + * Tests that createTemplate() posts the documented fields, without the subject and the + * auto_generate that the create endpoint does not accept, and parses the created template + */ + @Test + @SuppressWarnings("deprecation") + public void testCreateTemplateSendsTheDocumentedFields() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(201, TEMPLATE_RESPONSE); + + Template template = ms.templates().builder() + .name("My Template") + .html("Hello {{name}}
") + .text("Hello {{name}}") + .subject("Ignored subject") + .domainId(TestHelper.domainId) + .categories(new String[] { "5genxmqlgozlyvk7" }) + .tags(new String[] { "welcome" }) + .autoGenerate(true) + .createTemplate(); + + assertEquals("POST", client.lastRequest().method()); + assertEquals(TEMPLATES_URL, client.lastUrl()); + assertEquals(json("{" + + "'name':'My Template'," + + "'html':'Hello {{name}}
'," + + "'text':'Hello {{name}}'," + + "'domain_id':'" + TestHelper.domainId + "'," + + "'categories':['5genxmqlgozlyvk7']," + + "'tags':['welcome']" + + "}"), client.lastBody()); + assertEquals(TEMPLATE_ID, template.id); + assertEquals(Date.from(Instant.parse("2021-06-22T15:38:51Z")), template.createdAt); + } + + + /** + * Tests that getTemplate() parses the content, the categories, the translations and the stats + * of the documented response + */ + @Test + public void testGetTemplateParsesContentTranslationsAndStats() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + Template template = ms.templates().getTemplate(TEMPLATE_ID); + + assertEquals("Hello {{name}}
", template.html); + assertEquals("Hello {{name}}", template.text); + assertEquals("5genxmqlgozlyvk7", template.categories[0].id); + assertEquals("de", template.translations[0].language); + assertEquals(Date.from(Instant.parse("2021-06-23T09:30:00Z")), template.translations[0].lastEmailSentAt); + + assertEquals(1200, template.stats.total); + assertEquals(0, template.stats.queued); + assertEquals(4, template.stats.sent); + assertEquals(16, template.stats.rejected); + assertEquals(1180, template.stats.delivered); + assertEquals("98", template.stats.deliveryRate); + assertEquals(590, template.stats.opened); + assertEquals("50", template.stats.openRate); + assertEquals(118, template.stats.clicked); + assertEquals("10", template.stats.clickRate); + assertEquals(2, template.stats.unsubscribed); + assertEquals("0", template.stats.unsubscribeRate); + assertEquals(0, template.stats.complained); + assertEquals("0", template.stats.complainRate); + assertEquals(Date.from(Instant.parse("2026-10-13T09:30:00Z")), template.stats.lastEmailSentAt); + } + + + /** + * Tests that getTemplate() leaves the domain totals null when the API returns them as an empty + * list, which it does for the domain of a template + */ + @Test + public void testGetTemplateWithEmptyDomainTotals() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + Template template = ms.templates().getTemplate(TEMPLATE_ID); + + assertEquals("1jreeo", template.domain.id); + assertNull(template.domain.totals); + } + + + /** + * Tests that the list items carry the categories and the translations of the documented response + */ + @Test + public void testGetTemplatesParsesCategoriesAndTranslations() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + TemplatesList list = ms.templates().getTemplates(); + + TemplateItem item = list.templates[0]; + + assertEquals(1, item.categories.length); + assertEquals("5genxmqlgozlyvk7", item.categories[0].id); + + TemplateTranslation translation = item.translations[0]; + + assertEquals("de", translation.language); + assertEquals("German", translation.languageName); + assertEquals("published", translation.status); + assertEquals(120, translation.totalEmailSent); + assertEquals(Date.from(Instant.parse("2020-06-12T08:15:00Z")), translation.lastEmailSentAt); + assertEquals(Date.from(Instant.parse("2020-06-10T10:20:00Z")), translation.translatedAt); + assertEquals(Date.from(Instant.parse("2020-06-10T10:15:00Z")), translation.createdAt); + assertEquals(Date.from(Instant.parse("2020-06-10T10:20:00Z")), translation.updatedAt); + + assertEquals(0, list.templates[1].categories.length); + assertEquals(0, list.templates[1].translations.length); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/TokensTest.java b/src/test/java/com/mailersend/sdk/tests/TokensTest.java index 0916902..7b17585 100644 --- a/src/test/java/com/mailersend/sdk/tests/TokensTest.java +++ b/src/test/java/com/mailersend/sdk/tests/TokensTest.java @@ -314,4 +314,79 @@ public void testCreateTokenWithInvalidScope() { assertEquals("Scope is not valid", ex.getMessage()); } + + /** + * Test that quotes and backslashes in the name are JSON-escaped - behavior: the fixture only matches + * the body {"name":"Name with \"quotes\" and \\ backslash"} + */ + @Test + public void testUpdateTokenNameIsJsonEscaped() { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + String name = "Name with \"quotes\" and \\ backslash"; + + try { + + Token token = ms.tokens().updateToken(TestHelper.tokenIdToPause, name, null); + + assertEquals(name, token.name); + + } catch (MailerSendException e) { + + e.printStackTrace(); + fail(); + } + } + + + /** + * Test page() and limit() are sent - behavior: GET /token?page=2&limit=10 + */ + @Test + public void testGetTokensWithPageAndLimit() { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + try { + + TokenListResponse response = ms.tokens().page(2).limit(10).getTokens(); + + assertEquals(200, response.responseStatusCode); + assertEquals(2, response.meta.currentPage); + assertEquals(TestHelper.tokenIdToPause, response.tokens[0].id); + assertEquals(TestHelper.domainId, response.tokens[0].domain.id); + + } catch (MailerSendException e) { + + e.printStackTrace(); + fail(); + } + } + + + /** + * Test that the create response fields are read + */ + @Test + public void testCreateTokenReadsTheResponseFields() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(201, "{\"data\":{\"id\":\"token-id-test\"," + + "\"accessToken\":\"[redacted]\",\"name\":\"Test token\",\"scopes\":[\"activity_full\"]," + + "\"has_full\":false,\"has_only_sending\":true,\"preview\":\"mlsn.a1b2c3\",\"expires_at\":null," + + "\"created_at\":\"2022-08-19T13:25:53.000000Z\"}}"); + + TokenAdd token = ms.tokens().addBuilder() + .name("Test token") + .addScope(TokenScopes.activityFull) + .addToken(); + + assertTrue(token.hasOnlySending); + assertEquals("mlsn.a1b2c3", token.preview); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/UsersTest.java b/src/test/java/com/mailersend/sdk/tests/UsersTest.java index d63569b..317bd88 100644 --- a/src/test/java/com/mailersend/sdk/tests/UsersTest.java +++ b/src/test/java/com/mailersend/sdk/tests/UsersTest.java @@ -366,4 +366,44 @@ public void testDeleteInvite() { fail(); } } + + + /** + * Test that an invite without permissions, templates or domains sends none of them and reads the response, which is not wrapped in data + */ + @Test + public void testInviteUserSendsNoEmptyLists() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, "{\"id\":\"invite-id-123\",\"email\":\"newuser@example.com\",\"role\":\"Admin\"}"); + + Invite invite = ms.users().builder() + .email(USER_EMAIL) + .role("Admin") + .inviteUser(); + + assertEquals(CapturingHttpClient.json("{'email':'newuser@example.com','role':'Admin'}"), client.lastBody()); + assertEquals("invite-id-123", invite.id); + } + + + /** + * Test that the update response, which is not wrapped in data, is read + */ + @Test + public void testUpdateUserReadsTheResponse() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(200, "{\"id\":\"user-id-123\",\"email\":\"member@example.com\",\"role\":\"Manager\"}"); + + User user = ms.users().builder() + .role("Manager") + .updateUser(USER_ID); + + assertEquals("Manager", user.role); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/WebhooksTest.java b/src/test/java/com/mailersend/sdk/tests/WebhooksTest.java index 0c2a550..290d17d 100644 --- a/src/test/java/com/mailersend/sdk/tests/WebhooksTest.java +++ b/src/test/java/com/mailersend/sdk/tests/WebhooksTest.java @@ -7,6 +7,7 @@ **************************************************/ package com.mailersend.sdk.tests; +import static com.mailersend.sdk.tests.CapturingHttpClient.json; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; @@ -33,6 +34,17 @@ public class WebhooksTest { + private static final String WEBHOOKS_URL = "https://api.mailersend.com/v1/webhooks"; + + private static final String WEBHOOK_ID = "pzkmgq7ow2l059vx"; + + /** The create and update response of the docs, without the domain */ + private static final String WEBHOOK_RESPONSE = "{\"data\":{\"id\":\"pzkmgq7ow2l059vx\"," + + "\"url\":\"https://www.example.com/webhook\",\"events\":[\"activity.sent\",\"activity.delivered\"]," + + "\"name\":\"Webhook\",\"version\":2,\"enabled\":true,\"editable\":true," + + "\"secret\":\"NSKNbRpNZoItpYpMiDcqfO7A1kzi1Ikk\"," + + "\"created_at\":\"2026-01-15T10:20:30.000000Z\",\"updated_at\":\"2026-01-15T10:20:30.000000Z\"}}"; + @BeforeEach public void setupEach(TestInfo info) throws IOException { @@ -358,4 +370,85 @@ public void testWebhookVersionInvalid() { assertEquals("Webhook version must be 1 or 2", e.getMessage()); } + + + /** + * Tests that createWebhook() accepts the events that the docs list and older SDK versions rejected + */ + @ParameterizedTest(name = "{0}") + @MethodSource("addedEvents") + public void testCreateWebhookAcceptsTheEvent(String event) throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(201, WEBHOOK_RESPONSE); + + ms.webhooks().builder() + .url("https://www.example.com/webhook") + .name("Webhook") + .addEvent(event) + .createWebhook(TestHelper.domainId); + + assertEquals(json("['" + event + "']"), client.lastBody().getAsJsonObject().get("events")); + } + + + static StreamHello {{name}}
\",\"text\":\"Hello {{name}}\",\"template_stats\":{\"total\":1200,\"queued\":0,\"sent\":4,\"rejected\":16,\"delivered\":1180,\"delivery_rate\":\"98\",\"opened\":590,\"open_rate\":\"50\",\"clicked\":118,\"click_rate\":\"10\",\"unsubscribed\":2,\"unsubscribe_rate\":0,\"complained\":0,\"complain_rate\":0,\"last_email_sent_at\":\"2026-10-13T09:30:00.000000Z\"}}}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/TemplatesTest_testGetTemplateWithEmptyDomainTotals().json b/src/test/resources/fixtures/TemplatesTest_testGetTemplateWithEmptyDomainTotals().json new file mode 100644 index 0000000..33a3109 --- /dev/null +++ b/src/test/resources/fixtures/TemplatesTest_testGetTemplateWithEmptyDomainTotals().json @@ -0,0 +1 @@ +{"982a3099384c7724baabc6a1bf31df74520d8181":{"body":"{\"data\":{\"id\":\"x8emy5o5world01w\",\"name\":\"libero\",\"description\":null,\"type\":\"html\",\"image_path\":\"http://mailersend.com/images/templates/placeholder.png\",\"variables\":{\"name\":\"\"},\"created_at\":\"2021-06-22T15:38:51.000000Z\",\"updated_at\":\"2021-06-22T16:00:00.000000Z\",\"category\":{\"id\":\"5genxmqlgozlyvk7\",\"name\":\"aut\"},\"categories\":[{\"id\":\"5genxmqlgozlyvk7\",\"name\":\"aut\"}],\"domain\":{\"id\":\"1jreeo\",\"name\":\"domain.com\",\"created_at\":\"2021-06-22T15:38:51.000000Z\",\"updated_at\":\"2021-06-22T15:38:51.000000Z\",\"totals\":[]},\"translations\":[{\"language\":\"de\",\"language_name\":\"German\",\"status\":\"published\",\"total_email_sent\":120,\"last_email_sent_at\":\"2021-06-23T09:30:00.000000Z\",\"translated_at\":\"2021-06-22T16:05:00.000000Z\",\"created_at\":\"2021-06-22T16:02:00.000000Z\",\"updated_at\":\"2021-06-22T16:05:00.000000Z\"}],\"html\":\"Hello {{name}}
\",\"text\":\"Hello {{name}}\",\"template_stats\":{\"total\":1200,\"queued\":0,\"sent\":4,\"rejected\":16,\"delivered\":1180,\"delivery_rate\":\"98\",\"opened\":590,\"open_rate\":\"50\",\"clicked\":118,\"click_rate\":\"10\",\"unsubscribed\":2,\"unsubscribe_rate\":0,\"complained\":0,\"complain_rate\":0,\"last_email_sent_at\":\"2026-10-13T09:30:00.000000Z\"}}}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/TemplatesTest_testGetTemplatesParsesCategoriesAndTranslations().json b/src/test/resources/fixtures/TemplatesTest_testGetTemplatesParsesCategoriesAndTranslations().json new file mode 100644 index 0000000..d240549 --- /dev/null +++ b/src/test/resources/fixtures/TemplatesTest_testGetTemplatesParsesCategoriesAndTranslations().json @@ -0,0 +1 @@ +{"579cae46a27892f19af1eb3a50245041cbf5c30e":{"body":"{\"data\":[{\"id\":\"x8emy5o5world01w\",\"name\":\"Signup email\",\"description\":null,\"type\":\"html\",\"image_path\":\"https://link.com/images/image.jpg\",\"variables\":{\"name\":\"\",\"company\":\"\"},\"created_at\":\"2020-06-10T10:09:56.000000Z\",\"updated_at\":\"2020-06-10T10:09:56.000000Z\",\"category\":{\"id\":\"5genxmqlgozlyvk7\",\"name\":\"aut\"},\"categories\":[{\"id\":\"5genxmqlgozlyvk7\",\"name\":\"aut\"}],\"translations\":[{\"language\":\"de\",\"language_name\":\"German\",\"status\":\"published\",\"total_email_sent\":120,\"last_email_sent_at\":\"2020-06-12T08:15:00.000000Z\",\"translated_at\":\"2020-06-10T10:20:00.000000Z\",\"created_at\":\"2020-06-10T10:15:00.000000Z\",\"updated_at\":\"2020-06-10T10:20:00.000000Z\"}]},{\"id\":\"x8emy5o5world01x\",\"name\":\"Premium subscription\",\"description\":null,\"type\":\"html\",\"image_path\":\"https://link.com/images/image.jpg\",\"created_at\":\"2020-06-10T10:09:56.000000Z\",\"updated_at\":\"2020-06-10T10:09:56.000000Z\",\"category\":null,\"categories\":[],\"translations\":[]}],\"links\":{\"first\":\"https://api.mailersend.com/v1/templates?page=1\",\"last\":null,\"prev\":null,\"next\":null},\"meta\":{\"current_page\":1,\"from\":1,\"path\":\"https://api.mailersend.com/v1/templates\",\"per_page\":25,\"to\":2}}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/TokensTest_TestCreateToken().json b/src/test/resources/fixtures/TokensTest_TestCreateToken().json index c46c8f8..1ed034c 100644 --- a/src/test/resources/fixtures/TokensTest_TestCreateToken().json +++ b/src/test/resources/fixtures/TokensTest_TestCreateToken().json @@ -1 +1 @@ -{"2356e2a466b9c8a5c9ad7baddcf903dde1892077":{"body":"{\"data\":{\"id\":\"783b62557fd71e8586679d5f15e9d5162624a56a9c9a2fdae6336a0105161d69f6bd37c2e0f7893d\",\"accessToken\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNzgzYjYyNTU3ZmQ3MWU4NTg2Njc5ZDVmMTVlOWQ1MTYyNjI0YTU2YTljOWEyZmRhZTYzMzZhMDEwNTE2MWQ2OWY2YmQzN2MyZTBmNzg5M2QiLCJpYXQiOjE2NjA5MTU1NTMuNjQ3NjA2LCJuYmYiOjE2NjA5MTU1NTMuNjQ3NjEyLCJleHAiOjQ4MTY1ODkxNTMuNjQwOTM3LCJzdWIiOiI0NjQ0Iiwic2NvcGVzIjpbImFjdGl2aXR5X2Z1bGwiLCJhbmFseXRpY3NfZnVsbCJdfQ.QdxFVy07aoba1ibgHbCa3bUOQLLrRm_1ZfBuzMuPux90VxW-toACthAHTGGgBpvBld0EQAh3TJNluvdATS3AK_bAfp0s76hSUj-wbYTeEtoiJBWpqKz1a5VVEtrDjhmJxv3c3q0jnk3Pj36umEvCpia_tt9P8xuHdF2w5DPzGPmeyAyDYYDN3SsMkGIHThfMhxlGjfIil5v0HEx56VatYW06EC4Kjyg7FO4O4OP1k0wfAUSNEQJtLNP7ud_gMUiuZTaUt2vxTrOPPsBBbDgfwrltkdflLfpCKqXqYrKMU_Ud3mYJXZB1ZiJ49QZ4Qj-htK-X1lnnLKDa83lmN7xvZ9vOvRLoMgukuUOU-VIk7PZoVGkyODcU8u0URJmLkswX3fe4szpW4nLCZK5oPRk-CiXWTcY3ALh-ZQGQOD8rAgJ7hExJ-NS4xP7lKsEpVdrxCUc8zejqi3He0VgtGzxal0OyeI0pWia4qPizjtN-Utj_PErh-LsQXS2nWXkqp-3n4tqfyGsnoOJoEegkWCWeQzCieIuzLKnIJzvdiU4UtBLRGcxqLEh2-g9hTq6Zp2bjNjwelHzF9LI740DLXZr-GgbU1VMsvxjFV17_1gPQbPIOJaFeccuaTfcnIhsykMzAjK5ksgZ9MpryeyiwGjydMqVfz661t1iqOW3dD2KXoQw\",\"name\":\"Test token\",\"created_at\":\"2022-08-19T13:25:53.000000Z\",\"scopes\":[\"activity_full\",\"analytics_full\"],\"domain\":{\"id\":\"jpzkmgq7e5vl059v\",\"name\":\"test-sdk.com\",\"dkim\":false,\"spf\":false,\"mx\":false,\"tracking\":false,\"is_verified\":true,\"is_dns_active\":false,\"domain_settings\":{\"send_paused\":false,\"track_clicks\":true,\"track_opens\":true,\"track_unsubscribe\":false,\"track_unsubscribe_html\":\"\u003cp\u003eClick here to \u003ca href\u003d\\\"{$unsubscribe}\\\"\u003eunsubscribe\u003c\\/a\u003e\u003c\\/p\u003e\",\"track_unsubscribe_html_enabled\":false,\"track_unsubscribe_plain\":\"Click here to unsubscribe: {$unsubscribe}\",\"track_unsubscribe_plain_enabled\":false,\"track_content\":false,\"custom_tracking_enabled\":false,\"custom_tracking_subdomain\":\"email\",\"return_path_subdomain\":\"mta\",\"inbound_routing_enabled\":false,\"inbound_routing_subdomain\":\"\",\"precedence_bulk\":false},\"can\":{\"manage\":true},\"totals\":[]}}}","headers":{":status":["200"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["73d337c06afb6f35-ATH"],"content-type":["application/json"],"date":["Fri, 19 Aug 2022 13:25:53 GMT"],"expect-ct":["max-age\u003d604800, report-uri\u003d\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""],"server":["cloudflare"],"strict-transport-security":["max-age\u003d15724800; includeSubDomains"],"x-apiquota-remaining":["-1"],"x-apiquota-reset":["2022-08-20T00:00:00Z"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} \ No newline at end of file +{"2356e2a466b9c8a5c9ad7baddcf903dde1892077":{"body":"{\"data\":{\"id\":\"783b62557fd71e8586679d5f15e9d5162624a56a9c9a2fdae6336a0105161d69f6bd37c2e0f7893d\",\"accessToken\":\"[redacted]\",\"name\":\"Test token\",\"created_at\":\"2022-08-19T13:25:53.000000Z\",\"scopes\":[\"activity_full\",\"analytics_full\"],\"domain\":{\"id\":\"jpzkmgq7e5vl059v\",\"name\":\"test-sdk.com\",\"dkim\":false,\"spf\":false,\"mx\":false,\"tracking\":false,\"is_verified\":true,\"is_dns_active\":false,\"domain_settings\":{\"send_paused\":false,\"track_clicks\":true,\"track_opens\":true,\"track_unsubscribe\":false,\"track_unsubscribe_html\":\"\u003cp\u003eClick here to \u003ca href\u003d\\\"{$unsubscribe}\\\"\u003eunsubscribe\u003c\\/a\u003e\u003c\\/p\u003e\",\"track_unsubscribe_html_enabled\":false,\"track_unsubscribe_plain\":\"Click here to unsubscribe: {$unsubscribe}\",\"track_unsubscribe_plain_enabled\":false,\"track_content\":false,\"custom_tracking_enabled\":false,\"custom_tracking_subdomain\":\"email\",\"return_path_subdomain\":\"mta\",\"inbound_routing_enabled\":false,\"inbound_routing_subdomain\":\"\",\"precedence_bulk\":false},\"can\":{\"manage\":true},\"totals\":[]}}}","headers":{":status":["200"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["73d337c06afb6f35-ATH"],"content-type":["application/json"],"date":["Fri, 19 Aug 2022 13:25:53 GMT"],"expect-ct":["max-age\u003d604800, report-uri\u003d\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""],"server":["cloudflare"],"strict-transport-security":["max-age\u003d15724800; includeSubDomains"],"x-apiquota-remaining":["-1"],"x-apiquota-reset":["2022-08-20T00:00:00Z"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/TokensTest_testGetTokensWithPageAndLimit().json b/src/test/resources/fixtures/TokensTest_testGetTokensWithPageAndLimit().json new file mode 100644 index 0000000..75f12aa --- /dev/null +++ b/src/test/resources/fixtures/TokensTest_testGetTokensWithPageAndLimit().json @@ -0,0 +1 @@ +{"4391c60b39b09e19a0d36399cce8b6c3ccf83f48":{"body":"{\"data\":[{\"id\":\"865133ae500de599223a523d764877d7d3a4581ab24661adb044e787692cd6a5503e55fb8c700a03\",\"name\":\"My Token\",\"status\":\"pause\",\"created_at\":\"2022-08-19T13:25:53.000000Z\",\"scopes\":[\"email_full\",\"analytics_full\"],\"domain\":{\"id\":\"jpzkmgq7e5vl059v\",\"name\":\"test-sdk.com\",\"created_at\":\"2022-08-19T13:20:00.000000Z\",\"updated_at\":\"2022-08-19T13:20:10.000000Z\"}}],\"links\":{\"first\":\"https://api.mailersend.com/v1/token?page=1\",\"last\":\"https://api.mailersend.com/v1/token?page=2\",\"prev\":\"https://api.mailersend.com/v1/token?page=1\",\"next\":null},\"meta\":{\"current_page\":2,\"from\":11,\"last_page\":2,\"path\":\"https://api.mailersend.com/v1/token\",\"per_page\":10,\"to\":11,\"total\":11}}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/TokensTest_testUpdateTokenNameIsJsonEscaped().json b/src/test/resources/fixtures/TokensTest_testUpdateTokenNameIsJsonEscaped().json new file mode 100644 index 0000000..eee3099 --- /dev/null +++ b/src/test/resources/fixtures/TokensTest_testUpdateTokenNameIsJsonEscaped().json @@ -0,0 +1 @@ +{"b2f4ce39e97dc5f9b4b54b4c386e66bc21ebc9dd":{"body":"{\"data\":{\"id\":\"865133ae500de599223a523d764877d7d3a4581ab24661adb044e787692cd6a5503e55fb8c700a03\",\"name\":\"Name with \\\"quotes\\\" and \\\\ backslash\",\"status\":\"unpause\",\"created_at\":\"2022-08-19T13:25:53.000000Z\",\"scopes\":[\"email_full\",\"analytics_full\"],\"domain\":{\"id\":\"jpzkmgq7e5vl059v\",\"name\":\"test-sdk.com\",\"created_at\":\"2022-08-19T13:20:00.000000Z\",\"updated_at\":\"2022-08-19T13:20:10.000000Z\"}}}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/UsersTest_TestInviteUser().json b/src/test/resources/fixtures/UsersTest_TestInviteUser().json index 92261e3..973b34e 100644 --- a/src/test/resources/fixtures/UsersTest_TestInviteUser().json +++ b/src/test/resources/fixtures/UsersTest_TestInviteUser().json @@ -1 +1 @@ -{"b5c316aa6ff7b7f9beebd95f201e4d477fa1b405":{"body":"{\"data\":{\"id\":\"invite-id-123\",\"email\":\"newuser@example.com\",\"role\":\"Admin\",\"permissions\":[],\"requires_periodic_password_change\":false,\"created_at\":\"2022-08-19T13:25:53.000000Z\",\"updated_at\":\"2022-08-19T13:25:53.000000Z\"}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} \ No newline at end of file +{"39922a51492976cc475f602343c6d016897488d2":{"body":"{\"id\":\"invite-id-123\",\"email\":\"newuser@example.com\",\"data\":{\"domains\":[],\"templates\":[]},\"permissions\":[],\"role\":\"Admin\",\"requires_periodic_password_change\":false,\"created_at\":\"2024-01-24T06:40:19.000000Z\",\"updated_at\":\"2024-01-24T06:40:19.000000Z\"}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/UsersTest_TestUpdateUser().json b/src/test/resources/fixtures/UsersTest_TestUpdateUser().json index 143b104..549d39a 100644 --- a/src/test/resources/fixtures/UsersTest_TestUpdateUser().json +++ b/src/test/resources/fixtures/UsersTest_TestUpdateUser().json @@ -1 +1 @@ -{"04084b114be381b78537bb7be94d86a944f094f6":{"body":"{\"data\":{\"id\":\"user-id-123\",\"email\":\"member@example.com\",\"name\":\"Test Member\",\"last_name\":\"Last\",\"avatar\":null,\"2fa\":false,\"role\":\"Manager\",\"permissions\":[],\"domains\":[],\"templates\":[],\"created_at\":\"2022-08-19T13:25:53.000000Z\",\"updated_at\":\"2022-08-19T13:30:00.000000Z\"}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} \ No newline at end of file +{"ca80bc6c67a047680c63887315ed7b08c65a09c6":{"body":"{\"id\":\"user-id-123\",\"avatar\":null,\"email\":\"member@example.com\",\"last_name\":\"Last\",\"name\":\"Test Member\",\"2fa\":false,\"created_at\":\"2024-01-22T09:35:04.000000Z\",\"updated_at\":\"2024-01-22T23:40:44.000000Z\",\"role\":\"Manager\",\"permissions\":[],\"domains\":[],\"templates\":[]}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/WhatsAppTests_TestSendWhatsAppWithMediaHeader().json b/src/test/resources/fixtures/WhatsAppTests_TestSendWhatsAppWithMediaHeader().json new file mode 100644 index 0000000..7e752c5 --- /dev/null +++ b/src/test/resources/fixtures/WhatsAppTests_TestSendWhatsAppWithMediaHeader().json @@ -0,0 +1 @@ +{"07be864d2502873bfa48e1997ca31d0d12245588":{"body":"{\"data\":{\"id\":\"67f91c3d69f79df391e9d795\",\"created_at\":\"2026-08-31 07:30:00\"}}","headers":{":status":["202"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"],"x-message-id":["67f91c3d69f79df391e9d795"]},"statusCode":202}} \ No newline at end of file diff --git a/src/test/resources/fixtures/WhatsAppTests_TestSendWhatsAppWithNamedVariables().json b/src/test/resources/fixtures/WhatsAppTests_TestSendWhatsAppWithNamedVariables().json new file mode 100644 index 0000000..5ad9173 --- /dev/null +++ b/src/test/resources/fixtures/WhatsAppTests_TestSendWhatsAppWithNamedVariables().json @@ -0,0 +1 @@ +{"c6798e4a4befe17abbf756af636331cd868277be":{"body":"{\"data\":{\"id\":\"67f91c3d69f79df391e9d796\",\"created_at\":\"2026-08-31 07:30:00\"}}","headers":{":status":["202"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"],"x-message-id":["67f91c3d69f79df391e9d796"]},"statusCode":202}} \ No newline at end of file