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
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.seatlayer</groupId>
<artifactId>seatlayer-java</artifactId>
<version>0.5.0</version>
<version>0.6.0</version>
<packaging>jar</packaging>

<name>SeatLayer Java SDK</name>
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/io/seatlayer/Events.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,29 @@ public Map<String, Object> retrieve(String eventKey) {
return http.get("/v1/events/" + encode(eventKey));
}

/** Reads the Event's exact immutable configuration binding and audit history. */
public Map<String, Object> retrieveConfigurationBinding(String eventKey) {
return http.get("/v1/events/" + encode(eventKey) + "/event-configuration");
}

/**
* Binds an exact published configuration version, or passes {@code null} to detach.
*
* <p>This compare-and-set mutation remains single-attempt because the public operation
* does not promise exact response replay.
*/
public Map<String, Object> updateConfigurationBinding(
String eventKey, long expectedRevision, Map<String, ?> configuration) {
Map<String, Object> 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<String, Object> update(String eventKey, Map<String, Object> fields) {
return http.patch("/v1/events/" + encode(eventKey), fields);
}
Expand Down
32 changes: 32 additions & 0 deletions src/test/java/io/seatlayer/ClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Object> retrieved = sdk.events().retrieveConfigurationBinding("ev / main");
assertEquals(7L, retrieved.get("revision"));
Map<String, Object> 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() {
Expand Down
Loading