retrieveConfigurationBinding(String eventKey) {
+ return http.get("/v1/events/" + encode(eventKey) + "/event-configuration");
+ }
+
+ /**
+ * Binds an exact published configuration version, or passes {@code null} to detach.
+ *
+ * This compare-and-set mutation remains single-attempt because the public operation
+ * does not promise exact response replay.
+ */
+ public Map updateConfigurationBinding(
+ String eventKey, long expectedRevision, Map configuration) {
+ Map request = new LinkedHashMap<>();
+ request.put("expectedRevision", expectedRevision);
+ // Null is meaningful here: it detaches the existing binding and must not
+ // be dropped by the optional-field body helper.
+ request.put("configuration", configuration);
+ return http.put(
+ "/v1/events/" + encode(eventKey) + "/event-configuration",
+ request);
+ }
+
public Map update(String eventKey, Map fields) {
return http.patch("/v1/events/" + encode(eventKey), fields);
}
diff --git a/src/test/java/io/seatlayer/ClientTest.java b/src/test/java/io/seatlayer/ClientTest.java
index 90090fc..411744c 100644
--- a/src/test/java/io/seatlayer/ClientTest.java
+++ b/src/test/java/io/seatlayer/ClientTest.java
@@ -111,6 +111,38 @@ void encodesPathParameters() {
assertEquals("https://api.seatlayer.io/v1/events/ev%2F..%2Fadmin", call(0).url());
}
+ @Test
+ @DisplayName("reads, binds and explicitly detaches an Event configuration")
+ void eventConfigurationBindingContract() {
+ String binding = "{\"configuration\":{\"id\":\"ec_touring\",\"version\":3},"
+ + "\"revision\":7,\"changedBy\":\"api-key:key_1\",\"changedAt\":123,"
+ + "\"audit\":[]}";
+ SeatLayer sdk = client(List.of(
+ Stub.of(200, binding), Stub.of(200, binding),
+ Stub.of(200, "{\"configuration\":null,\"revision\":8,"
+ + "\"changedBy\":\"api-key:key_1\",\"changedAt\":124,\"audit\":[]}")));
+
+ Map retrieved = sdk.events().retrieveConfigurationBinding("ev / main");
+ assertEquals(7L, retrieved.get("revision"));
+ Map configuration = new LinkedHashMap<>();
+ configuration.put("id", "ec_touring");
+ configuration.put("version", 3);
+ sdk.events().updateConfigurationBinding("ev / main", 6, configuration);
+ sdk.events().updateConfigurationBinding("ev / main", 7, null);
+
+ String expectedUrl = "https://api.seatlayer.io/v1/events/ev%20%2F%20main/event-configuration";
+ assertEquals(expectedUrl, call(0).url());
+ assertEquals(expectedUrl, call(1).url());
+ assertEquals(expectedUrl, call(2).url());
+ assertEquals("GET", call(0).method());
+ assertEquals(
+ "{\"expectedRevision\":6,\"configuration\":{\"id\":\"ec_touring\",\"version\":3}}",
+ call(1).body());
+ assertEquals("{\"expectedRevision\":7,\"configuration\":null}", call(2).body());
+ assertNull(call(1).headers().get("Idempotency-Key"));
+ assertNull(call(2).headers().get("Idempotency-Key"));
+ }
+
@Test
@DisplayName("generates an Idempotency-Key only for header-replay mutations")
void idempotencyKeyOnlyOnHeaderReplayMutations() {