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: 7 additions & 1 deletion src/main/java/co/lettermint/api/ApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,12 @@ public DomainUpdateProjectsResponse updateProjects(String domainId, UpdateDomain
}
}

public static class MessagesEndpoint {
// Keep the pre-v2.6 JVM descriptor through javac's covariant return bridge.
private interface ScheduledMessageCancellation {
RescheduleMessageResponse cancel(String messageId);
}

public static class MessagesEndpoint implements ScheduledMessageCancellation {
private final LettermintClient client;

MessagesEndpoint(LettermintClient client) {
Expand All @@ -145,6 +150,7 @@ public RescheduleMessageResponse reschedule(String messageId, RescheduleMessageR
return client.patch("/messages/" + segment(messageId), payload, RescheduleMessageResponse.class);
}

@Override
public CancelScheduledMessageResponse cancel(String messageId) {
return client.post("/messages/" + segment(messageId) + "/cancel", null, CancelScheduledMessageResponse.class);
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/co/lettermint/models/api/RouteData.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public class RouteData {
@JsonProperty("inbound_mx_hostname")
public String inboundMxHostname;

@JsonProperty("inbound_route_domain")
public String inboundRouteDomain;

@JsonProperty("inbound_domain")
public String inboundDomain;

Expand Down
87 changes: 87 additions & 0 deletions src/test/java/co/lettermint/CancelBinaryCompatibilityTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package co.lettermint;

import co.lettermint.api.ApiClient;
import co.lettermint.models.api.CancelScheduledMessageResponse;
import co.lettermint.models.api.RescheduleMessageResponse;
import co.lettermint.models.api.RouteData;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.junit.jupiter.api.Assertions.*;

class CancelBinaryCompatibilityTest {
@TempDir
Path temporary;

@Test
void callerCompiledAgainstOriginalPr53DescriptorStillRuns() throws Exception {
// PR #53 base: 8e3f4b1b0cbad79fbda6e6d0c724d18a899a2b0b.
Path stub = temporary.resolve("co/lettermint/api/ApiClient.java");
Files.createDirectories(stub.getParent());
Files.write(stub, ("package co.lettermint.api; "
+ "import co.lettermint.models.api.RescheduleMessageResponse; "
+ "public class ApiClient { public static class MessagesEndpoint { "
+ "public RescheduleMessageResponse cancel(String id) { return null; } } }")
.getBytes(StandardCharsets.UTF_8));
Path caller = temporary.resolve("OldCaller.java");
Files.write(caller, ("import co.lettermint.api.ApiClient; "
+ "import co.lettermint.models.api.RescheduleMessageResponse; "
+ "public class OldCaller { "
+ "public static RescheduleMessageResponse cancel(ApiClient.MessagesEndpoint endpoint) { "
+ "return endpoint.cancel(\"message_1\"); } }").getBytes(StandardCharsets.UTF_8));
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
assertNotNull(compiler, "This test requires a JDK, not a JRE");
assertEquals(0, compiler.run(null, null, null, "-classpath",
System.getProperty("java.class.path"), "-d", temporary.toString(),
stub.toString(), caller.toString()));

try (MockWebServer server = new MockWebServer();
URLClassLoader oldCaller = new URLClassLoader(new URL[]{temporary.toUri().toURL()},
ApiClient.class.getClassLoader())) {
server.enqueue(new MockResponse().setHeader("Content-Type", "application/json")
.setBody("{\"message_id\":\"message_1\",\"status\":\"canceled\"}"));
server.start();
ApiClient api = Lettermint.api("token", server.url("/v1").toString());
Object value = oldCaller.loadClass("OldCaller")
.getMethod("cancel", ApiClient.MessagesEndpoint.class).invoke(null, api.messages());
assertTrue(value instanceof CancelScheduledMessageResponse);
assertTrue(value instanceof RescheduleMessageResponse);
assertEquals("message_1", ((RescheduleMessageResponse) value).messageId);
assertEquals("canceled", ((RescheduleMessageResponse) value).status);
okhttp3.mockwebserver.RecordedRequest request = server.takeRequest();
assertEquals("POST", request.getMethod());
assertEquals("/v1/messages/message_1/cancel", request.getPath());
}
}

@Test
void currentReturnTypeAndOldSourceAssignmentBothCompile() {
assertTrue(RescheduleMessageResponse.class.isAssignableFrom(CancelScheduledMessageResponse.class));
}

private static RescheduleMessageResponse oldSourceCaller(ApiClient api) {
return api.messages().cancel("message_1");
}

@Test
void inboundRouteDomainIsOptionalAndNullable() throws Exception {
ObjectMapper mapper = new ObjectMapper();
RouteData populated = mapper.readValue("{\"inbound_route_domain\":\"incoming.example.com\"}", RouteData.class);
assertEquals("incoming.example.com", populated.inboundRouteDomain);
assertEquals("incoming.example.com",
mapper.readValue(mapper.writeValueAsString(populated), RouteData.class).inboundRouteDomain);
assertNull(mapper.readValue("{\"inbound_route_domain\":null}", RouteData.class).inboundRouteDomain);
assertNull(mapper.readValue("{}", RouteData.class).inboundRouteDomain);
}
}
Loading