diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c9468..2cfdcf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog -## Unreleased +## 0.6.0 — 2026-08-23 + +- Added exact immutable Event configuration binding reads and compare-and-set + attach/detach through `Events.retrieveConfigurationBinding` and + `Events.updateConfigurationBinding`. Updates remain deliberately + single-attempt. ## 0.5.0 — 2026-08-21 diff --git a/README.md b/README.md index 687a3f4..c3cfe2b 100644 --- a/README.md +++ b/README.md @@ -265,7 +265,7 @@ seatlayer.request("POST", "/v1/events/ev_1/some-new-route", null, Map.of("qty", | --- | --- | | `charts()` | `list` `listAll` `create` `retrieve` `update` `delete` `copy` `archive` `unarchive` `publish` | | `templates()` | `instantiateTemplate` | -| `events()` | `list` `listAll` `create` `retrieve` `update` `delete` `updateChart` `close` `reopen` `archive` `retrieveHoldTtl` `updateHoldTtl` `listTicketReleases` `updateTicketReleases` `closeTicketRelease` `retrieveReport` `retrieveLog` | +| `events()` | `list` `listAll` `create` `retrieve` `retrieveConfigurationBinding` `updateConfigurationBinding` `update` `delete` `updateChart` `close` `reopen` `archive` `retrieveHoldTtl` `updateHoldTtl` `listTicketReleases` `updateTicketReleases` `closeTicketRelease` `retrieveReport` `retrieveLog` | | `channels()` | `list` `create` `update` `updateAssignments` `listAllocation` `retrieveAccessPreview` `retrieveReport` `pause` `unpause` `archive` `createBuyerAccessSession` `listBuyerAccessSessions` `revokeBuyerAccessSession` | | `inventory()` | `hold` `holdBestAvailable` `bookBestAvailable` `extendHold` `retrieveHold` `release` `book` `bookLabels` `boxOfficeBook` `unbook` `block` `unblock` `unblockAll` `retrieveAvailability` `updateAvailability` `listBookings` `retrieveBooking` | | `sessions()` | `createManageSession` `revokeManageSession` `createDesignerSession` `revokeDesignerSession` | diff --git a/pom.xml b/pom.xml index bac198d..b75b57a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.seatlayer seatlayer-java - 0.5.0 + 0.6.0 jar SeatLayer Java SDK diff --git a/src/main/java/io/seatlayer/Events.java b/src/main/java/io/seatlayer/Events.java index 1cc263d..cf9431b 100644 --- a/src/main/java/io/seatlayer/Events.java +++ b/src/main/java/io/seatlayer/Events.java @@ -73,6 +73,29 @@ public Map retrieve(String eventKey) { return http.get("/v1/events/" + encode(eventKey)); } + /** Reads the Event's exact immutable configuration binding and audit history. */ + public Map 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() {