Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/main/java/co/lettermint/api/ApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.net.URLEncoder;
import java.util.Map;

public class ApiClient {

Check warning on line 10 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 10 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 10 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
private final LettermintClient client;
private final DomainsEndpoint domains;
private final MessagesEndpoint messages;
Expand All @@ -18,7 +18,7 @@
private final TeamEndpoint team;
private final WebhooksEndpoint webhooks;

public ApiClient(String apiToken, String baseUrl) {

Check warning on line 21 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 21 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 21 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
this.client = new LettermintClient(apiToken, baseUrl, LettermintClient.AuthMode.BEARER);
this.domains = new DomainsEndpoint(client);
this.messages = new MessagesEndpoint(client);
Expand Down Expand Up @@ -78,7 +78,7 @@
}
}

public static class DomainsEndpoint {

Check warning on line 81 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 81 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 81 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
private final LettermintClient client;

DomainsEndpoint(LettermintClient client) {
Expand Down Expand Up @@ -118,7 +118,7 @@
}
}

public static class MessagesEndpoint {

Check warning on line 121 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 121 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 121 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
private final LettermintClient client;

MessagesEndpoint(LettermintClient client) {
Expand All @@ -137,6 +137,14 @@
return client.get("/messages/" + segment(messageId), MessageData.class);
}

public RescheduleMessageResponse reschedule(String messageId, RescheduleMessageRequest payload) {
return client.patch("/messages/" + segment(messageId), payload, RescheduleMessageResponse.class);
}

public RescheduleMessageResponse cancel(String messageId) {
return client.post("/messages/" + segment(messageId) + "/cancel", null, RescheduleMessageResponse.class);
}

public MessageEventsResponse events(String messageId) {
return events(messageId, null);
}
Expand All @@ -158,7 +166,7 @@
}
}

public static class ProjectsEndpoint {

Check warning on line 169 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 169 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 169 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
private final LettermintClient client;

ProjectsEndpoint(LettermintClient client) {
Expand Down Expand Up @@ -206,7 +214,7 @@
}
}

public static class RoutesEndpoint {

Check warning on line 217 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 217 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 217 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
private final LettermintClient client;

RoutesEndpoint(LettermintClient client) {
Expand All @@ -230,7 +238,7 @@
}
}

public static class StatsEndpoint {

Check warning on line 241 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 241 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 241 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
private final LettermintClient client;

StatsEndpoint(LettermintClient client) {
Expand All @@ -246,7 +254,7 @@
}
}

public static class SuppressionsEndpoint {

Check warning on line 257 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 257 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 257 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
private final LettermintClient client;

SuppressionsEndpoint(LettermintClient client) {
Expand All @@ -270,7 +278,7 @@
}
}

public static class TeamEndpoint {

Check warning on line 281 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 281 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 281 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
private final LettermintClient client;

TeamEndpoint(LettermintClient client) {
Expand Down Expand Up @@ -314,7 +322,7 @@
}
}

public static class WebhooksEndpoint {

Check warning on line 325 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 17 - ubuntu-latest)

no comment

Check warning on line 325 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Test (Java 21 - ubuntu-latest)

no comment

Check warning on line 325 in src/main/java/co/lettermint/api/ApiClient.java

View workflow job for this annotation

GitHub Actions / Build

no comment
private final LettermintClient client;

