diff --git a/CHANGELOG.md b/CHANGELOG.md index 2448c95..4e6d3f1 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 b62f496..b1ecf24 100644 --- a/README.md +++ b/README.md @@ -290,7 +290,7 @@ suite runs without a network. | --- | --- | | `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` | `listChannels` `createChannel` `updateChannel` `updateAssignments` `listAllocation` `retrieveAccessPreview` `retrieveReport` `pause` `unpause` `archive` `createBuyerAccessSession` `listBuyerAccessSessions` `revokeBuyerAccessSession` | | `inventory` | `hold` `holdBestAvailable` `bookBestAvailable` `extendHold` `retrieveHold` `release` `book` `boxOfficeBook` `unbook` `block` `unblock` `unblockAll` `retrieveAvailability` `updateAvailability` `listBookings` `retrieveBooking` | | `sessions` | `createManageSession` `revokeManageSession` `createDesignerSession` `revokeDesignerSession` | diff --git a/src/Resources/Events.php b/src/Resources/Events.php index c96e37d..a968f88 100644 --- a/src/Resources/Events.php +++ b/src/Resources/Events.php @@ -116,6 +116,42 @@ public function retrieve(string $eventKey): array return (array) $this->http->get('/v1/events/' . HttpClient::encode($eventKey)); } + /** Read the Event's exact immutable configuration binding and audit history. + * + * @return array + */ + public function retrieveConfigurationBinding(string $eventKey): array + { + /** @var array */ + return (array) $this->http->get( + '/v1/events/' . HttpClient::encode($eventKey) . '/event-configuration', + ); + } + + /** + * Bind an exact published version, or pass null to detach. + * + * This compare-and-set mutation remains single-attempt because the public + * operation does not promise exact response replay. + * + * @param array{id: string, version: int}|null $configuration + * @return array + */ + public function updateConfigurationBinding( + string $eventKey, + int $expectedRevision, + ?array $configuration, + ): array { + /** @var array */ + return (array) $this->http->put( + '/v1/events/' . HttpClient::encode($eventKey) . '/event-configuration', + [ + 'expectedRevision' => $expectedRevision, + 'configuration' => $configuration, + ], + ); + } + /** * @param array $fields * @return array diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 02fb474..33df2f9 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -101,6 +101,52 @@ public function testPercentEncodesPathParameters(): void self::assertSame('https://api.seatlayer.io/v1/events/ev%2F..%2Fadmin', $this->call(0)['url']); } + public function testEventConfigurationBindingRoutesAndBodies(): void + { + $binding = [ + 'configuration' => ['id' => 'ec_touring', 'version' => 3], + 'revision' => 7, + 'changedBy' => 'api-key:key_1', + 'changedAt' => 123, + 'audit' => [[ + 'id' => 'eca_1', + 'from' => null, + 'to' => ['id' => 'ec_touring', 'version' => 3], + 'revision' => 7, + 'actor' => 'api-key:key_1', + 'createdAt' => 123, + ]], + ]; + $sdk = $this->client([ + ['status' => 200, 'body' => $binding], + ['status' => 200, 'body' => $binding], + ['status' => 200, 'body' => [...$binding, 'configuration' => null, 'revision' => 8]], + ]); + + $retrieved = $sdk->events->retrieveConfigurationBinding('ev / main'); + self::assertSame(['id' => 'ec_touring', 'version' => 3], $retrieved['audit'][0]['to']); + $sdk->events->updateConfigurationBinding( + 'ev / main', + 6, + ['id' => 'ec_touring', 'version' => 3], + ); + $sdk->events->updateConfigurationBinding('ev / main', 7, null); + + $url = 'https://api.seatlayer.io/v1/events/ev%20%2F%20main/event-configuration'; + self::assertSame([$url, $url, $url], array_column($this->calls, 'url')); + self::assertSame('GET', $this->call(0)['method']); + self::assertSame([ + 'expectedRevision' => 6, + 'configuration' => ['id' => 'ec_touring', 'version' => 3], + ], json_decode((string) $this->call(1)['body'], true)); + self::assertSame([ + 'expectedRevision' => 7, + 'configuration' => null, + ], json_decode((string) $this->call(2)['body'], true)); + self::assertArrayNotHasKey('Idempotency-Key', $this->call(1)['headers']); + self::assertArrayNotHasKey('Idempotency-Key', $this->call(2)['headers']); + } + public function testTemplateInstantiationUsesAnObjectBodyAndReplaysItsResponse(): void { $sdk = $this->client([