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 { + + Map body = new LinkedHashMap(); + body.put("first_name", "John"); + body.put("order_date", "December 31, 2026"); + + WhatsAppPersonalization p1 = new WhatsAppPersonalization("19191234567") + .setMediaHeader("https://www.example.com/invoices/12345.pdf", "invoice-12345.pdf") + .setNamedBody(body); + + String messageId = ms.whatsapp().builder() + .from("12345678901") + .addRecipient("19191234567") + .templateId("your_template_id") + .addPersonalization(p1) + .send(); + + System.out.println(messageId); + + } catch (MailerSendException e) { + + e.printStackTrace(); + } +} +``` + ## WhatsApp messages > **Note:** A token with one of the `whatsapp_read` or `whatsapp_full` scopes is required. Sent messages are kept for 7 days. diff --git a/src/main/java/com/mailersend/sdk/MailerSendApi.java b/src/main/java/com/mailersend/sdk/MailerSendApi.java index f239feb..a30aa0f 100644 --- a/src/main/java/com/mailersend/sdk/MailerSendApi.java +++ b/src/main/java/com/mailersend/sdk/MailerSendApi.java @@ -89,11 +89,11 @@ public T getRequest(String endpoint, Class res } if (responseClass == MailerSendStringResponse.class) { - - MailerSendStringResponse response = new MailerSendStringResponse(); - response.responseString = responseObject.body().toString(); - - return (T) response; + + T response = this.handleApiResponse(responseObject, responseClass); + ((MailerSendStringResponse) response).responseString = responseObject.body().toString(); + + return response; } diff --git a/src/main/java/com/mailersend/sdk/activities/Activities.java b/src/main/java/com/mailersend/sdk/activities/Activities.java index 4e63a21..95124e5 100644 --- a/src/main/java/com/mailersend/sdk/activities/Activities.java +++ b/src/main/java/com/mailersend/sdk/activities/Activities.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.Date; +import java.util.concurrent.TimeUnit; import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.MailerSendApi; @@ -64,7 +65,7 @@ public Activity getSingleActivity(String activityId) throws MailerSendException /** - * Gets the activities for the given domain id + * Gets the first page of the activities of the last 7 days for the given domain id * * @param domainId a {@link java.lang.String} object. * @throws com.mailersend.sdk.exceptions.MailerSendException @@ -72,7 +73,10 @@ public Activity getSingleActivity(String activityId) throws MailerSendException */ public ActivitiesList getActivities(String domainId) throws MailerSendException { - return this.getActivities(domainId, 1, 25, null, null, null); + Date dateTo = new Date(); + Date dateFrom = new Date(dateTo.getTime() - TimeUnit.DAYS.toMillis(7)); + + return this.getActivities(domainId, 1, 25, dateFrom, dateTo, null); } /** diff --git a/src/main/java/com/mailersend/sdk/activities/ActivityEmail.java b/src/main/java/com/mailersend/sdk/activities/ActivityEmail.java index 959dffd..8076286 100644 --- a/src/main/java/com/mailersend/sdk/activities/ActivityEmail.java +++ b/src/main/java/com/mailersend/sdk/activities/ActivityEmail.java @@ -114,7 +114,10 @@ public Email toEmail() { email.html = html; email.text = text; - Collections.addAll(email.tags, tags); + if (tags != null) { + + Collections.addAll(email.tags, tags); + } email.AddRecipient(recipient.toRecipient()); diff --git a/src/main/java/com/mailersend/sdk/analytics/Analytics.java b/src/main/java/com/mailersend/sdk/analytics/Analytics.java index 960f3c3..a6a130d 100644 --- a/src/main/java/com/mailersend/sdk/analytics/Analytics.java +++ b/src/main/java/com/mailersend/sdk/analytics/Analytics.java @@ -7,6 +7,8 @@ **************************************************/ package com.mailersend.sdk.analytics; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -149,7 +151,11 @@ public AnalyticsByDateList getByDate(String groupBy, String[] events) throws Mai ArrayList params = new ArrayList(); - params.add(groupBy); + if (groupBy != null && !groupBy.isBlank()) { + + // keeps accepting the raw "group_by=..." fragment that callers had to pass before + params.add(groupBy.startsWith("group_by=") ? groupBy : "group_by=".concat(urlEncode(groupBy))); + } if (events != null && events.length > 0) { @@ -300,10 +306,21 @@ private String arrayToUrlRequest(String[] values, String field) { params = params.concat("&"); } - params = params.concat(field).concat("[]=").concat(values[i]); + params = params.concat(field).concat("[]=").concat(urlEncode(values[i])); } return params; } + + + /** + * 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/analytics/AnalyticsByDate.java b/src/main/java/com/mailersend/sdk/analytics/AnalyticsByDate.java index b0904cf..90244ea 100644 --- a/src/main/java/com/mailersend/sdk/analytics/AnalyticsByDate.java +++ b/src/main/java/com/mailersend/sdk/analytics/AnalyticsByDate.java @@ -66,6 +66,9 @@ public class AnalyticsByDate { @SerializedName("survey_submitted") public int surveySubmitted; + @SerializedName("suppressed") + public int suppressed; + /** *

postDeserialize.

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 MailerSend + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.blocklistmonitoring; + +import java.io.IOException; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +/** + * Reads the blocklisted count, which the create response returns as a boolean. + */ +class BlocklistedCountAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, Integer value) throws IOException { + out.value(value); + } + + @Override + public Integer read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.BOOLEAN) { + return in.nextBoolean() ? 1 : 0; + } + + return in.nextInt(); + } +} diff --git a/src/main/java/com/mailersend/sdk/dmarcmonitoring/DmarcMonitoring.java b/src/main/java/com/mailersend/sdk/dmarcmonitoring/DmarcMonitoring.java index 27b1aa5..a26ebaf 100644 --- a/src/main/java/com/mailersend/sdk/dmarcmonitoring/DmarcMonitoring.java +++ b/src/main/java/com/mailersend/sdk/dmarcmonitoring/DmarcMonitoring.java @@ -7,6 +7,8 @@ **************************************************/ package com.mailersend.sdk.dmarcmonitoring; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.stream.IntStream; @@ -336,15 +338,15 @@ private String prepareListParamsUrl() { params.add("limit=".concat(String.valueOf(limitFilter))); if (queryFilter != null) { - params.add("query=".concat(queryFilter)); + params.add("query=".concat(urlEncode(queryFilter))); } if (sortByFilter != null) { - params.add("sort_by=".concat(sortByFilter)); + params.add("sort_by=".concat(urlEncode(sortByFilter))); } if (orderFilter != null) { - params.add("order=".concat(orderFilter)); + params.add("order=".concat(urlEncode(orderFilter))); } return buildQueryString(params); @@ -361,23 +363,23 @@ private String prepareReportParamsUrl() { params.add("limit=".concat(String.valueOf(limitFilter))); if (dateFromFilter != null) { - params.add("date_from=".concat(dateFromFilter)); + params.add("date_from=".concat(urlEncode(dateFromFilter))); } if (dateToFilter != null) { - params.add("date_to=".concat(dateToFilter)); + params.add("date_to=".concat(urlEncode(dateToFilter))); } if (searchFilter != null) { - params.add("search=".concat(searchFilter)); + params.add("search=".concat(urlEncode(searchFilter))); } if (categoryFilter != null) { - params.add("category=".concat(categoryFilter)); + params.add("category=".concat(urlEncode(categoryFilter))); } if (reportSourceFilter != null) { - params.add("report_source=".concat(reportSourceFilter)); + params.add("report_source=".concat(urlEncode(reportSourceFilter))); } return buildQueryString(params); @@ -390,11 +392,11 @@ private String prepareReportParamsUrl() { private String prepareReportSourcesParamsUrl() { ArrayList params = new ArrayList(); - params.add("date_from=".concat(dateFromFilter)); - params.add("date_to=".concat(dateToFilter)); + params.add("date_from=".concat(urlEncode(dateFromFilter))); + params.add("date_to=".concat(urlEncode(dateToFilter))); if (statusFilter != null) { - params.add("status=".concat(statusFilter)); + params.add("status=".concat(urlEncode(statusFilter))); } return buildQueryString(params); @@ -411,4 +413,11 @@ private String buildQueryString(ArrayList params) { } return requestParams; } + + /** + * Url encodes a query parameter value + */ + private String urlEncode(String value) { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } } diff --git a/src/main/java/com/mailersend/sdk/domains/Domain.java b/src/main/java/com/mailersend/sdk/domains/Domain.java index 0a6c94c..e2b2b87 100644 --- a/src/main/java/com/mailersend/sdk/domains/Domain.java +++ b/src/main/java/com/mailersend/sdk/domains/Domain.java @@ -7,13 +7,11 @@ **************************************************/ package com.mailersend.sdk.domains; -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.MailerSendResponse; +import com.mailersend.sdk.util.DateParser; /** *

Domain 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 ArrayList rcptTo = new ArrayList(); + @Deprecated + public transient ArrayList rcptTo = new ArrayList(); @SerializedName("subject") public String subject; @@ -75,8 +80,8 @@ public class Email { @SerializedName("personalization") public ArrayList personalization = new ArrayList(); - public Date sendAt; - + public transient Date sendAt; + // keeps the global personalizations protected transient HashMap allRecipientsPersonalization = new HashMap(); @@ -134,6 +139,7 @@ public void addRecipient(Recipient recipient) { * @param recipient a {@link com.mailersend.sdk.Recipient} object. * @deprecated Use {@link #addRecipient(Recipient)} instead. */ + @Deprecated public void AddRecipient(Recipient recipient) { addRecipient(recipient); @@ -157,6 +163,7 @@ public void addRecipients(Recipient[] recipients) { * @param recipients an array of {@link com.mailersend.sdk.Recipient} objects. * @deprecated Use {@link #addRecipients(Recipient[])} instead. */ + @Deprecated public void AddRecipients(Recipient[] recipients) { addRecipients(recipients); @@ -194,6 +201,7 @@ public void addCc(Recipient recipient) { * @param email a {@link java.lang.String} object. * @deprecated Use {@link #addCc(String, String)} instead. */ + @Deprecated public void AddCc(String name, String email) { addCc(name, email); @@ -206,6 +214,7 @@ public void AddCc(String name, String email) { * @param recipient a {@link com.mailersend.sdk.Recipient} object. * @deprecated Use {@link #addCc(Recipient)} instead. */ + @Deprecated public void AddCc(Recipient recipient) { addCc(recipient); @@ -243,6 +252,7 @@ public void addBcc(Recipient recipient) { * @param email a {@link java.lang.String} object. * @deprecated Use {@link #addBcc(String, String)} instead. */ + @Deprecated public void AddBcc(String name, String email) { addBcc(name, email); @@ -255,6 +265,7 @@ public void AddBcc(String name, String email) { * @param recipient a {@link com.mailersend.sdk.Recipient} object. * @deprecated Use {@link #addBcc(Recipient)} instead. */ + @Deprecated public void AddBcc(Recipient recipient) { addBcc(recipient); @@ -290,6 +301,7 @@ public void addReplyTo(String name, String email) { * @param replyTo a {@link com.mailersend.sdk.Recipient} object. * @deprecated Use {@link #addReplyTo(Recipient)} instead. */ + @Deprecated public void AddReplyTo(Recipient replyTo) { addReplyTo(replyTo); @@ -303,6 +315,7 @@ public void AddReplyTo(Recipient replyTo) { * @param email a {@link java.lang.String} object. * @deprecated Use {@link #addReplyTo(String, String)} instead. */ + @Deprecated public void AddReplyTo(String name, String email) { addReplyTo(name, email); @@ -310,12 +323,14 @@ public void AddReplyTo(String name, String email) { /** - * Adds an RCPT TO recipient for SMTP relay delivery. - * When {@code to} is empty and {@code rcptTo} is provided, addresses are forwarded as BCC. + * Adds an RCPT TO recipient * * @param name a {@link java.lang.String} object. * @param email a {@link java.lang.String} object. + * @deprecated The email API does not accept RCPT TO recipients, so they are not sent. + * Use {@link #addRecipient(String, String)}, {@link #addCc(String, String)} or {@link #addBcc(String, String)} instead. */ + @Deprecated public void addRcptTo(String name, String email) { Recipient recipient = new Recipient(name, email); @@ -324,11 +339,13 @@ public void addRcptTo(String name, String email) { /** - * Adds an RCPT TO recipient for SMTP relay delivery. - * When {@code to} is empty and {@code rcptTo} is provided, addresses are forwarded as BCC. + * Adds an RCPT TO recipient * * @param recipient a {@link com.mailersend.sdk.Recipient} object. + * @deprecated The email API does not accept RCPT TO recipients, so they are not sent. + * Use {@link #addRecipient(Recipient)}, {@link #addCc(Recipient)} or {@link #addBcc(Recipient)} instead. */ + @Deprecated public void addRcptTo(Recipient recipient) { this.rcptTo.add(recipient); @@ -539,6 +556,7 @@ public void addVariable(String name, String value) { * @param value a {@link java.lang.String} object. * @deprecated Use {@link #addVariable(String, String)} instead. */ + @Deprecated public void AddVariable(String name, String value) { addVariable(name, value); @@ -562,6 +580,7 @@ public void addTag(String tag) { * @param tag a {@link java.lang.String} object. * @deprecated Use {@link #addTag(String)} instead. */ + @Deprecated public void AddTag(String tag) { addTag(tag); @@ -630,25 +649,48 @@ protected void prepareSubstitutionsForAllRecipients() { * @return String */ public String serializeForSending() { - + + return createSendingGson().toJson(prepareForSending()); + } + + + /** + * Prepares the email for sending and returns it as a JSON object + * + * @return a {@link com.google.gson.JsonObject} object. + */ + protected JsonObject prepareForSending() { + preparePersonalizationForAllRecipients(); prepareSubstitutionsForAllRecipients(); - + if (sendAt != null) { sendAtStamp = String.valueOf(sendAt.getTime() / 1000); } - - Gson gson = new GsonBuilder() + + JsonObject json = createSendingGson().toJsonTree(this).getAsJsonObject(); + + // empty lists are left out because the API rejects some of them, e.g. "references": [] on the Sandbox plan + json.entrySet().removeIf(field -> field.getValue().isJsonArray() && field.getValue().getAsJsonArray().isEmpty()); + + return json; + } + + + /** + * Creates the Gson instance that serializes the email for sending + * + * @return a {@link com.google.gson.Gson} object. + */ + protected Gson createSendingGson() { + + return new GsonBuilder() .addSerializationExclusionStrategy(new JsonSerializationDeserializationStrategy(false)) .addDeserializationExclusionStrategy(new JsonSerializationDeserializationStrategy(true)) .registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter().nullSafe()) .registerTypeAdapter(Instant.class, new InstantTypeAdapter().nullSafe()) .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeTypeAdapter().nullSafe()) .create(); - - String json = gson.toJson(this); - - return json; } /** diff --git a/src/main/java/com/mailersend/sdk/emails/Emails.java b/src/main/java/com/mailersend/sdk/emails/Emails.java index ea90f1c..867caa1 100644 --- a/src/main/java/com/mailersend/sdk/emails/Emails.java +++ b/src/main/java/com/mailersend/sdk/emails/Emails.java @@ -12,6 +12,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Date; +import java.util.Map; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -123,18 +124,19 @@ public String bulkSend(Email[] emails) throws MailerSendException { MailerSendApi api = new MailerSendApi(); api.setToken(apiObjectReference.getToken()); + JsonArray emailsJson = new JsonArray(); + for (Email email : emails) { - - email.preparePersonalizationForAllRecipients(); - email.prepareSubstitutionsForAllRecipients(); + + emailsJson.add(email.prepareForSending()); } - + Gson gson = new GsonBuilder() .addSerializationExclusionStrategy(new JsonSerializationDeserializationStrategy(false)) .addDeserializationExclusionStrategy(new JsonSerializationDeserializationStrategy(true)) .create(); - - String json = gson.toJson(emails); + + String json = gson.toJson(emailsJson); SendBulkResponse response = api.postRequest("/bulk-email", json, SendBulkResponse.class); @@ -185,14 +187,28 @@ public BulkSendStatus deserialize(JsonElement json, Type typeOfT, JsonDeserializ ArrayList messagesIdsList = new ArrayList(); - if (!data.get("messages_id").isJsonNull()) { - - JsonArray messagesIds = data.get("messages_id").getAsJsonArray(); - - for (JsonElement messageId : messagesIds) { - + JsonElement messagesIdEl = data.get("messages_id"); + + if (messagesIdEl.isJsonArray()) { + + for (JsonElement messageId : messagesIdEl.getAsJsonArray()) { + messagesIdsList.add(messageId.getAsString()); } + } else if (messagesIdEl.isJsonObject()) { + + // when an email of the request is not sent, the ids come keyed by the position of the email in the request + for (Map.Entry messageId : messagesIdEl.getAsJsonObject().entrySet()) { + + int position = Integer.parseInt(messageId.getKey()); + + while (messagesIdsList.size() <= position) { + + messagesIdsList.add(null); + } + + messagesIdsList.set(position, messageId.getValue().getAsString()); + } } newStatus.messagesId = messagesIdsList.toArray(new String[0]); @@ -203,8 +219,8 @@ public BulkSendStatus deserialize(JsonElement json, Type typeOfT, JsonDeserializ JsonElement validationErrorsEl = data.get("validation_errors"); - if (validationErrorsEl != null) { - + if (validationErrorsEl != null && !validationErrorsEl.isJsonNull()) { + newStatus.validationErrors = validationErrorsEl.getAsJsonObject(); } diff --git a/src/main/java/com/mailersend/sdk/emailverification/AsyncEmailVerificationData.java b/src/main/java/com/mailersend/sdk/emailverification/AsyncEmailVerificationData.java new file mode 100644 index 0000000..ac9f8bb --- /dev/null +++ b/src/main/java/com/mailersend/sdk/emailverification/AsyncEmailVerificationData.java @@ -0,0 +1,31 @@ +/************************************************* + * MailerSend Java SDK + * https://github.com/mailersend/mailersend-java + * + * @author MailerSend + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.emailverification; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@code data} object of an async email verification response. + */ +class AsyncEmailVerificationData { + + @SerializedName("id") + public String id; + + @SerializedName("address") + public String address; + + @SerializedName("status") + public String status; + + @SerializedName("result") + public String result; + + @SerializedName("error") + public String error; +} diff --git a/src/main/java/com/mailersend/sdk/emailverification/AsyncEmailVerificationResponse.java b/src/main/java/com/mailersend/sdk/emailverification/AsyncEmailVerificationResponse.java index bafac85..0332a97 100644 --- a/src/main/java/com/mailersend/sdk/emailverification/AsyncEmailVerificationResponse.java +++ b/src/main/java/com/mailersend/sdk/emailverification/AsyncEmailVerificationResponse.java @@ -19,6 +19,9 @@ */ public class AsyncEmailVerificationResponse extends MailerSendResponse { + @SerializedName("data") + private AsyncEmailVerificationData data; + @SerializedName("id") public String id; @@ -33,4 +36,20 @@ public class AsyncEmailVerificationResponse extends MailerSendResponse { @SerializedName("error") public String error; + + + /** + * Is called to perform any actions after the deserialization of the response. + * Do not call directly. + */ + protected void postDeserialize() { + + if (data != null) { + id = data.id; + address = data.address; + status = data.status; + result = data.result; + error = data.error; + } + } } diff --git a/src/main/java/com/mailersend/sdk/emailverification/EmailVerification.java b/src/main/java/com/mailersend/sdk/emailverification/EmailVerification.java index 8aecbc5..79cf340 100644 --- a/src/main/java/com/mailersend/sdk/emailverification/EmailVerification.java +++ b/src/main/java/com/mailersend/sdk/emailverification/EmailVerification.java @@ -298,7 +298,11 @@ public AsyncEmailVerificationResponse verifyEmailAsync(String email) throws Mail String json = gson.toJson(new EmailBody(email)); - return api.postRequest(endpoint, json, AsyncEmailVerificationResponse.class); + AsyncEmailVerificationResponse response = api.postRequest(endpoint, json, AsyncEmailVerificationResponse.class); + + response.postDeserialize(); + + return response; } /** @@ -319,7 +323,11 @@ public AsyncEmailVerificationResponse getVerifyEmailAsyncStatus(String id) throw MailerSendApi api = new MailerSendApi(); api.setToken(apiObjectReference.getToken()); - return api.getRequest(endpoint, AsyncEmailVerificationResponse.class); + AsyncEmailVerificationResponse response = api.getRequest(endpoint, AsyncEmailVerificationResponse.class); + + response.postDeserialize(); + + return response; } /** diff --git a/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationBuilder.java b/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationBuilder.java index 49d4deb..8fe8bb9 100644 --- a/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationBuilder.java +++ b/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationBuilder.java @@ -123,6 +123,7 @@ public EmailVerificationList create() throws MailerSendException { String json = gson.toJson(builderBody); builderBody.reset(); + emails.clear(); SingleEmailVerificationListResponse response = api.postRequest(endpoint, json, SingleEmailVerificationListResponse.class); diff --git a/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationList.java b/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationList.java index 10b9420..3a9bad4 100644 --- a/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationList.java +++ b/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationList.java @@ -59,6 +59,9 @@ public class EmailVerificationList { @SerializedName("statistics") public Statistics statistics; + + @SerializedName("emails") + public EmailVerificationListEmails emails; /** *

postDeserialize.

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 MailerSend + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.emailverification; + +import com.google.gson.annotations.SerializedName; +import com.mailersend.sdk.util.ResponseLinks; + +/** + * The paginated {@code emails} object of a list requested with {@code detailed=true}. + * + * @author mailersend + */ +public class EmailVerificationListEmails { + + @SerializedName("data") + public VerificationResult[] results; + + @SerializedName("pagination") + public EmailVerificationListPagination pagination; + + @SerializedName("links") + public ResponseLinks links; +} diff --git a/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationListPagination.java b/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationListPagination.java new file mode 100644 index 0000000..41378d3 --- /dev/null +++ b/src/main/java/com/mailersend/sdk/emailverification/EmailVerificationListPagination.java @@ -0,0 +1,36 @@ +/************************************************* + * MailerSend Java SDK + * https://github.com/mailersend/mailersend-java + * + * @author MailerSend + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.emailverification; + +import com.google.gson.annotations.SerializedName; + +/** + * Pagination of the {@code emails} object of a detailed verification list. + * + * @author mailersend + */ +public class EmailVerificationListPagination { + + @SerializedName("current_page") + public int currentPage; + + @SerializedName("per_page") + public int perPage; + + @SerializedName("total") + public int total; + + @SerializedName("last_page") + public int lastPage; + + @SerializedName("from") + public int from; + + @SerializedName("to") + public int to; +} diff --git a/src/main/java/com/mailersend/sdk/emailverification/Statistics.java b/src/main/java/com/mailersend/sdk/emailverification/Statistics.java index 61120ad..34ddef2 100644 --- a/src/main/java/com/mailersend/sdk/emailverification/Statistics.java +++ b/src/main/java/com/mailersend/sdk/emailverification/Statistics.java @@ -49,5 +49,8 @@ public class Statistics { @SerializedName("failed") public int failed; + + @SerializedName("not_verified") + public int notVerified; } diff --git a/src/main/java/com/mailersend/sdk/inboundroutes/Forward.java b/src/main/java/com/mailersend/sdk/inboundroutes/Forward.java index 7c2ea14..31af184 100644 --- a/src/main/java/com/mailersend/sdk/inboundroutes/Forward.java +++ b/src/main/java/com/mailersend/sdk/inboundroutes/Forward.java @@ -1,5 +1,6 @@ package com.mailersend.sdk.inboundroutes; +import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; /** @@ -10,7 +11,12 @@ */ public class Forward { + /** + * @deprecated The API neither accepts nor returns a forward id, so it is not sent and is always null. + */ + @Expose(serialize = false) @SerializedName("id") + @Deprecated public String id; @SerializedName("type") @@ -19,6 +25,10 @@ public class Forward { @SerializedName("value") public String value; + /** + * Returned by the API for webhook forwards. The API does not accept it, so it is not sent. + */ + @Expose(serialize = false) @SerializedName("secret") public String secret; } diff --git a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoute.java b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoute.java index b28e733..576c89b 100644 --- a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoute.java +++ b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRoute.java @@ -47,6 +47,12 @@ public class InboundRoute { @SerializedName("priority") public int priority; + @SerializedName("catch_type") + public String catchType; + + @SerializedName("match_type") + public String matchType; + @SerializedName("include_attachments") public boolean includeAttachments; diff --git a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRouteBuilder.java b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRouteBuilder.java index 4c3b095..bdddac5 100644 --- a/src/main/java/com/mailersend/sdk/inboundroutes/InboundRouteBuilder.java +++ b/src/main/java/com/mailersend/sdk/inboundroutes/InboundRouteBuilder.java @@ -4,6 +4,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; @@ -33,6 +34,8 @@ public InboundRouteBuilder(MailerSend ref) { /** *

domainId.

* + *

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 JsonDeserializer { + + @Override + public TemplateDomainTotals deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { + + if (!json.isJsonObject()) { + + return null; + } + + return new Gson().fromJson(json, TemplateDomainTotals.class); + } + } } diff --git a/src/main/java/com/mailersend/sdk/templates/TemplateItem.java b/src/main/java/com/mailersend/sdk/templates/TemplateItem.java index 621416f..34c1c4a 100644 --- a/src/main/java/com/mailersend/sdk/templates/TemplateItem.java +++ b/src/main/java/com/mailersend/sdk/templates/TemplateItem.java @@ -54,6 +54,12 @@ public class TemplateItem { @SerializedName("updated_at") private String updatedAtStr; + @SerializedName("categories") + public TemplateCategory[] categories; + + @SerializedName("translations") + public TemplateTranslation[] translations; + /** * Is called to perform any actions after the deserialization of the response @@ -62,6 +68,14 @@ public class TemplateItem { protected void postDeserialize() { parseDates(); + + if (translations != null) { + + for (TemplateTranslation translation : translations) { + + translation.postDeserialize(); + } + } } diff --git a/src/main/java/com/mailersend/sdk/templates/TemplateStats.java b/src/main/java/com/mailersend/sdk/templates/TemplateStats.java index e43fae2..3188acd 100644 --- a/src/main/java/com/mailersend/sdk/templates/TemplateStats.java +++ b/src/main/java/com/mailersend/sdk/templates/TemplateStats.java @@ -40,6 +40,33 @@ public class TemplateStats { @SerializedName("delivered") public int delivered; + @SerializedName("delivery_rate") + public String deliveryRate; + + @SerializedName("opened") + public int opened; + + @SerializedName("open_rate") + public String openRate; + + @SerializedName("clicked") + public int clicked; + + @SerializedName("click_rate") + public String clickRate; + + @SerializedName("unsubscribed") + public int unsubscribed; + + @SerializedName("unsubscribe_rate") + public String unsubscribeRate; + + @SerializedName("complained") + public int complained; + + @SerializedName("complain_rate") + public String complainRate; + public Date lastEmailSentAt; @SerializedName("last_email_sent_at") diff --git a/src/main/java/com/mailersend/sdk/templates/TemplateTranslation.java b/src/main/java/com/mailersend/sdk/templates/TemplateTranslation.java new file mode 100644 index 0000000..e19f3ce --- /dev/null +++ b/src/main/java/com/mailersend/sdk/templates/TemplateTranslation.java @@ -0,0 +1,67 @@ +/************************************************* + * MailerSend Java SDK + * https://github.com/mailersend/mailersend-java + * + * @author MailerSend + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.templates; + +import java.util.Date; + +import com.google.gson.annotations.SerializedName; +import com.mailersend.sdk.util.DateParser; + +/** + *

TemplateTranslation 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 MailerSend + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.tokens; + +import com.google.gson.annotations.SerializedName; + +/** + * Serializable body for token update requests. + */ +class TokenUpdateRequestBody { + + @SerializedName("name") + public String name = null; + + @SerializedName("status") + public String status = null; +} diff --git a/src/main/java/com/mailersend/sdk/tokens/Tokens.java b/src/main/java/com/mailersend/sdk/tokens/Tokens.java index 020b7f4..137d596 100644 --- a/src/main/java/com/mailersend/sdk/tokens/Tokens.java +++ b/src/main/java/com/mailersend/sdk/tokens/Tokens.java @@ -9,10 +9,13 @@ import java.util.ArrayList; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.MailerSendApi; import com.mailersend.sdk.MailerSendResponse; import com.mailersend.sdk.exceptions.MailerSendException; +import com.mailersend.sdk.util.JsonSerializationDeserializationStrategy; /** *

Tokens class.

@@ -176,17 +179,16 @@ public Token updateToken(String tokenId, String name, String status) throws Mail MailerSendApi api = new MailerSendApi(); api.setToken(apiObjectReference.getToken()); - ArrayList fields = new ArrayList(); + TokenUpdateRequestBody body = new TokenUpdateRequestBody(); + body.name = nameBlank ? null : name; + body.status = statusBlank ? null : status; - if (name != null && !name.isBlank()) { - fields.add("\"name\":\"" + name + "\""); - } - - if (status != null && !status.isBlank()) { - fields.add("\"status\":\"" + status + "\""); - } + Gson gson = new GsonBuilder() + .addSerializationExclusionStrategy(new JsonSerializationDeserializationStrategy(false)) + .addDeserializationExclusionStrategy(new JsonSerializationDeserializationStrategy(true)) + .create(); - String json = "{" + String.join(",", fields) + "}"; + String json = gson.toJson(body); TokenResponse response = api.putRequest(endpoint, json, TokenResponse.class); diff --git a/src/main/java/com/mailersend/sdk/users/Invite.java b/src/main/java/com/mailersend/sdk/users/Invite.java index 58406d6..ec0265e 100644 --- a/src/main/java/com/mailersend/sdk/users/Invite.java +++ b/src/main/java/com/mailersend/sdk/users/Invite.java @@ -13,11 +13,12 @@ import java.util.Date; import com.google.gson.annotations.SerializedName; +import com.mailersend.sdk.MailerSendResponse; /** * Represents a pending user invite. */ -public class Invite { +public class Invite extends MailerSendResponse { @SerializedName("id") public String id; diff --git a/src/main/java/com/mailersend/sdk/users/User.java b/src/main/java/com/mailersend/sdk/users/User.java index 731c6ef..7055e1e 100644 --- a/src/main/java/com/mailersend/sdk/users/User.java +++ b/src/main/java/com/mailersend/sdk/users/User.java @@ -13,11 +13,12 @@ import java.util.Date; import com.google.gson.annotations.SerializedName; +import com.mailersend.sdk.MailerSendResponse; /** * Represents a MailerSend account user. */ -public class User { +public class User extends MailerSendResponse { @SerializedName("id") public String id; diff --git a/src/main/java/com/mailersend/sdk/users/UserBuilder.java b/src/main/java/com/mailersend/sdk/users/UserBuilder.java index 66e3bb6..cf065f3 100644 --- a/src/main/java/com/mailersend/sdk/users/UserBuilder.java +++ b/src/main/java/com/mailersend/sdk/users/UserBuilder.java @@ -7,6 +7,8 @@ **************************************************/ package com.mailersend.sdk.users; +import java.util.ArrayList; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.mailersend.sdk.MailerSend; @@ -89,6 +91,10 @@ public UserBuilder role(String role) { */ public UserBuilder addPermission(String permission) { + if (builderBody.permissions == null) { + builderBody.permissions = new ArrayList(); + } + builderBody.permissions.add(permission); return this; @@ -103,6 +109,10 @@ public UserBuilder addPermission(String permission) { */ public UserBuilder addTemplate(String templateId) { + if (builderBody.templates == null) { + builderBody.templates = new ArrayList(); + } + builderBody.templates.add(templateId); return this; @@ -117,6 +127,10 @@ public UserBuilder addTemplate(String templateId) { */ public UserBuilder addDomain(String domainId) { + if (builderBody.domains == null) { + builderBody.domains = new ArrayList(); + } + builderBody.domains.add(domainId); return this; @@ -155,7 +169,7 @@ public Invite inviteUser() throws MailerSendException { throw new MailerSendException("User role cannot be null or empty"); } - if ("Custom User".equals(builderBody.role) && builderBody.permissions.isEmpty()) { + if ("Custom User".equals(builderBody.role) && builderBody.permissions == null) { throw new MailerSendException("Permissions are required for Custom User role"); } @@ -174,11 +188,11 @@ public Invite inviteUser() throws MailerSendException { builderBody.reset(); - InviteResponse response = api.postRequest(endpoint, json, InviteResponse.class); + Invite invite = api.postRequest(endpoint, json, Invite.class); - response.invite.postDeserialize(); + invite.postDeserialize(); - return response.invite; + return invite; } @@ -201,7 +215,7 @@ public User updateUser(String userId) throws MailerSendException { throw new MailerSendException("User role cannot be null or empty"); } - if ("Custom User".equals(builderBody.role) && builderBody.permissions.isEmpty()) { + if ("Custom User".equals(builderBody.role) && builderBody.permissions == null) { throw new MailerSendException("Permissions are required for Custom User role"); } @@ -220,10 +234,10 @@ public User updateUser(String userId) throws MailerSendException { builderBody.reset(); - UserResponse response = api.putRequest(endpoint, json, UserResponse.class); + User user = api.putRequest(endpoint, json, User.class); - response.user.postDeserialize(); + user.postDeserialize(); - return response.user; + return user; } } diff --git a/src/main/java/com/mailersend/sdk/users/UserRequestBody.java b/src/main/java/com/mailersend/sdk/users/UserRequestBody.java index dd948b3..c2df886 100644 --- a/src/main/java/com/mailersend/sdk/users/UserRequestBody.java +++ b/src/main/java/com/mailersend/sdk/users/UserRequestBody.java @@ -23,13 +23,13 @@ class UserRequestBody { public String role = null; @SerializedName("permissions") - public ArrayList permissions = new ArrayList(); + public ArrayList permissions = null; @SerializedName("templates") - public ArrayList templates = new ArrayList(); + public ArrayList templates = null; @SerializedName("domains") - public ArrayList domains = new ArrayList(); + public ArrayList domains = null; @SerializedName("requires_periodic_password_change") public Boolean requiresPeriodicPasswordChange = null; @@ -42,9 +42,9 @@ public void reset() { email = null; role = null; - permissions.clear(); - templates.clear(); - domains.clear(); + permissions = null; + templates = null; + domains = null; requiresPeriodicPasswordChange = null; } } diff --git a/src/main/java/com/mailersend/sdk/util/DateParser.java b/src/main/java/com/mailersend/sdk/util/DateParser.java new file mode 100644 index 0000000..53f6486 --- /dev/null +++ b/src/main/java/com/mailersend/sdk/util/DateParser.java @@ -0,0 +1,65 @@ +/************************************************* + * MailerSend Java SDK + * https://github.com/mailersend/mailersend-java + * + * @author MailerSend + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.util; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.util.Date; + +/** + * Parses the date strings returned by the MailerSend API + * + * @author mailersend + * @version $Id: $Id + */ +public final class DateParser { + + private static final DateTimeFormatter SPACE_SEPARATED_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + private DateParser() { + + // intentionally left empty + } + + + /** + * Converts an API date string to a java.util.Date. Returns null for empty or unparseable values + * + * @param value the date string as returned by the API + * @return the parsed date or null + */ + public static Date parse(String value) { + + if (value == null || value.isBlank()) { + + return null; + } + + try { + + TemporalAccessor ta = DateTimeFormatter.ISO_INSTANT.parse(value); + + return Date.from(Instant.from(ta)); + } catch (Exception e) { + + // the API may also return dates as "2020-06-04 12:00:00", assumed to be UTC + try { + + LocalDateTime localDateTime = LocalDateTime.parse(value, SPACE_SEPARATED_FORMATTER); + + return Date.from(localDateTime.toInstant(ZoneOffset.UTC)); + } catch (Exception ex) { + + return null; + } + } + } +} diff --git a/src/main/java/com/mailersend/sdk/util/EventTypes.java b/src/main/java/com/mailersend/sdk/util/EventTypes.java index 6b090a6..ddca880 100644 --- a/src/main/java/com/mailersend/sdk/util/EventTypes.java +++ b/src/main/java/com/mailersend/sdk/util/EventTypes.java @@ -8,7 +8,12 @@ */ public class EventTypes { - /** Constant PROCESSED="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 + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.whatsapp; + +import com.google.gson.annotations.SerializedName; + +/** + *

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(Map header) { + this.data.namedHeader = header; + this.data.header = null; + this.data.mediaHeader = null; return this; } @@ -51,6 +92,19 @@ public WhatsAppPersonalization setHeader(String[] header) { */ public WhatsAppPersonalization setBody(String[] body) { this.data.body = body; + this.data.namedBody = null; + return this; + } + + /** + *

setNamedBody.

+ * + * @param body body variable values keyed by variable name + * @return a {@link com.mailersend.sdk.whatsapp.WhatsAppPersonalization} object. + */ + public WhatsAppPersonalization setNamedBody(Map body) { + this.data.namedBody = body; + this.data.body = null; return this; } diff --git a/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalizationData.java b/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalizationData.java index e74cc65..85e6352 100644 --- a/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalizationData.java +++ b/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalizationData.java @@ -7,6 +7,9 @@ **************************************************/ package com.mailersend.sdk.whatsapp; +import java.util.Map; + +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; /** @@ -15,6 +18,7 @@ * @author mailersend * @version $Id: $Id */ +@JsonAdapter(WhatsAppPersonalizationDataSerializer.class) public class WhatsAppPersonalizationData { @SerializedName("header") @@ -25,4 +29,19 @@ public class WhatsAppPersonalizationData { @SerializedName("buttons") public String[] buttons; + + /** + * Sent as {@code header} for templates with an image, video or document header. + */ + public transient WhatsAppMediaHeader mediaHeader; + + /** + * Sent as {@code header} for templates with named header variables. + */ + public transient Map namedHeader; + + /** + * Sent as {@code body} for templates with named body variables. + */ + public transient Map namedBody; } diff --git a/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalizationDataSerializer.java b/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalizationDataSerializer.java new file mode 100644 index 0000000..b46ac7e --- /dev/null +++ b/src/main/java/com/mailersend/sdk/whatsapp/WhatsAppPersonalizationDataSerializer.java @@ -0,0 +1,47 @@ +/************************************************* + * MailerSend Java SDK + * https://github.com/mailersend/mailersend-java + * + * @author MailerSend + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.whatsapp; + +import java.lang.reflect.Type; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * Serializes {@code header} and {@code body} as an array or an object, depending on the template. + */ +class WhatsAppPersonalizationDataSerializer implements JsonSerializer { + + @Override + public JsonElement serialize(WhatsAppPersonalizationData data, Type typeOfSrc, JsonSerializationContext context) { + + JsonObject json = new JsonObject(); + + if (data.mediaHeader != null) { + json.add("header", context.serialize(data.mediaHeader)); + } else if (data.namedHeader != null) { + json.add("header", context.serialize(data.namedHeader)); + } else if (data.header != null) { + json.add("header", context.serialize(data.header)); + } + + if (data.namedBody != null) { + json.add("body", context.serialize(data.namedBody)); + } else if (data.body != null) { + json.add("body", context.serialize(data.body)); + } + + if (data.buttons != null) { + json.add("buttons", context.serialize(data.buttons)); + } + + return json; + } +} diff --git a/src/main/java/com/mailersend/sms/webhooks/SmsWebhookBuilderBody.java b/src/main/java/com/mailersend/sms/webhooks/SmsWebhookBuilderBody.java index 3004841..d3f4739 100644 --- a/src/main/java/com/mailersend/sms/webhooks/SmsWebhookBuilderBody.java +++ b/src/main/java/com/mailersend/sms/webhooks/SmsWebhookBuilderBody.java @@ -20,7 +20,7 @@ class SmsWebhookBuilderBody { public String name; @SerializedName("events") - public ArrayList events = new ArrayList(); + public ArrayList events = null; @SerializedName("enabled") public Boolean enabled = null; diff --git a/src/main/java/com/mailersend/sms/webhooks/SmsWebhooksBuilder.java b/src/main/java/com/mailersend/sms/webhooks/SmsWebhooksBuilder.java index 9905c36..c87c14b 100644 --- a/src/main/java/com/mailersend/sms/webhooks/SmsWebhooksBuilder.java +++ b/src/main/java/com/mailersend/sms/webhooks/SmsWebhooksBuilder.java @@ -7,6 +7,8 @@ **************************************************/ package com.mailersend.sms.webhooks; +import java.util.ArrayList; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.mailersend.sdk.MailerSend; @@ -75,6 +77,11 @@ public SmsWebhooksBuilder name(String name) { */ public SmsWebhooksBuilder addEvent(String event) { + if (builderBody.events == null) { + + builderBody.events = new ArrayList(); + } + builderBody.events.add(event); return this; @@ -102,7 +109,7 @@ public SmsWebhooksBuilder enabled(boolean enabled) { */ public SmsWebhooksBuilder clearEvents() { - builderBody.events.clear(); + builderBody.events = null; return this; } diff --git a/src/test/java/com/mailersend/sdk/tests/ActivitiesTest.java b/src/test/java/com/mailersend/sdk/tests/ActivitiesTest.java index 5007b0e..8ee7141 100644 --- a/src/test/java/com/mailersend/sdk/tests/ActivitiesTest.java +++ b/src/test/java/com/mailersend/sdk/tests/ActivitiesTest.java @@ -7,11 +7,14 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; +import java.net.URI; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -310,4 +313,69 @@ public void testActivityEmailConversion() { fail(); } } + + + /** + * Tests that the one argument overload requests the first page of the last 7 days + */ + @Test + public void testGetActivitiesWithoutDatesUsesTheLast7Days() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, "{\"data\":[],\"links\":{},\"meta\":{\"current_page\":1}}"); + + long before = System.currentTimeMillis() / 1000; + ms.activities().getActivities(TestHelper.domainId); + long after = System.currentTimeMillis() / 1000; + + URI uri = client.lastRequest().uri(); + Map query = queryParams(uri); + + assertEquals("GET", client.lastRequest().method()); + assertEquals("/v1/activity/" + TestHelper.domainId, uri.getPath()); + assertEquals("1", query.get("page")); + assertEquals("25", query.get("limit")); + + long dateTo = Long.parseLong(query.get("date_to")); + long dateFrom = Long.parseLong(query.get("date_from")); + + assertTrue(dateTo >= before && dateTo <= after); + assertEquals(7 * 24 * 60 * 60, dateTo - dateFrom); + } + + + /** + * Tests that an activity email without tags converts to an email without tags + */ + @Test + public void testActivityEmailConversionWithoutTags() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(200, "{\"data\":{\"id\":\"65a4f0b5b1ffd57ddb0b5eec\",\"type\":\"sent\",\"email\":{\"id\":\"65a4f0a86f6d7fb43c0c498b\",\"from\":\"sender@test-sdk.com\",\"subject\":\"Subject\",\"tags\":null,\"recipient\":{\"email\":\"java@mailersend.com\"}}}}"); + + Activity activity = ms.activities().getSingleActivity("65a4f0b5b1ffd57ddb0b5eec"); + + Email email = activity.email.toEmail(); + + assertEquals("Subject", email.subject); + assertTrue(email.tags.isEmpty()); + } + + + private static Map queryParams(URI uri) { + + Map params = new HashMap(); + + for (String pair : uri.getRawQuery().split("&")) { + + String[] parts = pair.split("=", 2); + params.put(parts[0], parts.length > 1 ? parts[1] : ""); + } + + return params; + } } diff --git a/src/test/java/com/mailersend/sdk/tests/AnalyticsTest.java b/src/test/java/com/mailersend/sdk/tests/AnalyticsTest.java index 56f7956..b9f04de 100644 --- a/src/test/java/com/mailersend/sdk/tests/AnalyticsTest.java +++ b/src/test/java/com/mailersend/sdk/tests/AnalyticsTest.java @@ -1,5 +1,6 @@ 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; @@ -24,6 +25,19 @@ public class AnalyticsTest { + private static final String API_URL = "https://api.mailersend.com/v1"; + + private static final Date DATE_FROM = Date.from(Instant.parse("2024-01-01T00:00:00Z")); + + private static final Date DATE_TO = Date.from(Instant.parse("2024-01-31T00:00:00Z")); + + private static final String DATES_QUERY = "date_from=1704067200&date_to=1706659200"; + + private static final String BY_DATE_RESPONSE = "{\"data\":{\"date_from\":\"1704067200\",\"date_to\":\"1706745599\",\"group_by\":\"days\",\"stats\":[{\"date\":\"1704067200\",\"suppressed\":3}]}}"; + + private static final String OPENS_RESPONSE = "{\"data\":{\"date_from\":1704067200,\"date_to\":1706745599,\"stats\":[{\"name\":\"GB\",\"count\":2}]}}"; + + @BeforeEach public void setupEach(TestInfo info) throws IOException @@ -229,4 +243,85 @@ public void testAnalyticsOpensByUaType() { fail(); } } + + + /** + * Tests that a plain group by value is sent as the group_by query parameter + */ + @Test + public void testGetByDateWithGroupBy() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, BY_DATE_RESPONSE); + + ms.analytics() + .dateFrom(DATE_FROM) + .dateTo(DATE_TO) + .getByDate("weeks", new String[] {EventTypes.DELIVERED}); + + assertEquals(API_URL + "/analytics/date?group_by=weeks&event[]=delivered&" + DATES_QUERY, client.lastUrl()); + } + + + /** + * Tests that a group_by=value fragment, which callers had to pass before the overload took a plain value, is still sent once + */ + @Test + public void testGetByDateWithGroupByFragment() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, BY_DATE_RESPONSE); + + ms.analytics() + .dateFrom(DATE_FROM) + .dateTo(DATE_TO) + .getByDate("group_by=weeks", new String[] {EventTypes.DELIVERED}); + + assertEquals(API_URL + "/analytics/date?group_by=weeks&event[]=delivered&" + DATES_QUERY, client.lastUrl()); + } + + + /** + * Tests that the suppressed counter is read from the analytics by date response + */ + @Test + public void testGetByDateReadsSuppressedCount() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient.install(200, BY_DATE_RESPONSE); + + AnalyticsByDateList list = ms.analytics() + .dateFrom(DATE_FROM) + .dateTo(DATE_TO) + .getByDate(new String[] {EventTypes.SUPPRESSED}); + + assertEquals(3, list.statistics[0].suppressed); + } + + + /** + * Tests that the tags are url encoded + */ + @Test + public void testTagsAreUrlEncoded() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, OPENS_RESPONSE); + + ms.analytics() + .dateFrom(DATE_FROM) + .dateTo(DATE_TO) + .tags(new String[] {"news letter", "a&b#1"}) + .getOpensByCountry(); + + assertEquals(API_URL + "/analytics/country?" + DATES_QUERY + "&tags[]=news+letter&tags[]=a%26b%231", client.lastUrl()); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/BlocklistMonitoringTest.java b/src/test/java/com/mailersend/sdk/tests/BlocklistMonitoringTest.java index 1bae7e6..216b7d8 100644 --- a/src/test/java/com/mailersend/sdk/tests/BlocklistMonitoringTest.java +++ b/src/test/java/com/mailersend/sdk/tests/BlocklistMonitoringTest.java @@ -7,6 +7,8 @@ **************************************************/ package com.mailersend.sdk.tests; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -75,6 +77,8 @@ public void getMonitorTest() { BlocklistMonitor monitor = ms.blocklistMonitoring().getMonitor(TestHelper.blocklistMonitorId); assertNotNull(monitor); + assertEquals(1, monitor.blocklistedCount); + assertTrue(monitor.blocklisted); System.out.println(monitor.id); System.out.println(monitor.address); @@ -99,6 +103,8 @@ public void createMonitorTest() { .createMonitor(TestHelper.blocklistMonitorAddress); assertNotNull(monitor); + assertEquals(0, monitor.blocklistedCount); + assertFalse(monitor.blocklisted); System.out.println(monitor.id); System.out.println(monitor.address); diff --git a/src/test/java/com/mailersend/sdk/tests/CapturingHttpClient.java b/src/test/java/com/mailersend/sdk/tests/CapturingHttpClient.java new file mode 100644 index 0000000..1231443 --- /dev/null +++ b/src/test/java/com/mailersend/sdk/tests/CapturingHttpClient.java @@ -0,0 +1,154 @@ +/************************************************* + * MailerSend Java SDK + * https://github.com/mailersend/mailersend-java + * + * @author MailerSend + * https://mailersend.com + **************************************************/ +package com.mailersend.sdk.tests; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandler; +import java.net.http.HttpResponse.BodySubscriber; +import java.net.http.HttpResponse.BodySubscribers; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Flow; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mailersend.sdk.util.MailerSendHttpClientFactory; +import com.mailersend.sdk.vcr.HttpClientVcr; +import com.mailersend.sdk.vcr.HttpClientVcrResponse; + +/** + * Records the requests the SDK makes and answers each of them with a canned response, for the + * tests that assert the request method, url and body. A replayed fixture only proves that the + * request hash matched, so it cannot show which fields were serialized + */ +class CapturingHttpClient extends HttpClientVcr { + + private final List requests = new ArrayList(); + + private final List bodies = new ArrayList(); + + private final int statusCode; + + private final String responseBody; + + private final Map> responseHeaders; + + + private CapturingHttpClient(int statusCode, String responseBody, Map> responseHeaders) { + + this.statusCode = statusCode; + this.responseBody = responseBody; + this.responseHeaders = responseHeaders; + } + + + /** + * Replaces the recording client with a capturing one that answers every request with the given response + */ + static CapturingHttpClient install(int statusCode, String responseBody) { + + return install(statusCode, responseBody, Map.of("content-type", List.of("application/json"))); + } + + + /** + * Replaces the recording client with a capturing one that answers every request with the given response + */ + static CapturingHttpClient install(int statusCode, String responseBody, Map> responseHeaders) { + + CapturingHttpClient client = new CapturingHttpClient(statusCode, responseBody, responseHeaders); + + MailerSendHttpClientFactory.getInstance().setClient(client); + + return client; + } + + + /** + * Parses a JSON string written with single quotes, so that the expected bodies stay readable + */ + static JsonElement json(String singleQuotedJson) { + + return JsonParser.parseString(singleQuotedJson.replace('\'', '"')); + } + + + HttpRequest lastRequest() { + + assertFalse(requests.isEmpty(), "no request was made"); + + return requests.get(requests.size() - 1); + } + + + String lastUrl() { + + return lastRequest().uri().toString(); + } + + + JsonElement lastBody() { + + assertFalse(bodies.isEmpty(), "no request was made"); + + return JsonParser.parseString(bodies.get(bodies.size() - 1)); + } + + + @SuppressWarnings("unchecked") + @Override + public HttpResponse send(HttpRequest request, BodyHandler responseBodyHandler) { + + requests.add(request); + bodies.add(request.bodyPublisher().map(CapturingHttpClient::readBody).orElse(null)); + + HttpClientVcrResponse response = new HttpClientVcrResponse(); + response.body = responseBody; + response.headers = responseHeaders; + response.statusCode = statusCode; + + return (HttpResponse) response; + } + + + private static String readBody(HttpRequest.BodyPublisher publisher) { + + BodySubscriber subscriber = BodySubscribers.ofString(StandardCharsets.UTF_8); + + publisher.subscribe(new Flow.Subscriber() { + + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscriber.onSubscribe(subscription); + } + + @Override + public void onNext(ByteBuffer item) { + subscriber.onNext(List.of(item)); + } + + @Override + public void onError(Throwable throwable) { + subscriber.onError(throwable); + } + + @Override + public void onComplete() { + subscriber.onComplete(); + } + }); + + return subscriber.getBody().toCompletableFuture().join(); + } +} diff --git a/src/test/java/com/mailersend/sdk/tests/DmarcMonitoringTest.java b/src/test/java/com/mailersend/sdk/tests/DmarcMonitoringTest.java index 3ecb984..503fb9f 100644 --- a/src/test/java/com/mailersend/sdk/tests/DmarcMonitoringTest.java +++ b/src/test/java/com/mailersend/sdk/tests/DmarcMonitoringTest.java @@ -317,4 +317,33 @@ public void getReportSourcesWithStatusTest() { fail(); } } + + /** + * Tests that every report filter is sent, with the Y-m-d H:i:s dates and the search url encoded + */ + @Test + public void getAggregatedReportWithFiltersTest() { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + try { + + DmarcAggregatedReportList list = ms.dmarcMonitoring() + .page(2) + .limit(10) + .dateFrom("2024-01-01 00:00:00") + .dateTo("2024-01-31 23:59:59") + .search("example org") + .category("category-id-test") + .reportSource("google.com") + .getAggregatedReport(TestHelper.dmarcMonitorId); + + assertEquals(TestHelper.dmarcMonitorIp, list.reports[0].ipAddress); + + } catch (MailerSendException e) { + e.printStackTrace(); + fail(); + } + } } diff --git a/src/test/java/com/mailersend/sdk/tests/DomainsTest.java b/src/test/java/com/mailersend/sdk/tests/DomainsTest.java index e3cb64e..d65d2db 100644 --- a/src/test/java/com/mailersend/sdk/tests/DomainsTest.java +++ b/src/test/java/com/mailersend/sdk/tests/DomainsTest.java @@ -5,6 +5,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; @@ -277,6 +279,72 @@ public void testUpdateDomainSettingsAllFields() { } + /** + * Tests that the verified filter is sent as 1 or 0, the only forms of a boolean query value the API accepts + */ + @Test + public void testGetDomainsWithVerifiedFilter() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, "{\"data\":[],\"links\":{},\"meta\":{\"current_page\":1}}"); + + ms.domains().onlyVerified().getDomains(); + + assertEquals("https://api.mailersend.com/v1/domains?page=1&limit=25&verified=1", client.lastUrl()); + + ms.domains().onlyUnverified().getDomains(); + + assertEquals("https://api.mailersend.com/v1/domains?page=1&limit=25&verified=0", client.lastUrl()); + } + + + /** + * Tests that the dates of the domains list and of a single domain are parsed + */ + @Test + public void testDomainDatesAreParsed() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + String domain = "{\"id\":\"jpzkmgq7e5vl059v\",\"name\":\"test-sdk.com\",\"created_at\":\"2022-04-20T11:36:15.000000Z\",\"updated_at\":\"2022-06-15T11:32:41.000000Z\"}"; + + CapturingHttpClient.install(200, "{\"data\":[" + domain + "],\"links\":{},\"meta\":{\"current_page\":1}}"); + + DomainsList list = ms.domains().getDomains(); + + assertEquals(Date.from(Instant.parse("2022-04-20T11:36:15Z")), list.domains[0].createdAt); + assertEquals(Date.from(Instant.parse("2022-06-15T11:32:41Z")), list.domains[0].updatedAt); + + CapturingHttpClient.install(200, "{\"data\":" + domain + "}"); + + Domain single = ms.domains().getDomain(TestHelper.domainId); + + assertEquals(Date.from(Instant.parse("2022-04-20T11:36:15Z")), single.createdAt); + } + + + /** + * Tests that customTrackingEnabled sets custom_tracking_enabled + */ + @Test + public void testCustomTrackingEnabled() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, "{\"data\":{\"id\":\"jpzkmgq7e5vl059v\",\"name\":\"test-sdk.com\"}}"); + + ms.domains().updateDomainSettingsBuilder() + .customTrackingEnabled(true) + .updateDomain(TestHelper.domainId); + + assertEquals(CapturingHttpClient.json("{'custom_tracking_enabled':true}"), client.lastBody()); + } + + /** * Simple helper method to print the DomainDnsAttribute properties * @param attribute diff --git a/src/test/java/com/mailersend/sdk/tests/EmailConfigurationTest.java b/src/test/java/com/mailersend/sdk/tests/EmailConfigurationTest.java index 9bd8c15..6264304 100644 --- a/src/test/java/com/mailersend/sdk/tests/EmailConfigurationTest.java +++ b/src/test/java/com/mailersend/sdk/tests/EmailConfigurationTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import com.mailersend.sdk.*; @@ -274,10 +275,10 @@ public void testRcptToRecipientsWithObjects() { /** - * Tests that rcptTo field serializes correctly as "rcpt_to" in JSON + * Tests that the RCPT TO recipients are not serialized, the email API does not accept them */ @Test - public void testRcptToSerialization() { + public void testRcptToIsNotSerialized() { Email email = TestHelper.createBasicEmail(true); @@ -285,8 +286,8 @@ public void testRcptToSerialization() { String json = email.serializeForSending(); - assertTrue(json.contains("\"rcpt_to\"")); - assertTrue(json.contains("rcpt@client.com")); + assertFalse(json.contains("\"rcpt_to\"")); + assertFalse(json.contains("rcpt@client.com")); } diff --git a/src/test/java/com/mailersend/sdk/tests/EmailSendTest.java b/src/test/java/com/mailersend/sdk/tests/EmailSendTest.java index b5a6ef1..c9c21f8 100644 --- a/src/test/java/com/mailersend/sdk/tests/EmailSendTest.java +++ b/src/test/java/com/mailersend/sdk/tests/EmailSendTest.java @@ -13,6 +13,7 @@ import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; +import java.util.Map; import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; @@ -23,14 +24,19 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import com.google.gson.JsonArray; import com.mailersend.sdk.MailerSend; import com.mailersend.sdk.Recipient; +import com.mailersend.sdk.emails.BulkSendStatus; import com.mailersend.sdk.emails.Email; import com.mailersend.sdk.exceptions.MailerSendException; import com.mailersend.sdk.vcr.VcrRecorder; +import static com.mailersend.sdk.tests.CapturingHttpClient.json; import static com.mailersend.sdk.util.EventTypes.QUEUED; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -378,4 +384,94 @@ public void scheduleEmailTest() throws MailerSendException { assertEquals(202, ms.emails().send(email).responseStatusCode); } + + + /** + * Test bulk send status keeps each message id at the position of its email in the request + * when the API keys the ids because some emails were not sent + */ + @Test + public void testBulkSendStatusKeepsThePositionOfKeyedMessageIds() throws MailerSendException { + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + BulkSendStatus status = ms.emails().bulkSendStatus("keyed-bulk-send-id"); + + assertArrayEquals(new String[] { null, "61487a14608b1d0b4d506633", null, "61487a14608b1d0b4d506644" }, status.messagesId); + } + + + /** + * Test bulk send status of an unknown bulk send fails with the API's 404 + */ + @Test + public void testBulkSendStatusWithAnUnknownIdFailsWith404() { + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + MailerSendException e = assertThrows(MailerSendException.class, () -> { + ms.emails().bulkSendStatus("unknown-bulk-send-id"); + }); + assertEquals(404, e.code); + assertEquals("Resource not found.", e.getMessage()); + } + + + /** + * Tests that send() leaves the lists that were never filled out of the request. The API + * rejects an empty references list on the Sandbox plan + */ + @Test + public void testSendLeavesOutEmptyLists() throws MailerSendException { + + Email email = TestHelper.createBasicEmail(true); + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(202, "", Map.of()); + + ms.emails().send(email); + + assertEquals(json("{" + + "'from':{'name':'From Name','email':'sender@test-sdk.com'}," + + "'to':[{'name':'To Name','email':'java@mailersend.com'}]," + + "'subject':'Subject'," + + "'text':'This is the text content'," + + "'html':'

This 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 Stream addedEvents() { + return Stream.of( + WebhookEvents.ACTIVITY_SUPPRESSED, + WebhookEvents.INBOUND_MESSAGE_REJECTED, + WebhookEvents.EMAIL_LIST_CREATED + ); + } + + + /** + * Tests that updateWebhook() puts the documented fields to the webhook's endpoint, without + * the editable flag and the domain id that the update endpoint does not accept + */ + @Test + public void testUpdateWebhookSendsTheDocumentedFields() throws MailerSendException { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + CapturingHttpClient client = CapturingHttpClient.install(200, WEBHOOK_RESPONSE); + + ms.webhooks().builder() + .url("https://www.example.com/webhook") + .name("Webhook") + .addEvent(WebhookEvents.ACTIVITY_SENT) + .enabled(false) + .editable(true) + .version(2) + .updateWebhook(WEBHOOK_ID); + + assertEquals("PUT", client.lastRequest().method()); + assertEquals(WEBHOOKS_URL + "/" + WEBHOOK_ID, client.lastUrl()); + assertEquals(json("{" + + "'url':'https://www.example.com/webhook'," + + "'name':'Webhook'," + + "'events':['activity.sent']," + + "'enabled':false," + + "'version':2" + + "}"), client.lastBody()); + } + + + /** + * Tests that updateWebhook() throws when the webhook id is empty + */ + @Test + public void testUpdateWebhookWithEmptyId() { + + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + MailerSendException e = assertThrows(MailerSendException.class, () -> { + ms.webhooks().builder().updateWebhook(""); + }); + + assertEquals("Webhook ID cannot be empty", e.getMessage()); + } } diff --git a/src/test/java/com/mailersend/sdk/tests/WhatsAppTests.java b/src/test/java/com/mailersend/sdk/tests/WhatsAppTests.java index 4fa4f51..a4a8d1a 100644 --- a/src/test/java/com/mailersend/sdk/tests/WhatsAppTests.java +++ b/src/test/java/com/mailersend/sdk/tests/WhatsAppTests.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -95,4 +97,61 @@ public void TestSendWhatsAppWithoutMessageIdHeader() { fail(); } } + + + @Test + public void TestSendWhatsAppWithMediaHeader() { + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + try { + WhatsAppPersonalization p1 = new WhatsAppPersonalization("19191234567") + .setMediaHeader("https://www.example.com/invoices/12345.pdf", "invoice-12345.pdf") + .setBody(new String[]{"John"}); + + String messageId = ms.whatsapp().builder() + .from("12345678901") + .addRecipient("19191234567") + .templateId("template_id_123") + .addPersonalization(p1) + .send(); + + assertEquals("67f91c3d69f79df391e9d795", messageId); + + } catch (MailerSendException e) { + fail(); + } + } + + + @Test + public void TestSendWhatsAppWithNamedVariables() { + MailerSend ms = new MailerSend(); + ms.setToken(TestHelper.validToken); + + try { + Map header = new LinkedHashMap(); + header.put("order_number", "12345"); + + Map body = new LinkedHashMap(); + body.put("first_name", "John"); + body.put("order_date", "December 31, 2026"); + + WhatsAppPersonalization p1 = new WhatsAppPersonalization("19191234567") + .setNamedHeader(header) + .setNamedBody(body); + + String messageId = ms.whatsapp().builder() + .from("12345678901") + .addRecipient("19191234567") + .templateId("template_id_123") + .addPersonalization(p1) + .send(); + + assertEquals("67f91c3d69f79df391e9d796", messageId); + + } catch (MailerSendException e) { + fail(); + } + } } diff --git a/src/test/resources/fixtures/BlocklistMonitoringTest_getMonitorTest().json b/src/test/resources/fixtures/BlocklistMonitoringTest_getMonitorTest().json index 480c3da..396f280 100644 --- a/src/test/resources/fixtures/BlocklistMonitoringTest_getMonitorTest().json +++ b/src/test/resources/fixtures/BlocklistMonitoringTest_getMonitorTest().json @@ -1 +1 @@ -{"0dfc5f4fc9a90e3d2e740f0b779576e7ea09c6f0":{"body":"{\"data\":{\"id\":\"blocklist-monitor-id-test\",\"name\":\"test-domain.com\",\"address\":\"test-domain.com\",\"type\":\"domain\",\"in_progress\":false,\"notify\":true,\"blocklisted\":false,\"last_checks_count\":5,\"last_check\":\"2023-06-01T10:00:00.000000Z\",\"next_check\":\"2023-06-02T10:00:00.000000Z\",\"has_children\":false,\"children_count\":0,\"forwards\":[],\"hits\":[],\"created_at\":\"2023-05-01T10:00:00.000000Z\",\"updated_at\":\"2023-06-01T10:00:00.000000Z\"}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} +{"0dfc5f4fc9a90e3d2e740f0b779576e7ea09c6f0":{"body":"{\"data\":{\"id\":\"blocklist-monitor-id-test\",\"name\":\"test-domain.com\",\"address\":\"test-domain.com\",\"type\":\"domain\",\"in_progress\":false,\"notify\":true,\"blocklisted\":1,\"last_checks_count\":5,\"last_check\":\"2023-06-01T10:00:00.000000Z\",\"next_check\":\"2023-06-02T10:00:00.000000Z\",\"has_children\":false,\"children_count\":0,\"forwards\":[],\"hits\":[],\"created_at\":\"2023-05-01T10:00:00.000000Z\",\"updated_at\":\"2023-06-01T10:00:00.000000Z\"}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} diff --git a/src/test/resources/fixtures/DmarcMonitoringTest_GetIpReportTest().json b/src/test/resources/fixtures/DmarcMonitoringTest_GetIpReportTest().json index 078ee93..bab231f 100644 --- a/src/test/resources/fixtures/DmarcMonitoringTest_GetIpReportTest().json +++ b/src/test/resources/fixtures/DmarcMonitoringTest_GetIpReportTest().json @@ -1 +1 @@ -{"12fd06d41d73e7ed6f4ea72ae0536b37219ae8e0":{"body":"{\"data\":[{\"id\":\"ip-report-id-1\",\"ip_address\":\"1.2.3.4\",\"ip_domain\":\"mail.example.com\",\"total_volume\":150,\"passed_dmarc\":88.5,\"passed_spf\":90.0,\"passed_dkim\":92.5,\"aligned_spf\":90.0,\"aligned_dkim\":92.5,\"applied_policy\":\"none\",\"override_reason\":null,\"country\":\"US\",\"report_source\":\"google.com\",\"spf_auth\":[{\"domain\":\"test-sdk.com\",\"result\":\"pass\"}],\"dkim_auth\":[{\"domain\":\"test-sdk.com\",\"selector\":\"mailersend\",\"result\":\"pass\"}]}],\"links\":{\"first\":\"https:\\/\\/api.mailersend.com\\/v1\\/dmarc-monitoring\\/dmarc-monitor-id-test\\/report\\/1.2.3.4?page=1\",\"last\":\"https:\\/\\/api.mailersend.com\\/v1\\/dmarc-monitoring\\/dmarc-monitor-id-test\\/report\\/1.2.3.4?page=1\",\"prev\":null,\"next\":null},\"meta\":{\"current_page\":1,\"from\":1,\"last_page\":1,\"links\":[],\"path\":\"https:\\/\\/api.mailersend.com\\/v1\\/dmarc-monitoring\\/dmarc-monitor-id-test\\/report\\/1.2.3.4\",\"per_page\":25,\"to\":1,\"total\":1}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} +{"12fd06d41d73e7ed6f4ea72ae0536b37219ae8e0":{"body":"{\"data\":[{\"id\":\"ip-report-id-1\",\"ip_address\":\"1.2.3.4\",\"ip_domain\":\"mail.example.com\",\"total_volume\":150,\"passed_dmarc\":88.5,\"passed_spf\":90.0,\"passed_dkim\":92.5,\"aligned_spf\":false,\"aligned_dkim\":true,\"applied_policy\":\"none\",\"override_reason\":null,\"country\":\"US\",\"report_source\":\"google.com\",\"spf_auth\":[{\"domain\":\"test-sdk.com\",\"result\":\"pass\"}],\"dkim_auth\":[{\"domain\":\"test-sdk.com\",\"selector\":\"mailersend\",\"result\":\"pass\"}]}],\"links\":{\"first\":\"https:\\/\\/api.mailersend.com\\/v1\\/dmarc-monitoring\\/dmarc-monitor-id-test\\/report\\/1.2.3.4?page=1\",\"last\":\"https:\\/\\/api.mailersend.com\\/v1\\/dmarc-monitoring\\/dmarc-monitor-id-test\\/report\\/1.2.3.4?page=1\",\"prev\":null,\"next\":null},\"meta\":{\"current_page\":1,\"from\":1,\"last_page\":1,\"links\":[],\"path\":\"https:\\/\\/api.mailersend.com\\/v1\\/dmarc-monitoring\\/dmarc-monitor-id-test\\/report\\/1.2.3.4\",\"per_page\":25,\"to\":1,\"total\":1}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} diff --git a/src/test/resources/fixtures/DmarcMonitoringTest_getAggregatedReportWithFiltersTest().json b/src/test/resources/fixtures/DmarcMonitoringTest_getAggregatedReportWithFiltersTest().json new file mode 100644 index 0000000..7920a2b --- /dev/null +++ b/src/test/resources/fixtures/DmarcMonitoringTest_getAggregatedReportWithFiltersTest().json @@ -0,0 +1 @@ +{"ceaa96b5afd5f584a03d6333a58703ab5256187e":{"body":"{\"data\":[{\"ip_address\":\"1.2.3.4\",\"total_volume\":2,\"passed_dmarc\":\"100%\",\"passed_spf\":\"0%\",\"passed_dkim\":\"100%\",\"domain\":\"test-sdk.com\",\"country\":\"United States\",\"country_code\":\"US\",\"city\":null,\"is_favorite\":true},{\"ip_address\":\"5.6.7.8\",\"total_volume\":2,\"passed_dmarc\":\"100%\",\"passed_spf\":\"100%\",\"passed_dkim\":\"100%\",\"domain\":\"test-sdk.com\",\"country\":\"United States\",\"country_code\":\"US\",\"city\":null}],\"links\":{\"first\":\"https://api.mailersend.com/v1/dmarc-monitoring/dmarc-monitor-id-test/report?page=1\",\"last\":\"https://api.mailersend.com/v1/dmarc-monitoring/dmarc-monitor-id-test/report?page=1\",\"prev\":null,\"next\":null},\"meta\":{\"current_page\":1,\"from\":1,\"last_page\":1,\"path\":\"https://api.mailersend.com/v1/dmarc-monitoring/dmarc-monitor-id-test/report\",\"per_page\":25,\"to\":2,\"total\":2}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_TestInvalidTokenFailsWith401().json b/src/test/resources/fixtures/EmailSendTest_TestInvalidTokenFailsWith401().json index 2769780..f80a4af 100644 --- a/src/test/resources/fixtures/EmailSendTest_TestInvalidTokenFailsWith401().json +++ b/src/test/resources/fixtures/EmailSendTest_TestInvalidTokenFailsWith401().json @@ -1 +1 @@ -{"10a3065757d7356252bd3438f1f5cb8df6d0fea5":{"body":"{\"message\":\"Unauthenticated.\"}","headers":{":status":["401"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["746fdaadbade38d6-ATH"],"content-type":["application/json"],"date":["Wed, 07 Sep 2022 13:40:05 GMT"],"server":["cloudflare"],"strict-transport-security":["max-age\u003d15724800; includeSubDomains"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["119"]},"statusCode":401},"1a5ab7aa0d32d5b2ef7c1db6499d5231378569b8":{"body":"{\"message\":\"Unauthenticated.\"}","headers":{":status":["401"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["8ac5ca348a31eee8-ATH"],"content-type":["application/json"],"date":["Thu, 01 Aug 2024 12:21:46 GMT"],"server":["cloudflare"],"strict-transport-security":["max-age\u003d31536000; includeSubDomains"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["119"]},"statusCode":401},"849fe8da6a9d524084324dc64302994316abcb34":{"body":"{\"message\":\"Unauthenticated.\"}","headers":{":status":["401"],"alt-svc":["h3\u003d\":443\"; ma\u003d86400"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["9f6782e7fa1c5b1a-LIS"],"content-type":["application/json"],"date":["Mon, 04 May 2026 12:29:30 GMT"],"server":["cloudflare"],"x-envoy-upstream-service-time":["27"]},"statusCode":401}} \ No newline at end of file +{"10a3065757d7356252bd3438f1f5cb8df6d0fea5":{"body":"{\"message\":\"Unauthenticated.\"}","headers":{":status":["401"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["746fdaadbade38d6-ATH"],"content-type":["application/json"],"date":["Wed, 07 Sep 2022 13:40:05 GMT"],"server":["cloudflare"],"strict-transport-security":["max-age\u003d15724800; includeSubDomains"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["119"]},"statusCode":401},"1a5ab7aa0d32d5b2ef7c1db6499d5231378569b8":{"body":"{\"message\":\"Unauthenticated.\"}","headers":{":status":["401"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["8ac5ca348a31eee8-ATH"],"content-type":["application/json"],"date":["Thu, 01 Aug 2024 12:21:46 GMT"],"server":["cloudflare"],"strict-transport-security":["max-age\u003d31536000; includeSubDomains"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["119"]},"statusCode":401},"dca647c2378f1039f605d4714fff29605e9ac251":{"body":"{\"message\":\"Unauthenticated.\"}","headers":{":status":["401"],"alt-svc":["h3\u003d\":443\"; ma\u003d86400"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["9f6782e7fa1c5b1a-LIS"],"content-type":["application/json"],"date":["Mon, 04 May 2026 12:29:30 GMT"],"server":["cloudflare"],"x-envoy-upstream-service-time":["27"]},"statusCode":401}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_missing body.json b/src/test/resources/fixtures/EmailSendTest_missing body.json index 8da57ab..2d17f62 100644 --- a/src/test/resources/fixtures/EmailSendTest_missing body.json +++ b/src/test/resources/fixtures/EmailSendTest_missing body.json @@ -1 +1 @@ -{"8346b720b117bb750c79c5cccee220790ef994f2":{"body":"{\"message\":\"The given data was invalid.\",\"errors\":{\"html\":[\"The html field is required when text is not present.\"],\"text\":[\"The text field is required when html is not present.\"]}}","headers":{":status":["422"],"content-type":["application/json"]},"statusCode":422}} \ No newline at end of file +{"b91060b93f6a4253c7f106839a2999e268e27905":{"body":"{\"message\":\"The given data was invalid.\",\"errors\":{\"html\":[\"The html field is required when text is not present.\"],\"text\":[\"The text field is required when html is not present.\"]}}","headers":{":status":["422"],"content-type":["application/json"]},"statusCode":422}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_missing from.json b/src/test/resources/fixtures/EmailSendTest_missing from.json index 0fea322..c025db6 100644 --- a/src/test/resources/fixtures/EmailSendTest_missing from.json +++ b/src/test/resources/fixtures/EmailSendTest_missing from.json @@ -1 +1 @@ -{"0f7201b42e59b5a6e50c7a9f05fb162d214ef086":{"body":"{\"message\":\"The given data was invalid.\",\"errors\":{\"from\":[\"The from field is required.\"]}}","headers":{":status":["422"],"content-type":["application/json"]},"statusCode":422}} \ No newline at end of file +{"810eb1e3b59e850127ff1fb9ce7585f7cfae20f4":{"body":"{\"message\":\"The given data was invalid.\",\"errors\":{\"from\":[\"The from field is required.\"]}}","headers":{":status":["422"],"content-type":["application/json"]},"statusCode":422}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_missing subject.json b/src/test/resources/fixtures/EmailSendTest_missing subject.json index 7f9dc78..af95626 100644 --- a/src/test/resources/fixtures/EmailSendTest_missing subject.json +++ b/src/test/resources/fixtures/EmailSendTest_missing subject.json @@ -1 +1 @@ -{"3dc04f05f61d12eab182502093173ab03f6e4b79":{"body":"{\"message\":\"The given data was invalid.\",\"errors\":{\"subject\":[\"The subject field is required.\"]}}","headers":{":status":["422"],"content-type":["application/json"]},"statusCode":422}} \ No newline at end of file +{"98dc33a333bd2feddb9590eaacaf332403c81d22":{"body":"{\"message\":\"The given data was invalid.\",\"errors\":{\"subject\":[\"The subject field is required.\"]}}","headers":{":status":["422"],"content-type":["application/json"]},"statusCode":422}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_missing to.json b/src/test/resources/fixtures/EmailSendTest_missing to.json index cc6d384..2804911 100644 --- a/src/test/resources/fixtures/EmailSendTest_missing to.json +++ b/src/test/resources/fixtures/EmailSendTest_missing to.json @@ -1 +1 @@ -{"acb896bc5d015870fc7fb187ee64412887a033b0":{"body":"{\"message\":\"The given data was invalid.\",\"errors\":{\"to\":[\"The to field is required.\"]}}","headers":{":status":["422"],"content-type":["application/json"]},"statusCode":422}} \ No newline at end of file +{"7d4ae25449700ecca0e9109c2e2855fc6d5946c6":{"body":"{\"message\":\"The given data was invalid.\",\"errors\":{\"to\":[\"The to field is required.\"]}}","headers":{":status":["422"],"content-type":["application/json"]},"statusCode":422}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusHasAllFields().json b/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusHasAllFields().json index c65d114..b8d375d 100644 --- a/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusHasAllFields().json +++ b/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusHasAllFields().json @@ -1 +1 @@ -{"63bd823e896c67fed67b64075012bb83f8cf8e7d":{"body":"{\"data\":{\"id\":\"known-bulk-send-id\",\"state\":\"queued\",\"total_recipients_count\":2,\"suppressed_recipients_count\":0,\"suppressed_recipients\":null,\"validation_errors_count\":0,\"validation_errors\":{},\"messages_id\":null,\"created_at\":\"2025-01-10T08:00:00.000000Z\",\"updated_at\":\"2025-01-10T08:01:00.000000Z\"}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["55"]},"statusCode":200}} \ No newline at end of file +{"63bd823e896c67fed67b64075012bb83f8cf8e7d":{"body":"{\"data\":{\"id\":\"known-bulk-send-id\",\"state\":\"queued\",\"total_recipients_count\":2,\"suppressed_recipients_count\":0,\"suppressed_recipients\":null,\"validation_errors_count\":0,\"validation_errors\":null,\"messages_id\":null,\"created_at\":\"2025-01-10T08:00:00.000000Z\",\"updated_at\":\"2025-01-10T08:01:00.000000Z\"}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["55"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusKeepsThePositionOfKeyedMessageIds().json b/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusKeepsThePositionOfKeyedMessageIds().json new file mode 100644 index 0000000..cad156f --- /dev/null +++ b/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusKeepsThePositionOfKeyedMessageIds().json @@ -0,0 +1 @@ +{"67e439fe2b4e369a877abd7002b90d0102bde6d3":{"body":"{\"data\":{\"id\":\"keyed-bulk-send-id\",\"state\":\"completed\",\"total_recipients_count\":4,\"suppressed_recipients_count\":0,\"suppressed_recipients\":null,\"validation_errors_count\":2,\"validation_errors\":{\"message.0\":{\"from.email\":[\"The from.email must be verified.\"]},\"message.2\":{\"from.email\":[\"The from.email must be verified.\"]}},\"messages_id\":{\"1\":\"61487a14608b1d0b4d506633\",\"3\":\"61487a14608b1d0b4d506644\"},\"created_at\":\"2021-09-17T10:41:21.892000Z\",\"updated_at\":\"2021-09-17T10:41:23.684000Z\"}}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusWithAnUnknownIdFailsWith404().json b/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusWithAnUnknownIdFailsWith404().json new file mode 100644 index 0000000..7318d2c --- /dev/null +++ b/src/test/resources/fixtures/EmailSendTest_testBulkSendStatusWithAnUnknownIdFailsWith404().json @@ -0,0 +1 @@ +{"6fa227d56721c50a5ea2f9a137b58477fa6e549e":{"body":"{\"message\":\"Resource not found.\"}","headers":{":status":["404"],"content-type":["application/json"]},"statusCode":404}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_testSendEmailWithHeaders().json b/src/test/resources/fixtures/EmailSendTest_testSendEmailWithHeaders().json index e46e2b4..6d23536 100644 --- a/src/test/resources/fixtures/EmailSendTest_testSendEmailWithHeaders().json +++ b/src/test/resources/fixtures/EmailSendTest_testSendEmailWithHeaders().json @@ -1 +1 @@ -{"f2294285f9fcd9f1512873ff3fbb04732358da0f":{"body":"","headers":{":status":["202"],"content-type":["text/html; charset=UTF-8"],"x-message-id":["test-message-id-headers"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["115"]},"statusCode":202}} \ No newline at end of file +{"1d977665f533a3f9247c7aaa4cbc3e6037a6cd3b":{"body":"","headers":{":status":["202"],"content-type":["text/html; charset=UTF-8"],"x-message-id":["test-message-id-headers"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["115"]},"statusCode":202}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_testSendEmailWithListUnsubscribe().json b/src/test/resources/fixtures/EmailSendTest_testSendEmailWithListUnsubscribe().json index 8f90207..a26fb71 100644 --- a/src/test/resources/fixtures/EmailSendTest_testSendEmailWithListUnsubscribe().json +++ b/src/test/resources/fixtures/EmailSendTest_testSendEmailWithListUnsubscribe().json @@ -1 +1 @@ -{"9b86740adcd358601b386d0876eb4e27d63ddf8c":{"body":"","headers":{":status":["202"],"content-type":["text/html; charset=UTF-8"],"x-message-id":["test-message-id-unsubscribe"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["113"]},"statusCode":202}} \ No newline at end of file +{"b52667d4751ebd31adcd91394164469d3c81c2ea":{"body":"","headers":{":status":["202"],"content-type":["text/html; charset=UTF-8"],"x-message-id":["test-message-id-unsubscribe"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["113"]},"statusCode":202}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailSendTest_testSendEmailWithPrecedenceBulk().json b/src/test/resources/fixtures/EmailSendTest_testSendEmailWithPrecedenceBulk().json index b655fce..860b447 100644 --- a/src/test/resources/fixtures/EmailSendTest_testSendEmailWithPrecedenceBulk().json +++ b/src/test/resources/fixtures/EmailSendTest_testSendEmailWithPrecedenceBulk().json @@ -1 +1 @@ -{"8b868ad50feda2b8204f4191b94eded5673bc718":{"body":"","headers":{":status":["202"],"content-type":["text/html; charset=UTF-8"],"x-message-id":["test-message-id-precedence"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["114"]},"statusCode":202}} \ No newline at end of file +{"13bdee4173a7e5f18c4fd7985447ace86f791fa0":{"body":"","headers":{":status":["202"],"content-type":["text/html; charset=UTF-8"],"x-message-id":["test-message-id-precedence"],"x-ratelimit-limit":["120"],"x-ratelimit-remaining":["114"]},"statusCode":202}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailVerificationTests_testBuilderResetsEmailsAfterCreate().json b/src/test/resources/fixtures/EmailVerificationTests_testBuilderResetsEmailsAfterCreate().json new file mode 100644 index 0000000..9d3dc39 --- /dev/null +++ b/src/test/resources/fixtures/EmailVerificationTests_testBuilderResetsEmailsAfterCreate().json @@ -0,0 +1 @@ +{"0e6e2847b8fb5af7af9ee37ccd299aead4331b7b":{"body":"{\"data\":{\"id\":\"2351ndgwvrlzqx8k\",\"name\":\"Test email verification\",\"total\":0,\"verification_started\":null,\"verification_ended\":null,\"created_at\":\"2022-08-20T12:44:51.000000Z\",\"updated_at\":\"2022-08-20T12:47:11.000000Z\",\"status\":{\"name\":\"uploading\",\"count\":0},\"source\":\"api\",\"statistics\":{\"valid\":0,\"catch_all\":0,\"mailbox_full\":0,\"role_based\":0,\"unknown\":0,\"syntax_error\":0,\"typo\":0,\"mailbox_not_found\":0,\"disposable\":0,\"mailbox_blocked\":0,\"failed\":0,\"not_verified\":0}}}","headers":{":status":["201"],"content-type":["application/json"]},"statusCode":201},"60be8b8e06c551b892b9182e7ad22c60e0550cb7":{"body":"{\"data\":{\"id\":\"9dnxr7qvdqg6pm0e\",\"name\":\"Second list\",\"total\":0,\"verification_started\":null,\"verification_ended\":null,\"created_at\":\"2022-08-20T12:44:51.000000Z\",\"updated_at\":\"2022-08-20T12:47:11.000000Z\",\"status\":{\"name\":\"uploading\",\"count\":0},\"source\":\"api\",\"statistics\":{\"valid\":0,\"catch_all\":0,\"mailbox_full\":0,\"role_based\":0,\"unknown\":0,\"syntax_error\":0,\"typo\":0,\"mailbox_not_found\":0,\"disposable\":0,\"mailbox_blocked\":0,\"failed\":0,\"not_verified\":0}}}","headers":{":status":["201"],"content-type":["application/json"]},"statusCode":201}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailVerificationTests_testGetListDetailed().json b/src/test/resources/fixtures/EmailVerificationTests_testGetListDetailed().json new file mode 100644 index 0000000..9a7291d --- /dev/null +++ b/src/test/resources/fixtures/EmailVerificationTests_testGetListDetailed().json @@ -0,0 +1 @@ +{"6f39e4682ce6cc0883f281f8739153732c55c4bf":{"body":"{\"data\":{\"id\":\"7z3m5jgroogdpyo6\",\"name\":\"Test email verification\",\"total\":3,\"verification_started\":\"2022-08-20T12:47:11.000000Z\",\"verification_ended\":\"2022-08-20T12:52:00.000000Z\",\"created_at\":\"2022-08-20T12:44:51.000000Z\",\"updated_at\":\"2022-08-20T12:47:11.000000Z\",\"status\":{\"name\":\"verified\",\"count\":3},\"source\":\"api\",\"statistics\":{\"valid\":1,\"catch_all\":0,\"mailbox_full\":0,\"role_based\":0,\"unknown\":0,\"syntax_error\":0,\"typo\":1,\"mailbox_not_found\":0,\"disposable\":0,\"mailbox_blocked\":0,\"failed\":0,\"not_verified\":1},\"emails\":{\"data\":[{\"address\":\"info@example.com\",\"result\":\"valid\"},{\"address\":\"info1@example.com\",\"result\":\"typo\"}],\"pagination\":{\"current_page\":2,\"per_page\":10,\"total\":12,\"last_page\":2,\"from\":11,\"to\":12},\"links\":{\"first\":\"https://api.mailersend.com/v1/email-verification/7z3m5jgroogdpyo6?detailed=true&page=1\",\"last\":\"https://api.mailersend.com/v1/email-verification/7z3m5jgroogdpyo6?detailed=true&page=2\",\"prev\":\"https://api.mailersend.com/v1/email-verification/7z3m5jgroogdpyo6?detailed=true&page=1\",\"next\":null}}}}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailVerificationTests_testGetVerifyEmailAsyncStatusReturnsResponseFields().json b/src/test/resources/fixtures/EmailVerificationTests_testGetVerifyEmailAsyncStatusReturnsResponseFields().json index b1aa796..9e0dec1 100644 --- a/src/test/resources/fixtures/EmailVerificationTests_testGetVerifyEmailAsyncStatusReturnsResponseFields().json +++ b/src/test/resources/fixtures/EmailVerificationTests_testGetVerifyEmailAsyncStatusReturnsResponseFields().json @@ -1 +1 @@ -{"6c2759d5090a762f831ed4df316104511bbaa974":{"body":"{\"id\":\"abc123asyncstatusid\",\"address\":\"test@example.com\",\"status\":\"completed\",\"result\":\"unknown\",\"error\":null}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file +{"6c2759d5090a762f831ed4df316104511bbaa974":{"body":"{\"data\":{\"id\":\"abc123asyncstatusid\",\"address\":\"test@example.com\",\"status\":\"completed\",\"result\":\"unknown\",\"error\":null}}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/EmailVerificationTests_testVerifyEmailAsyncReturnsResponseFields().json b/src/test/resources/fixtures/EmailVerificationTests_testVerifyEmailAsyncReturnsResponseFields().json index 76b3e83..c99fe37 100644 --- a/src/test/resources/fixtures/EmailVerificationTests_testVerifyEmailAsyncReturnsResponseFields().json +++ b/src/test/resources/fixtures/EmailVerificationTests_testVerifyEmailAsyncReturnsResponseFields().json @@ -1 +1 @@ -{"8b07d899a976936825de278007ef8a1f6a2fd1e1":{"body":"{\"id\":\"67c83bf24a5d02568029ee10\",\"address\":\"test@example.com\",\"status\":\"queued\",\"result\":null,\"error\":null}","headers":{":status":["200"],"content-type":["application/json"]},"statusCode":200}} \ No newline at end of file +{"8b07d899a976936825de278007ef8a1f6a2fd1e1":{"body":"{\"data\":{\"id\":\"67c83bf24a5d02568029ee10\",\"address\":\"test@example.com\",\"status\":\"queued\",\"result\":null,\"error\":null}}","headers":{":status":["201"],"content-type":["application/json"]},"statusCode":201}} \ No newline at end of file diff --git a/src/test/resources/fixtures/InboundRoutesTest_AddInboundRouteTest().json b/src/test/resources/fixtures/InboundRoutesTest_AddInboundRouteTest().json index afd487f..b86a221 100644 --- a/src/test/resources/fixtures/InboundRoutesTest_AddInboundRouteTest().json +++ b/src/test/resources/fixtures/InboundRoutesTest_AddInboundRouteTest().json @@ -1 +1 @@ -{"b8a0377fe05e24ddd4159a4076934d0e3fb8dad1":{"body":"{\"data\":{\"id\":\"jpr9084z9xlw63dn\",\"name\":\"Test inbound name\",\"address\":\"emcbcwyz41fdrr7uuiz1@inbound.mailersend.net\",\"domain\":null,\"dns_checked_at\":null,\"enabled\":true,\"filters\":[{\"type\":\"match_all\",\"key\":null,\"comparer\":null,\"value\":null}],\"forwards\":[{\"type\":\"webhook\",\"value\":\"https:\\/\\/example-domain.com\",\"secret\":\"FRwZFJCkf7nbydrn0oq9uLc76USD4iaI\"}],\"mxValues\":{\"priority\":\"10\",\"target\":\"inbound.mailersend.net\"},\"include_attachments\":true}}","headers":{":status":["201"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["73d3b22358826f68-ATH"],"content-type":["application/json"],"date":["Fri, 19 Aug 2022 14:49:26 GMT"],"expect-ct":["max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""],"server":["cloudflare"],"strict-transport-security":["max-age=15724800; includeSubDomains"],"x-apiquota-remaining":["-1"],"x-apiquota-reset":["2022-08-20T00:00:00Z"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["57"]},"statusCode":201}} \ No newline at end of file +{"32d0d1cf07060940993a725bfcce8cc77e1331db":{"body":"{\"data\":{\"id\":\"jpr9084z9xlw63dn\",\"name\":\"Test inbound name\",\"address\":\"emcbcwyz41fdrr7uuiz1@inbound.mailersend.net\",\"domain\":null,\"dns_checked_at\":null,\"enabled\":true,\"filters\":[{\"type\":\"match_all\",\"key\":null,\"comparer\":null,\"value\":null}],\"forwards\":[{\"type\":\"webhook\",\"value\":\"https:\\/\\/example-domain.com\",\"secret\":\"FRwZFJCkf7nbydrn0oq9uLc76USD4iaI\"}],\"mxValues\":{\"priority\":\"10\",\"target\":\"inbound.mailersend.net\"},\"include_attachments\":true}}","headers":{":status":["201"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["73d3b22358826f68-ATH"],"content-type":["application/json"],"date":["Fri, 19 Aug 2022 14:49:26 GMT"],"expect-ct":["max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""],"server":["cloudflare"],"strict-transport-security":["max-age=15724800; includeSubDomains"],"x-apiquota-remaining":["-1"],"x-apiquota-reset":["2022-08-20T00:00:00Z"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["57"]},"statusCode":201}} \ No newline at end of file diff --git a/src/test/resources/fixtures/InboundRoutesTest_UpdateInboundRouteTest().json b/src/test/resources/fixtures/InboundRoutesTest_UpdateInboundRouteTest().json index 0699ce9..2eacc85 100644 --- a/src/test/resources/fixtures/InboundRoutesTest_UpdateInboundRouteTest().json +++ b/src/test/resources/fixtures/InboundRoutesTest_UpdateInboundRouteTest().json @@ -1 +1 @@ -{"1aff61f4d4446105b742718ccb14f912cd075602":{"body":"{\"data\":{\"id\":\"83yxj6ljoq4do2rm\",\"name\":\"Updated route name\",\"address\":\"vxlarh7vthhnagf3zved@inbound.mailersend.net\",\"domain\":null,\"dns_checked_at\":null,\"enabled\":true,\"filters\":[{\"type\":\"match_all\",\"key\":null,\"comparer\":null,\"value\":null}],\"forwards\":[{\"type\":\"webhook\",\"value\":\"https:\\/\\/example.com\",\"secret\":\"9IAyXyE6eAg2pre8QQfvcs3VPJSS8iaW\"}],\"mxValues\":{\"priority\":\"10\",\"target\":\"inbound.mailersend.net\"},\"include_attachments\":true}}","headers":{":status":["200"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["73d3bf27ad6238d6-ATH"],"content-type":["application/json"],"date":["Fri, 19 Aug 2022 14:58:19 GMT"],"expect-ct":["max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""],"server":["cloudflare"],"strict-transport-security":["max-age=15724800; 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 +{"602d07f16c819cb029d42639d9710117165f4a16":{"body":"{\"data\":{\"id\":\"83yxj6ljoq4do2rm\",\"name\":\"Updated route name\",\"address\":\"vxlarh7vthhnagf3zved@inbound.mailersend.net\",\"domain\":null,\"dns_checked_at\":null,\"enabled\":true,\"filters\":[{\"type\":\"match_all\",\"key\":null,\"comparer\":null,\"value\":null}],\"forwards\":[{\"type\":\"webhook\",\"value\":\"https:\\/\\/example.com\",\"secret\":\"9IAyXyE6eAg2pre8QQfvcs3VPJSS8iaW\"}],\"mxValues\":{\"priority\":\"10\",\"target\":\"inbound.mailersend.net\"},\"include_attachments\":true}}","headers":{":status":["200"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["73d3bf27ad6238d6-ATH"],"content-type":["application/json"],"date":["Fri, 19 Aug 2022 14:58:19 GMT"],"expect-ct":["max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""],"server":["cloudflare"],"strict-transport-security":["max-age=15724800; 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/SenderIdentitiesTest_testGetIdentitiesWithFilters().json b/src/test/resources/fixtures/SenderIdentitiesTest_testGetIdentitiesWithFilters().json new file mode 100644 index 0000000..b9db128 --- /dev/null +++ b/src/test/resources/fixtures/SenderIdentitiesTest_testGetIdentitiesWithFilters().json @@ -0,0 +1 @@ +{"1599bb2fd4293998b325b7cd2e93009934c46079":{"body":"{\"data\":[{\"id\":\"ident-id-123\",\"email\":\"test+1@example.com\",\"name\":\"Test Identity\",\"reply_to_email\":\"reply@example.com\",\"reply_to_name\":\"Reply Name\",\"is_verified\":true,\"resends\":0,\"domain\":{\"id\":\"jpzkmgq7e5vl059v\",\"name\":\"test-sdk.com\",\"created_at\":\"2022-11-29T10:43:22.000000Z\",\"updated_at\":\"2022-11-29T10:43:32.000000Z\"}}],\"links\":{\"first\":\"https://api.mailersend.com/v1/identities?page=1\",\"last\":\"https://api.mailersend.com/v1/identities?page=2\",\"prev\":\"https://api.mailersend.com/v1/identities?page=1\",\"next\":null},\"meta\":{\"current_page\":2,\"from\":11,\"last_page\":2,\"path\":\"https://api.mailersend.com/v1/identities\",\"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/SmsInboundRoutesTests_testAddSmsInboundRouteWithoutEnabled().json b/src/test/resources/fixtures/SmsInboundRoutesTests_testAddSmsInboundRouteWithoutEnabled().json new file mode 100644 index 0000000..5f5cc00 --- /dev/null +++ b/src/test/resources/fixtures/SmsInboundRoutesTests_testAddSmsInboundRouteWithoutEnabled().json @@ -0,0 +1 @@ +{"5acb4b0679a464197e61fa1c81d528845c900c40":{"body":"{\"data\":{\"id\":\"83yxj6lj5gdo2rme\",\"name\":\"Test inbound route\",\"filter\":{\"value\":\"START\",\"comparer\":\"equal\"},\"forward_url\":\"https://example.com\",\"enabled\":true,\"secret\":\"1SYXKqcJNBPhrnnGFDO1sfSZvr9tLlD8\",\"created_at\":\"2022-09-14T08:03:50.000000Z\"}}","headers":{":status":["201"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":201}} \ No newline at end of file diff --git a/src/test/resources/fixtures/SmsInboundRoutesTests_testUpdateSmsInboundRouteWithoutEnabled().json b/src/test/resources/fixtures/SmsInboundRoutesTests_testUpdateSmsInboundRouteWithoutEnabled().json new file mode 100644 index 0000000..5b3d62d --- /dev/null +++ b/src/test/resources/fixtures/SmsInboundRoutesTests_testUpdateSmsInboundRouteWithoutEnabled().json @@ -0,0 +1 @@ +{"495778f9cb0a074598cf2aa7f2e1d9f41f5fd12a":{"body":"{\"data\":{\"id\":\"83yxj6lj5gdo2rme\",\"name\":\"Test inbound route renamed\",\"filter\":{\"value\":\"START\",\"comparer\":\"equal\"},\"forward_url\":\"https://example.com\",\"enabled\":true,\"secret\":\"1SYXKqcJNBPhrnnGFDO1sfSZvr9tLlD8\",\"created_at\":\"2022-09-14T08:03:50.000000Z\"}}","headers":{":status":["200"],"content-type":["application/json"],"x-ratelimit-limit":["60"],"x-ratelimit-remaining":["59"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/SmsWebhooksTest_TestUpdateSmsWebhook().json b/src/test/resources/fixtures/SmsWebhooksTest_TestUpdateSmsWebhook().json index 7e31245..54c646c 100644 --- a/src/test/resources/fixtures/SmsWebhooksTest_TestUpdateSmsWebhook().json +++ b/src/test/resources/fixtures/SmsWebhooksTest_TestUpdateSmsWebhook().json @@ -1 +1 @@ -{"abfd453e17343e7b76d97ef62afdf4a72a58cde3":{"body":"{\"data\":[{\"id\":\"jpr9084zjgw63dnx\",\"url\":\"https:\\/\\/example.com\",\"events\":[\"sms.sent\"],\"name\":\"sms webhook\",\"enabled\":true,\"created_at\":\"2022-09-14T08:32:16.000000Z\",\"updated_at\":\"2022-09-14T08:32:16.000000Z\",\"sms_number\":{\"id\":\"3yxj6lj9x14do2rm\",\"telephone_number\":\"+18133657349\",\"paused\":false,\"created_at\":\"2022-08-01T15:33:47.000000Z\"}}],\"links\":{\"first\":\"https:\\/\\/api.mailersend.com\\/v1\\/sms-webhooks?page\u003d1\",\"last\":\"https:\\/\\/api.mailersend.com\\/v1\\/sms-webhooks?page\u003d1\",\"prev\":null,\"next\":null},\"meta\":{\"current_page\":1,\"from\":1,\"last_page\":1,\"links\":[{\"url\":null,\"label\":\"\u0026laquo; Previous\",\"active\":false},{\"url\":\"https:\\/\\/api.mailersend.com\\/v1\\/sms-webhooks?page\u003d1\",\"label\":\"1\",\"active\":true},{\"url\":null,\"label\":\"Next \u0026raquo;\",\"active\":false}],\"path\":\"https:\\/\\/api.mailersend.com\\/v1\\/sms-webhooks\",\"per_page\":\"25\",\"to\":1,\"total\":1}}","headers":{":status":["200"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["74a7de547f046f32-ATH"],"content-type":["application/json"],"date":["Wed, 14 Sep 2022 08:49:17 GMT"],"server":["cloudflare"],"strict-transport-security":["max-age\u003d15724800; includeSubDomains"],"x-apiquota-remaining":["-1"],"x-apiquota-reset":["2022-09-15T00:00:00Z"]},"statusCode":200},"4dc945a534289f91b56eec97813e445063339c1f":{"body":"{\"data\":{\"id\":\"jpr9084zjgw63dnx\",\"url\":\"https:\\/\\/example.com\",\"events\":[],\"name\":\"sms updated webhook\",\"enabled\":true,\"created_at\":\"2022-09-14T08:32:16.000000Z\",\"updated_at\":\"2022-09-14T08:49:18.000000Z\",\"sms_number\":{\"id\":\"3yxj6lj9x14do2rm\",\"telephone_number\":\"+18133657349\",\"paused\":false,\"created_at\":\"2022-08-01T15:33:47.000000Z\"}}}","headers":{":status":["200"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["74a7de57489e6f32-ATH"],"content-type":["application/json"],"date":["Wed, 14 Sep 2022 08:49:18 GMT"],"server":["cloudflare"],"strict-transport-security":["max-age\u003d15724800; includeSubDomains"],"x-apiquota-remaining":["-1"],"x-apiquota-reset":["2022-09-15T00:00:00Z"]},"statusCode":200}} \ No newline at end of file +{"abfd453e17343e7b76d97ef62afdf4a72a58cde3":{"body":"{\"data\":[{\"id\":\"jpr9084zjgw63dnx\",\"url\":\"https:\\/\\/example.com\",\"events\":[\"sms.sent\"],\"name\":\"sms webhook\",\"enabled\":true,\"created_at\":\"2022-09-14T08:32:16.000000Z\",\"updated_at\":\"2022-09-14T08:32:16.000000Z\",\"sms_number\":{\"id\":\"3yxj6lj9x14do2rm\",\"telephone_number\":\"+18133657349\",\"paused\":false,\"created_at\":\"2022-08-01T15:33:47.000000Z\"}}],\"links\":{\"first\":\"https:\\/\\/api.mailersend.com\\/v1\\/sms-webhooks?page=1\",\"last\":\"https:\\/\\/api.mailersend.com\\/v1\\/sms-webhooks?page=1\",\"prev\":null,\"next\":null},\"meta\":{\"current_page\":1,\"from\":1,\"last_page\":1,\"links\":[{\"url\":null,\"label\":\"« Previous\",\"active\":false},{\"url\":\"https:\\/\\/api.mailersend.com\\/v1\\/sms-webhooks?page=1\",\"label\":\"1\",\"active\":true},{\"url\":null,\"label\":\"Next »\",\"active\":false}],\"path\":\"https:\\/\\/api.mailersend.com\\/v1\\/sms-webhooks\",\"per_page\":\"25\",\"to\":1,\"total\":1}}","headers":{":status":["200"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["74a7de547f046f32-ATH"],"content-type":["application/json"],"date":["Wed, 14 Sep 2022 08:49:17 GMT"],"server":["cloudflare"],"strict-transport-security":["max-age=15724800; includeSubDomains"],"x-apiquota-remaining":["-1"],"x-apiquota-reset":["2022-09-15T00:00:00Z"]},"statusCode":200},"4e07ea251fbf03f181fb48e7e979d7e6b6abb765":{"body":"{\"data\":{\"id\":\"jpr9084zjgw63dnx\",\"url\":\"https://example.com\",\"events\":[\"sms.sent\"],\"name\":\"sms updated webhook\",\"enabled\":true,\"created_at\":\"2022-09-14T08:32:16.000000Z\",\"updated_at\":\"2022-09-14T08:49:18.000000Z\",\"sms_number\":{\"id\":\"3yxj6lj9x14do2rm\",\"telephone_number\":\"+18133657349\",\"paused\":false,\"created_at\":\"2022-08-01T15:33:47.000000Z\"}}}","headers":{":status":["200"],"cache-control":["no-cache, private"],"cf-cache-status":["DYNAMIC"],"cf-ray":["74a7de57489e6f32-ATH"],"content-type":["application/json"],"date":["Wed, 14 Sep 2022 08:49:18 GMT"],"server":["cloudflare"],"strict-transport-security":["max-age=15724800; includeSubDomains"],"x-apiquota-remaining":["-1"],"x-apiquota-reset":["2022-09-15T00:00:00Z"]},"statusCode":200}} \ No newline at end of file diff --git a/src/test/resources/fixtures/TemplatesTest_testGetTemplateParsesContentTranslationsAndStats().json b/src/test/resources/fixtures/TemplatesTest_testGetTemplateParsesContentTranslationsAndStats().json new file mode 100644 index 0000000..33a3109 --- /dev/null +++ b/src/test/resources/fixtures/TemplatesTest_testGetTemplateParsesContentTranslationsAndStats().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_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