WebhooksEndpoint(LettermintClient client) {
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/co/lettermint/client/LettermintClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ public <T> T put(String path, Object payload, Class<T> responseClass) {
return request("PUT", url(path, null), payload, responseClass, null, null);
}

public <T> T patch(String path, Object payload, Class<T> responseClass) {
return request("PATCH", url(path, null), payload, responseClass, null, null);
}

public <T> T delete(String path, Class<T> responseClass) {
return request("DELETE", url(path, null), null, responseClass, null, null);
}
Expand Down Expand Up @@ -148,6 +152,8 @@ private <T> T request(String method, String requestUrl, Object payload, Class<T>
requestBuilder.post(body != null ? body : RequestBody.create(new byte[0], JSON));
} else if ("PUT".equals(method)) {
requestBuilder.put(body != null ? body : RequestBody.create(new byte[0], JSON));
} else if ("PATCH".equals(method)) {
requestBuilder.patch(body != null ? body : RequestBody.create(new byte[0], JSON));
} else if ("DELETE".equals(method)) {
requestBuilder.delete();
} else {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/co/lettermint/endpoints/EmailEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
private List<String> bcc;
private List<String> replyTo;
private String subject;
private String scheduledAt;
private String html;
private String text;
private Map<String, String> headers;
Expand All @@ -46,6 +47,7 @@
this.bcc = new ArrayList<>();
this.replyTo = new ArrayList<>();
this.subject = null;
this.scheduledAt = null;
this.html = null;
this.text = null;
this.headers = new LinkedHashMap<>();
Expand All @@ -62,7 +64,7 @@
* Set the sender email address.
* Format: {@code "Name <email>"} or just {@code "email"}
*/
public EmailEndpoint from(String from) {

Check warning on line 67 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

Check warning on line 67 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

no @PARAM for from

Check warning on line 67 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

Check warning on line 67 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

no @PARAM for from
this.from = from;
return this;
}
Expand All @@ -70,7 +72,7 @@
/**
* Set recipient email addresses. Replaces any existing recipients.
*/
public EmailEndpoint to(String... emails) {

Check warning on line 75 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

Check warning on line 75 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

no @PARAM for emails

Check warning on line 75 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

Check warning on line 75 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

no @PARAM for emails
this.to = new ArrayList<>(Arrays.asList(emails));
return this;
}
Expand All @@ -78,7 +80,7 @@
/**
* Set CC email addresses. Replaces any existing CC recipients.
*/
public EmailEndpoint cc(String... emails) {

Check warning on line 83 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

Check warning on line 83 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

no @PARAM for emails

Check warning on line 83 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

Check warning on line 83 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

no @PARAM for emails
this.cc = new ArrayList<>(Arrays.asList(emails));
return this;
}
Expand All @@ -86,7 +88,7 @@
/**
* Set BCC email addresses. Replaces any existing BCC recipients.
*/
public EmailEndpoint bcc(String... emails) {

Check warning on line 91 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

Check warning on line 91 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

no @PARAM for emails

Check warning on line 91 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

Check warning on line 91 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

no @PARAM for emails
this.bcc = new ArrayList<>(Arrays.asList(emails));
return this;
}
Expand All @@ -94,7 +96,7 @@
/**
* Set reply-to email addresses. Replaces any existing reply-to addresses.
*/
public EmailEndpoint replyTo(String... emails) {

Check warning on line 99 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

Check warning on line 99 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 11 - ubuntu-latest)

no @PARAM for emails

Check warning on line 99 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

Check warning on line 99 in src/main/java/co/lettermint/endpoints/EmailEndpoint.java

View workflow job for this annotation

GitHub Actions / Test (Java 8 - ubuntu-latest)

no @PARAM for emails
this.replyTo = new ArrayList<>(Arrays.asList(emails));
return this;
}
Expand All @@ -107,6 +109,12 @@
return this;
}

/** Set the requested delivery time for the email. */
public EmailEndpoint scheduledAt(String scheduledAt) {
this.scheduledAt = scheduledAt;
return this;
}

/**
* Set the HTML content of the email.
*/
Expand Down Expand Up @@ -296,6 +304,10 @@
payload.put("subject", subject);
}

if (scheduledAt != null) {
payload.put("scheduled_at", scheduledAt);
}

