From 36115c272ff9d68b04a436d1855848c35fd6f0f1 Mon Sep 17 00:00:00 2001 From: navin10sharma <3096611+navin10sharma@users.noreply.github.com> Date: Sun, 23 Aug 2026 13:58:06 +0530 Subject: [PATCH 1/3] feat(events): add configuration binding --- CHANGELOG.md | 4 +++ README.md | 2 +- lib/seatlayer/resources.rb | 15 +++++++++++ spec/client_spec.rb | 55 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52346ba..a50d4ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Added exact immutable Event configuration binding reads and compare-and-set + attach/detach through `events.retrieve_configuration_binding` and + `events.update_configuration_binding`. Updates remain deliberately single-attempt. + - Added `templates.instantiate_template` and the ticket-release lifecycle on `events` (`list_ticket_releases`, `update_ticket_releases`, and `close_ticket_release`). Template instantiation sends `{}` when no overrides diff --git a/README.md b/README.md index ef5fd4c..d5c2837 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ client.request("POST", "/v1/events/ev_1/some-new-route", body: { "qty" => 2 }) | --- | --- | | `charts` | `list` `list_all` `create` `retrieve` `update` `delete` `copy` `archive` `unarchive` `publish` | | `templates` | `instantiate_template` | -| `events` | `list` `list_all` `create` `retrieve` `update` `delete` `update_poster` `delete_poster` `update_chart` `close` `reopen` `archive` `retrieve_hold_ttl` `update_hold_ttl` `list_ticket_releases` `update_ticket_releases` `close_ticket_release` `retrieve_report` `retrieve_log` | +| `events` | `list` `list_all` `create` `retrieve` `retrieve_configuration_binding` `update_configuration_binding` `update` `delete` `update_poster` `delete_poster` `update_chart` `close` `reopen` `archive` `retrieve_hold_ttl` `update_hold_ttl` `list_ticket_releases` `update_ticket_releases` `close_ticket_release` `retrieve_report` `retrieve_log` | | `inventory` | `hold` `hold_best_available` `book_best_available` `extend_hold` `retrieve_hold` `release` `book` `box_office_book` `unbook` `list_bookings` `retrieve_booking` `block` `unblock` `unblock_all` `retrieve_availability` `update_availability` | | `channels` | `list_channels` `create_channel` `update_channel` `update_assignments` `list_allocation` `retrieve_access_preview` `retrieve_report` `pause` `unpause` `archive` `create_buyer_access_session` `list_buyer_access_sessions` `revoke_buyer_access_session` `create_access_link` `list_access_links` `rotate_access_link` `revoke_access_link` | | `sessions` | `create_manage_session` `revoke_manage_session` `create_designer_session` `revoke_designer_session` | diff --git a/lib/seatlayer/resources.rb b/lib/seatlayer/resources.rb index 7ab3055..64b56d3 100644 --- a/lib/seatlayer/resources.rb +++ b/lib/seatlayer/resources.rb @@ -196,6 +196,21 @@ def retrieve(event_key) @client.get("/v1/events/#{encode(event_key)}") end + # Read the Event's exact immutable configuration selection and audit trail. + def retrieve_configuration_binding(event_key) + @client.get("/v1/events/#{encode(event_key)}/event-configuration") + end + + # Attach an exact published configuration version, or pass +nil+ to detach. + # The expected revision prevents one administrator from silently overwriting + # another, and the mutation remains deliberately single-attempt. + def update_configuration_binding(event_key, expected_revision:, configuration:) + @client.put( + "/v1/events/#{encode(event_key)}/event-configuration", + { "expectedRevision" => expected_revision, "configuration" => configuration } + ) + end + def update(event_key, fields) @client.patch("/v1/events/#{encode(event_key)}", fields) end diff --git a/spec/client_spec.rb b/spec/client_spec.rb index dd567ad..17b64b4 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -470,6 +470,61 @@ expect(transport.calls[2].headers).not_to have_key("Idempotency-Key") end + it "reads, attaches, and explicitly detaches an Event configuration" do + binding = { + "configuration" => { "id" => "ec_touring", "version" => 3 }, + "revision" => 7, "changedBy" => "api-key:key_1", "changedAt" => 123, + "audit" => [{ "id" => "eca_1", "from" => nil, + "to" => { "id" => "ec_touring", "version" => 3 }, + "revision" => 7, "actor" => "api-key:key_1", "createdAt" => 123 }] + } + detached_body = '{"configuration":null,"revision":8,' \ + '"changedBy":null,"changedAt":null,"audit":[]}' + client, transport = build_client([ + { status: 200, body: JSON.generate(binding) }, + { status: 200, body: JSON.generate(binding) }, + { status: 200, body: detached_body } + ]) + + retrieved = client.events.retrieve_configuration_binding("ev / main") + attached = client.events.update_configuration_binding( + "ev / main", expected_revision: 6, + configuration: { "id" => "ec_touring", "version" => 3 } + ) + detached = client.events.update_configuration_binding( + "ev / main", expected_revision: 7, configuration: nil + ) + + expect(retrieved.fetch("audit").first.fetch("from")).to be_nil + expect(attached.dig("configuration", "id")).to eq("ec_touring") + expect(detached.fetch("configuration")).to be_nil + expect(transport.calls.map(&:url)).to all( + end_with("/v1/events/ev%20%2F%20main/event-configuration") + ) + expect(transport.calls.map(&:http_method)).to eq(%w[GET PUT PUT]) + expect(JSON.parse(transport.calls[1].body)).to eq( + "expectedRevision" => 6, + "configuration" => { "id" => "ec_touring", "version" => 3 } + ) + expect(JSON.parse(transport.calls[2].body)).to eq( + "expectedRevision" => 7, "configuration" => nil + ) + expect(transport.calls[1].headers).not_to have_key("Idempotency-Key") + expect(transport.calls[2].headers).not_to have_key("Idempotency-Key") + + retry_client, retry_transport = build_client([ + { status: 429, + body: '{"error":"rate_limited"}', + headers: { "retry-after" => "0" } } + ]) + expect do + retry_client.events.update_configuration_binding( + "ev_1", expected_revision: 1, configuration: nil + ) + end.to raise_error(SeatLayer::RateLimitError) + expect(retry_transport.calls.length).to eq(1) + end + it "sends the expanded event, hold, and block fields" do client, transport = build_client([ { status: 201, body: '{"meta":{"key":"ev_1"}}' }, From 36e5cf67f613bf449ac70ab0f759b9564de79668 Mon Sep 17 00:00:00 2001 From: navin10sharma <3096611+navin10sharma@users.noreply.github.com> Date: Sun, 23 Aug 2026 14:30:32 +0530 Subject: [PATCH 2/3] chore(release): prepare v0.5.0 --- CHANGELOG.md | 2 +- lib/seatlayer/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a50d4ce..ddb49ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 0.5.0 — 2026-08-23 - Added exact immutable Event configuration binding reads and compare-and-set attach/detach through `events.retrieve_configuration_binding` and diff --git a/lib/seatlayer/version.rb b/lib/seatlayer/version.rb index 50771a1..208ee73 100644 --- a/lib/seatlayer/version.rb +++ b/lib/seatlayer/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SeatLayer - VERSION = "0.4.0" + VERSION = "0.5.0" end From 0ee97dc4f981f77a108c55f99bb14262b9752efb Mon Sep 17 00:00:00 2001 From: navin10sharma <3096611+navin10sharma@users.noreply.github.com> Date: Sun, 23 Aug 2026 14:35:53 +0530 Subject: [PATCH 3/3] chore(release): move configuration binding to v0.6.0 --- CHANGELOG.md | 2 +- lib/seatlayer/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4f4106..b2079b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.5.0 — 2026-08-23 +## 0.6.0 — 2026-08-23 - Added exact immutable Event configuration binding reads and compare-and-set attach/detach through `events.retrieve_configuration_binding` and diff --git a/lib/seatlayer/version.rb b/lib/seatlayer/version.rb index 208ee73..4220ae0 100644 --- a/lib/seatlayer/version.rb +++ b/lib/seatlayer/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module SeatLayer - VERSION = "0.5.0" + VERSION = "0.6.0" end