diff --git a/CHANGELOG.md b/CHANGELOG.md index f9e776e..b2079b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog -## Unreleased +## 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 + `events.update_configuration_binding`. Updates remain deliberately single-attempt. ## 0.5.0 — 2026-08-21 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/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 diff --git a/spec/client_spec.rb b/spec/client_spec.rb index 70a161e..2477f9c 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -535,6 +535,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"}}' },