if (html != null) {
payload.put("html", html);
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/co/lettermint/models/api/MessageData.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public class MessageData {
@JsonProperty("status_changed_at")
public String statusChangedAt;

@JsonProperty("scheduled_at")
public String scheduledAt;

@JsonProperty("tag")
public String tag;

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/co/lettermint/models/api/MessageEventType.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package co.lettermint.models.api;

public final class MessageEventType {
public static final String SCHEDULED = "scheduled";
public static final String RESCHEDULED = "rescheduled";
public static final String CANCELED = "canceled";
public static final String RELEASED = "released";
public static final String QUEUED = "queued";
public static final String PROCESSED = "processed";
public static final String SUPPRESSED = "suppressed";
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/co/lettermint/models/api/MessageListData.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public class MessageListData {
@JsonProperty("status")
public String status;

@JsonProperty("scheduled_at")
public String scheduledAt;

@JsonProperty("spam_score")
public Double spamScore;

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/co/lettermint/models/api/MessageStatus.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package co.lettermint.models.api;

public final class MessageStatus {
public static final String SCHEDULED = "scheduled";
public static final String PENDING = "pending";
public static final String QUEUED = "queued";
public static final String SUPPRESSED = "suppressed";
Expand All @@ -15,6 +16,7 @@ public final class MessageStatus {
public static final String BLOCKED = "blocked";
public static final String POLICYREJECTED = "policy_rejected";
public static final String UNSUBSCRIBED = "unsubscribed";
public static final String CANCELED = "canceled";

private MessageStatus() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package co.lettermint.models.api;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class RescheduleMessageRequest {
@JsonProperty("scheduled_at")
public String scheduledAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package co.lettermint.models.api;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class RescheduleMessageResponse {
@JsonProperty("message_id")
public String messageId;

@JsonProperty("status")
public String status;

@JsonProperty("scheduled_at")
public String scheduledAt;
}
3 changes: 3 additions & 0 deletions src/main/java/co/lettermint/models/api/SendMailRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public class SendMailRequest {
@JsonProperty("subject")
public String subject;

@JsonProperty("scheduled_at")
public String scheduledAt;

@JsonProperty("headers")
public Map<String, String> headers;

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/co/lettermint/models/api/SendMailResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ public class SendMailResponse {

@JsonProperty("status")
public String status;

@JsonProperty("scheduled_at")
public String scheduledAt;
}
29 changes: 28 additions & 1 deletion src/test/java/co/lettermint/ApiClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,29 @@ void rawMessageEndpointsPreserveResponseBody() throws Exception {
}
}

@Test
void schedulesMessageChangesWithTypedResponses() throws Exception {
try (MockWebServer server = new MockWebServer()) {
server.enqueue(new MockResponse().setHeader("Content-Type", "application/json").setBody("{\"message_id\":\"msg/id\",\"status\":\"scheduled\",\"scheduled_at\":\"2026-08-27T09:00:00Z\"}"));
server.enqueue(new MockResponse().setHeader("Content-Type", "application/json").setBody("{\"message_id\":\"msg/id\",\"status\":\"canceled\",\"scheduled_at\":null}"));
server.start();

ApiClient api = Lettermint.api("api-token", server.url("/v1").toString());
co.lettermint.models.api.RescheduleMessageRequest payload = new co.lettermint.models.api.RescheduleMessageRequest();
payload.scheduledAt = "2026-08-27T09:00:00Z";
assertEquals("scheduled", api.messages().reschedule("msg/id", payload).status);
assertEquals("canceled", api.messages().cancel("msg/id").status);

RecordedRequest reschedule = server.takeRequest();
assertEquals("PATCH", reschedule.getMethod());
assertEquals("/v1/messages/msg%2Fid", reschedule.getPath());
assertTrue(reschedule.getBody().readUtf8().contains("scheduled_at"));
RecordedRequest cancel = server.takeRequest();
assertEquals("POST", cancel.getMethod());
assertEquals("/v1/messages/msg%2Fid/cancel", cancel.getPath());
}
}

@Test
void publicClientRejectsAbsoluteRequestPaths() throws Exception {
Lettermint lettermint = new Lettermint("sending-token", "https://api.example.test/v1");
Expand All @@ -159,7 +182,7 @@ void publicClientRejectsAbsoluteRequestPaths() throws Exception {

@Test
void generatedApiModelsMatchCurrentTeamSchema() {
assertEquals("auto_replied", co.lettermint.models.api.MessageEventType.AUTOREPLIED);
assertEquals("scheduled", co.lettermint.models.api.MessageEventType.SCHEDULED);
assertEquals("message.auto_replied", co.lettermint.models.api.WebhookEvent.MESSAGEAUTOREPLIED);
assertEquals("admin", co.lettermint.models.api.BuiltInTeamRole.ADMIN);
assertEquals("enforced", co.lettermint.models.api.TlsPolicy.ENFORCED);
Expand Down Expand Up @@ -210,6 +233,7 @@ void generatedApiModelsMatchCurrentTeamSchema() {

co.lettermint.models.api.MessageListData message = new co.lettermint.models.api.MessageListData();
message.spamScore = 2.5;
message.scheduledAt = "2026-08-27T09:00:00Z";

assertFalse(routeUpdate.settings.generatePlaintextFallback);
assertEquals("enforced", routeUpdate.settings.tls);
Expand All @@ -226,6 +250,7 @@ void generatedApiModelsMatchCurrentTeamSchema() {
assertEquals("managed_cname", domain.dkimMode);
assertEquals("msg_123", recipient.sourceMessage.id);
assertEquals(2.5, message.spamScore);
assertEquals("2026-08-27T09:00:00Z", message.scheduledAt);
}

@Test
Expand All @@ -243,6 +268,8 @@ void apiExposesDocumentedOperations() throws Exception {
methods.put("v1.blockedFileTypes", api.getClass().getMethod("blockedFileTypes"));
methods.put("message.index", api.messages().getClass().getMethod("list"));
methods.put("message.show", api.messages().getClass().getMethod("retrieve", String.class));
methods.put("rescheduleMessage", api.messages().getClass().getMethod("reschedule", String.class, co.lettermint.models.api.RescheduleMessageRequest.class));
methods.put("cancelScheduledMessage", api.messages().getClass().getMethod("cancel", String.class));
methods.put("message.events", api.messages().getClass().getMethod("events", String.class));
methods.put("message.source", api.messages().getClass().getMethod("source", String.class));
methods.put("message.html", api.messages().getClass().getMethod("html", String.class));
Expand Down