From 3ced0d44d6bf853a7bbe163c2fa857fe5ecedef9 Mon Sep 17 00:00:00 2001 From: navin10sharma <3096611+navin10sharma@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:30:13 +0530 Subject: [PATCH 1/2] feat(seasons): complete Season server SDK candidate --- CHANGELOG.md | 10 + README.md | 31 +++- client.go | 8 + seasons.go | 483 ++++++++++++++++++++++++++++++++++++++++++++++++ seasons_test.go | 123 ++++++++++++ 5 files changed, 654 insertions(+), 1 deletion(-) create mode 100644 seasons.go create mode 100644 seasons_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 88107be..b790f5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## Unreleased + +- Added source-candidate coverage for all 48 Fixed Renewable Season server + operations under `Client.Seasons`, with exact path encoding and + operation-specific retry/idempotency behavior. No module tag has been published. +- Season allocations are identity-only and the API response declares host + pricing authority. Buyer rehearsal validation sends no evidence body because + SeatLayer discovers the retained hold, booking, cancellation, and delivered + webhook chain automatically. + ## 0.6.1 - Documentation only. Refreshes the README, adds frequently asked diff --git a/README.md b/README.md index 7473e86..83d4358 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ seat inventory through one typed ticketing API client. [SeatLayer module on pkg.go.dev](https://pkg.go.dev/github.com/seatlayer/seatlayer-go) · [SeatLayer server SDK documentation](https://docs.seatlayer.io/server-sdk/install/) · -[SeatLayer reserved-seating platform](https://seatlayer.io/) · +[SeatLayer developer platform](https://seatlayer.io/developers/) · [SeatLayer JavaScript seat map SDK](https://www.npmjs.com/package/@seatlayer/js) · [SeatLayer AI Toolkit](https://github.com/seatlayer/seatlayer-ai-toolkit) @@ -87,6 +87,35 @@ treated as a transient fault to back off through. ## Test vs live +## Fixed Renewable Seasons (unpublished candidate) + +The source candidate exposes all 48 trusted organizer operations through +`client.Seasons`. It is not part of a published module tag and does not make a +production-support claim. + +After the test hold/book/cancel journey and matching webhook deliveries, +`client.Seasons.ValidateBuyerRehearsal(ctx, seasonKey)` sends no evidence body; +SeatLayer discovers the retained chain automatically. Retrieved Season holds +contain inventory identity, not an authoritative amount—your platform owns +package price, payment, order, tax, refunds, benefits, and ticket or pass delivery. + +```go +checked, err := client.Seasons.Validate(ctx, seatlayer.SeasonSelectionParams{ + SourcePerformanceGroupKeys: []string{"pg_subscription_run"}, +}) +created, err := client.Seasons.Create(ctx, seatlayer.SeasonCreateParams{ + Name: "2027 subscription", + SourcePerformanceGroupKeys: []string{"pg_subscription_run"}, + IdempotencyKey: "season-create-2027", +}) +``` + +Treat `202` as accepted work and poll `RetrieveLifecycle` with the returned +operation identity. Buyer-session minting and domain-exact booking, +cancellation, and renewal actions remain single-attempt; only declared +header-replay catalogue mutations retry automatically. + + Keys carry their own mode. `sk_test_…` keys can only touch test-mode events and `sk_live_…` only live ones; crossing them returns `403 mode_mismatch`. diff --git a/client.go b/client.go index 705a8d1..07705c0 100644 --- a/client.go +++ b/client.go @@ -59,6 +59,7 @@ type Client struct { Events *EventsService Inventory *InventoryService PerformanceGroups *PerformanceGroupsService + Seasons *SeasonsService Sessions *SessionsService Templates *TemplatesService Webhooks *WebhooksService @@ -137,6 +138,7 @@ func New(secretKey string, options ...Option) (*Client, error) { client.Events = &EventsService{client: client} client.Inventory = &InventoryService{client: client} client.PerformanceGroups = &PerformanceGroupsService{client: client} + client.Seasons = &SeasonsService{client: client} client.Sessions = &SessionsService{client: client} client.Templates = &TemplatesService{client: client} client.Webhooks = &WebhooksService{client: client} @@ -321,6 +323,12 @@ func (c *Client) postHeaderReplay( return c.do(ctx, http.MethodPost, path, nil, body, idempotencyKey, true) } +func (c *Client) mutationHeaderReplay( + ctx context.Context, method, path string, body any, idempotencyKey string, +) (map[string]any, error) { + return c.do(ctx, method, path, nil, body, idempotencyKey, true) +} + func (c *Client) put(ctx context.Context, path string, body any) (map[string]any, error) { return c.Do(ctx, http.MethodPut, path, nil, body, "") } diff --git a/seasons.go b/seasons.go new file mode 100644 index 0000000..9e20e17 --- /dev/null +++ b/seasons.go @@ -0,0 +1,483 @@ +package seatlayer + +import ( + "context" + "net/http" + "net/url" + "strconv" +) + +// SeasonsService manages Fixed Renewable Seasons from trusted server code. +// Browser selection belongs in the distinct SeasonPicker and receives only a +// scoped buyer token minted through this service. +type SeasonsService struct{ client *Client } + +type SeasonListParams struct { + WorkspaceID string + StructureState string + Limit int + Cursor string +} + +func (p *SeasonListParams) query() url.Values { + values := url.Values{} + if p == nil { + return values + } + setIfNotEmpty(values, "workspaceId", p.WorkspaceID) + setIfNotEmpty(values, "structureState", p.StructureState) + setIfNotEmpty(values, "cursor", p.Cursor) + if p.Limit > 0 { + values.Set("limit", strconv.Itoa(p.Limit)) + } + return values +} + +type SeasonSelectionParams struct { + EventKeys []string + SourcePerformanceGroupKeys []string +} + +func (p SeasonSelectionParams) body() map[string]any { + return params( + "eventKeys", seasonSliceOrNil(p.EventKeys), + "sourcePerformanceGroupKeys", seasonSliceOrNil(p.SourcePerformanceGroupKeys), + ) +} + +type SeasonCreateParams struct { + Name string + Edition NullableField[string] + EventKeys []string + SourcePerformanceGroupKeys []string + IdempotencyKey string +} + +type SeasonUpdateParams struct { + ExpectedRevision int + Name string + Edition NullableField[string] + IdempotencyKey string +} + +type SeasonPlanCreateParams struct { + Name string + EventKeys []string + SourcePerformanceGroupKeys []string + IdempotencyKey string +} + +type SeasonDuplicateToLiveParams struct { + EventKeys []string + Name string + IdempotencyKey string +} + +type SeasonBuyerAccessSessionParams struct { + AllowedOrigin string + IncludePublic bool + ExpiresInSeconds int + MaxQuantity NullableField[int] + BuyerRef NullableField[string] +} + +type SeasonCancelBookingParams struct { + CancelActionID string + BookingRef string + PlanActivationID string + RightDisposition string +} + +type SeasonHolderImportRow struct { + RowID string `json:"rowId"` + HolderRef string `json:"holderRef"` + PriorPlanActivationID string `json:"priorPlanActivationId"` + PriorContractRef string `json:"priorContractRef"` + Labels []string `json:"labels"` + ExistingBookingRef *string `json:"existingBookingRef,omitempty"` +} + +type SeasonHolderImportParams struct { + SuccessorPlanActivationID string + DryRun *bool + Rows []SeasonHolderImportRow + IdempotencyKey string +} + +type SeasonRenewalOffersParams struct { + SuccessorPlanActivationID string + DeadlineAt int64 + ContractIDs []string + IdempotencyKey string +} + +type SeasonAmendmentParams struct { + EventKey string + Kind string + StartsAt int64 + Name string + IdempotencyKey string +} + +type SeasonSupportLookupParams struct { + BookingRef string + HolderRef string +} + +func seasonPath(seasonKey, suffix string) string { + return "/v1/seasons/" + escape(seasonKey) + suffix +} + +func seasonSliceOrNil[T any](values []T) any { + if values == nil { + return nil + } + return values +} + +// List returns one cursor page of Seasons. +func (s *SeasonsService) List(ctx context.Context, p *SeasonListParams) (map[string]any, error) { + return s.client.get(ctx, "/v1/seasons", p.query()) +} + +// Validate is a read-only compatibility preflight. +func (s *SeasonsService) Validate(ctx context.Context, p SeasonSelectionParams) (map[string]any, error) { + return s.client.post(ctx, "/v1/seasons/validate", p.body(), "") +} + +func (s *SeasonsService) Create(ctx context.Context, p SeasonCreateParams) (map[string]any, error) { + body := p.SeasonSelectionParams().body() + body["name"] = p.Name + if value, present := p.Edition.requestValue(); present { + body["edition"] = value + } + return s.client.postHeaderReplay(ctx, "/v1/seasons", body, p.IdempotencyKey) +} + +func (p SeasonCreateParams) SeasonSelectionParams() SeasonSelectionParams { + return SeasonSelectionParams{ + EventKeys: p.EventKeys, SourcePerformanceGroupKeys: p.SourcePerformanceGroupKeys, + } +} + +func (s *SeasonsService) Retrieve(ctx context.Context, seasonKey string) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, ""), nil) +} + +func (s *SeasonsService) Update( + ctx context.Context, seasonKey string, p SeasonUpdateParams, +) (map[string]any, error) { + body := params("expectedRevision", p.ExpectedRevision, "name", stringOrNil(p.Name)) + if value, present := p.Edition.requestValue(); present { + body["edition"] = value + } + return s.client.mutationHeaderReplay( + ctx, http.MethodPatch, seasonPath(seasonKey, ""), body, p.IdempotencyKey) +} + +func (s *SeasonsService) Delete( + ctx context.Context, seasonKey, idempotencyKey string, +) error { + _, err := s.client.mutationHeaderReplay( + ctx, http.MethodDelete, seasonPath(seasonKey, ""), nil, idempotencyKey) + return err +} + +func (s *SeasonsService) Activate( + ctx context.Context, seasonKey string, expectedRevision int, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/activate"), + params("expectedRevision", expectedRevision), "") +} + +func (s *SeasonsService) Close( + ctx context.Context, seasonKey string, expectedRevision int, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/close"), + params("expectedRevision", expectedRevision), "") +} + +func (s *SeasonsService) Archive( + ctx context.Context, seasonKey string, expectedRevision int, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/archive"), + params("expectedRevision", expectedRevision), "") +} + +func (s *SeasonsService) RetrieveLifecycle( + ctx context.Context, seasonKey, operationID string, +) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/lifecycle/"+escape(operationID)), nil) +} + +func (s *SeasonsService) CreatePlan( + ctx context.Context, seasonKey string, p SeasonPlanCreateParams, +) (map[string]any, error) { + body := SeasonSelectionParams{ + EventKeys: p.EventKeys, SourcePerformanceGroupKeys: p.SourcePerformanceGroupKeys, + }.body() + body["name"] = p.Name + return s.client.postHeaderReplay(ctx, seasonPath(seasonKey, "/plans"), body, p.IdempotencyKey) +} + +func (s *SeasonsService) RetrievePlan( + ctx context.Context, seasonKey, planKey string, +) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/plans/"+escape(planKey)), nil) +} + +func (s *SeasonsService) PublishPlan( + ctx context.Context, seasonKey, planKey string, expectedRevision int, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/plans/"+escape(planKey)+"/publish"), + params("expectedRevision", expectedRevision), "") +} + +func (s *SeasonsService) SupersedePlan( + ctx context.Context, seasonKey, planKey string, expectedRevision int, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/plans/"+escape(planKey)+"/supersede"), + params("expectedRevision", expectedRevision), "") +} + +func (s *SeasonsService) sales( + ctx context.Context, seasonKey, action string, expectedRevision int, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/sales/"+action), + params("expectedRevision", expectedRevision), "") +} + +func (s *SeasonsService) OpenSales(ctx context.Context, seasonKey string, expectedRevision int) (map[string]any, error) { + return s.sales(ctx, seasonKey, "open", expectedRevision) +} + +func (s *SeasonsService) PauseSales(ctx context.Context, seasonKey string, expectedRevision int) (map[string]any, error) { + return s.sales(ctx, seasonKey, "pause", expectedRevision) +} + +func (s *SeasonsService) ResumeSales(ctx context.Context, seasonKey string, expectedRevision int) (map[string]any, error) { + return s.sales(ctx, seasonKey, "resume", expectedRevision) +} + +func (s *SeasonsService) EndSales(ctx context.Context, seasonKey string, expectedRevision int) (map[string]any, error) { + return s.sales(ctx, seasonKey, "end", expectedRevision) +} + +func (s *SeasonsService) DuplicateToLive( + ctx context.Context, seasonKey string, p SeasonDuplicateToLiveParams, +) (map[string]any, error) { + return s.client.postHeaderReplay(ctx, seasonPath(seasonKey, "/duplicate-to-live"), + params("eventKeys", p.EventKeys, "name", stringOrNil(p.Name)), p.IdempotencyKey) +} + +// CreateBuyerAccessSession reveals a show-once bearer and is deliberately single-attempt. +func (s *SeasonsService) CreateBuyerAccessSession( + ctx context.Context, seasonKey string, p SeasonBuyerAccessSessionParams, +) (map[string]any, error) { + body := params( + "allowedOrigin", p.AllowedOrigin, + "includePublic", p.IncludePublic, + "expiresInSeconds", intOrNil(p.ExpiresInSeconds), + ) + if value, present := p.MaxQuantity.requestValue(); present { + body["maxQuantity"] = value + } + if value, present := p.BuyerRef.requestValue(); present { + body["buyerRef"] = value + } + return s.client.post(ctx, seasonPath(seasonKey, "/buyer-access-sessions"), body, "") +} + +func (s *SeasonsService) ListBuyerAccessSessions( + ctx context.Context, seasonKey string, limit int, +) (map[string]any, error) { + query := url.Values{} + if limit > 0 { + query.Set("limit", strconv.Itoa(limit)) + } + return s.client.get(ctx, seasonPath(seasonKey, "/buyer-access-sessions"), query) +} + +func (s *SeasonsService) RevokeBuyerAccessSession( + ctx context.Context, seasonKey, sessionID string, +) (map[string]any, error) { + return s.client.delete(ctx, seasonPath(seasonKey, "/buyer-access-sessions/"+escape(sessionID))) +} + +func (s *SeasonsService) RetrieveHold( + ctx context.Context, seasonKey, operationID string, +) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/holds/"+escape(operationID)), nil) +} + +func (s *SeasonsService) BookHold( + ctx context.Context, seasonKey, operationID, bookActionID, bookingRef string, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/holds/"+escape(operationID)+"/book"), + params("bookActionId", bookActionID, "bookingRef", bookingRef), "") +} + +func (s *SeasonsService) RetrieveBooking( + ctx context.Context, seasonKey, actionID string, +) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/bookings/"+escape(actionID)), nil) +} + +func (s *SeasonsService) CancelBooking( + ctx context.Context, seasonKey, actionID string, p SeasonCancelBookingParams, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/bookings/"+escape(actionID)+"/cancel"), + params( + "cancelActionId", p.CancelActionID, + "bookingRef", p.BookingRef, + "planActivationId", p.PlanActivationID, + "rightDisposition", p.RightDisposition, + ), "") +} + +func (s *SeasonsService) ValidateBuyerRehearsal( + ctx context.Context, seasonKey string, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/buyer-rehearsals/validate"), nil, "") +} + +func (s *SeasonsService) CreateHolderImport( + ctx context.Context, seasonKey string, p SeasonHolderImportParams, +) (map[string]any, error) { + body := params( + "successorPlanActivationId", p.SuccessorPlanActivationID, + "dryRun", p.DryRun, + "rows", p.Rows, + ) + return s.client.postHeaderReplay(ctx, seasonPath(seasonKey, "/imports"), body, p.IdempotencyKey) +} + +func (s *SeasonsService) RetrieveHolderImport( + ctx context.Context, seasonKey, importID string, +) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/imports/"+escape(importID)), nil) +} + +func (s *SeasonsService) CreateRenewalOffers( + ctx context.Context, seasonKey string, p SeasonRenewalOffersParams, +) (map[string]any, error) { + return s.client.postHeaderReplay(ctx, seasonPath(seasonKey, "/renewal-offers"), + params( + "successorPlanActivationId", stringOrNil(p.SuccessorPlanActivationID), + "deadlineAt", p.DeadlineAt, + "contractIds", seasonSliceOrNil(p.ContractIDs), + ), p.IdempotencyKey) +} + +func (s *SeasonsService) ListRenewalOffers(ctx context.Context, seasonKey string) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/renewal-offers"), nil) +} + +func (s *SeasonsService) RetrieveRenewalOffer( + ctx context.Context, seasonKey, offerID string, +) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/renewal-offers/"+escape(offerID)), nil) +} + +func (s *SeasonsService) ExtendRenewalOffer( + ctx context.Context, seasonKey, offerID string, deadlineAt int64, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/renewal-offers/"+escape(offerID)+"/extend"), + params("deadlineAt", deadlineAt), "") +} + +func (s *SeasonsService) InspectRenewalOffer( + ctx context.Context, seasonKey, offerID string, +) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/renewal-offers/"+escape(offerID)+"/inspect"), nil) +} + +func (s *SeasonsService) CommitRenewalOffer( + ctx context.Context, seasonKey, offerID, commitActionID, orderRef, bookingRef, planActivationID string, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/renewal-offers/"+escape(offerID)+"/commit"), + params( + "commitActionId", commitActionID, + "orderRef", orderRef, + "bookingRef", bookingRef, + "planActivationId", planActivationID, + ), "") +} + +func (s *SeasonsService) DeclineRenewalOffer( + ctx context.Context, seasonKey, offerID string, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/renewal-offers/"+escape(offerID)+"/decline"), + map[string]any{}, "") +} + +func (s *SeasonsService) ReleaseRenewalOffer( + ctx context.Context, seasonKey, offerID string, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/renewal-offers/"+escape(offerID)+"/release"), + map[string]any{}, "") +} + +func (s *SeasonsService) ListOccurrences(ctx context.Context, seasonKey string) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/occurrences"), nil) +} + +func (s *SeasonsService) CreateAmendment( + ctx context.Context, seasonKey string, p SeasonAmendmentParams, +) (map[string]any, error) { + return s.client.postHeaderReplay(ctx, seasonPath(seasonKey, "/amendments"), + params( + "eventKey", p.EventKey, + "kind", p.Kind, + "startsAt", int64OrNil(p.StartsAt), + "name", stringOrNil(p.Name), + ), p.IdempotencyKey) +} + +func (s *SeasonsService) ListAmendments(ctx context.Context, seasonKey string) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/amendments"), nil) +} + +func (s *SeasonsService) RetrieveAmendment( + ctx context.Context, seasonKey, amendmentID string, +) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/amendments/"+escape(amendmentID)), nil) +} + +func (s *SeasonsService) RetrieveReport(ctx context.Context, seasonKey string) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/reports"), nil) +} + +func (s *SeasonsService) ListOperations(ctx context.Context, seasonKey string) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/operations"), nil) +} + +func (s *SeasonsService) RetrieveSupportLookup( + ctx context.Context, seasonKey string, p *SeasonSupportLookupParams, +) (map[string]any, error) { + query := url.Values{} + if p != nil { + setIfNotEmpty(query, "bookingRef", p.BookingRef) + setIfNotEmpty(query, "holderRef", p.HolderRef) + } + return s.client.get(ctx, seasonPath(seasonKey, "/support-lookups"), query) +} + +func (s *SeasonsService) ListOutbox(ctx context.Context, seasonKey string) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/outbox"), nil) +} + +func (s *SeasonsService) ReplayOutbox( + ctx context.Context, seasonKey, occurrenceID string, +) (map[string]any, error) { + return s.client.post(ctx, seasonPath(seasonKey, "/outbox/"+escape(occurrenceID)+"/replay"), + map[string]any{}, "") +} + +func (s *SeasonsService) ListAudit(ctx context.Context, seasonKey string) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/audit"), nil) +} + +func (s *SeasonsService) ExportSupportSnapshot(ctx context.Context, seasonKey string) (map[string]any, error) { + return s.client.get(ctx, seasonPath(seasonKey, "/export"), nil) +} diff --git a/seasons_test.go b/seasons_test.go new file mode 100644 index 0000000..cf82dbe --- /dev/null +++ b/seasons_test.go @@ -0,0 +1,123 @@ +package seatlayer + +import ( + "context" + "net/http" + "testing" +) + +func TestSeasonsMapAll48OperationsAndReplayClasses(t *testing.T) { + responses := make([]stub, 48) + for i := range responses { + responses[i] = stub{status: 200, body: `{}`} + } + client, calls := newTestClient(t, responses) + ctx := context.Background() + key := "sea/a" + + _, _ = client.Seasons.List(ctx, &SeasonListParams{WorkspaceID: "ws 1", StructureState: "draft", Limit: 20, Cursor: "c/1"}) + _, _ = client.Seasons.Validate(ctx, SeasonSelectionParams{SourcePerformanceGroupKeys: []string{"pg_1"}}) + _, _ = client.Seasons.Create(ctx, SeasonCreateParams{Name: "Series", EventKeys: []string{"ev_1", "ev_2"}, IdempotencyKey: "create-1"}) + _, _ = client.Seasons.Retrieve(ctx, key) + _, _ = client.Seasons.Update(ctx, key, SeasonUpdateParams{ExpectedRevision: 1, Name: "Series 2", IdempotencyKey: "update-1"}) + _ = client.Seasons.Delete(ctx, key, "delete-1") + _, _ = client.Seasons.Activate(ctx, key, 1) + _, _ = client.Seasons.Close(ctx, key, 2) + _, _ = client.Seasons.Archive(ctx, key, 3) + _, _ = client.Seasons.RetrieveLifecycle(ctx, key, "life/1") + _, _ = client.Seasons.CreatePlan(ctx, key, SeasonPlanCreateParams{Name: "Plan", EventKeys: []string{"ev_1", "ev_2"}, IdempotencyKey: "plan-1"}) + _, _ = client.Seasons.RetrievePlan(ctx, key, "plan/1") + _, _ = client.Seasons.PublishPlan(ctx, key, "plan/1", 2) + _, _ = client.Seasons.SupersedePlan(ctx, key, "plan/1", 3) + _, _ = client.Seasons.OpenSales(ctx, key, 3) + _, _ = client.Seasons.PauseSales(ctx, key, 4) + _, _ = client.Seasons.ResumeSales(ctx, key, 5) + _, _ = client.Seasons.EndSales(ctx, key, 6) + _, _ = client.Seasons.DuplicateToLive(ctx, key, SeasonDuplicateToLiveParams{EventKeys: []string{"live_1", "live_2"}, IdempotencyKey: "live-1"}) + _, _ = client.Seasons.CreateBuyerAccessSession(ctx, key, SeasonBuyerAccessSessionParams{AllowedOrigin: "https://tickets.example", IncludePublic: true}) + _, _ = client.Seasons.ListBuyerAccessSessions(ctx, key, 10) + _, _ = client.Seasons.RevokeBuyerAccessSession(ctx, key, "session/1") + _, _ = client.Seasons.RetrieveHold(ctx, key, "hold/1") + _, _ = client.Seasons.BookHold(ctx, key, "hold/1", "book_1", "order_1") + _, _ = client.Seasons.RetrieveBooking(ctx, key, "book/1") + _, _ = client.Seasons.CancelBooking(ctx, key, "book/1", SeasonCancelBookingParams{CancelActionID: "cancel_1", BookingRef: "order_1", PlanActivationID: "pa_1", RightDisposition: "release"}) + _, _ = client.Seasons.ValidateBuyerRehearsal(ctx, key) + _, _ = client.Seasons.CreateHolderImport(ctx, key, SeasonHolderImportParams{SuccessorPlanActivationID: "pa_1", Rows: []SeasonHolderImportRow{}, IdempotencyKey: "import-1"}) + _, _ = client.Seasons.RetrieveHolderImport(ctx, key, "import/1") + _, _ = client.Seasons.CreateRenewalOffers(ctx, key, SeasonRenewalOffersParams{DeadlineAt: 123, IdempotencyKey: "offers-1"}) + _, _ = client.Seasons.ListRenewalOffers(ctx, key) + _, _ = client.Seasons.RetrieveRenewalOffer(ctx, key, "offer/1") + _, _ = client.Seasons.ExtendRenewalOffer(ctx, key, "offer/1", 456) + _, _ = client.Seasons.InspectRenewalOffer(ctx, key, "offer/1") + _, _ = client.Seasons.CommitRenewalOffer(ctx, key, "offer/1", "commit_1", "order_1", "book_1", "pa_1") + _, _ = client.Seasons.DeclineRenewalOffer(ctx, key, "offer/1") + _, _ = client.Seasons.ReleaseRenewalOffer(ctx, key, "offer/1") + _, _ = client.Seasons.ListOccurrences(ctx, key) + _, _ = client.Seasons.CreateAmendment(ctx, key, SeasonAmendmentParams{EventKey: "ev_1", Kind: "reschedule", IdempotencyKey: "amend-1"}) + _, _ = client.Seasons.ListAmendments(ctx, key) + _, _ = client.Seasons.RetrieveAmendment(ctx, key, "amend/1") + _, _ = client.Seasons.RetrieveReport(ctx, key) + _, _ = client.Seasons.ListOperations(ctx, key) + _, _ = client.Seasons.RetrieveSupportLookup(ctx, key, &SeasonSupportLookupParams{HolderRef: "holder a/b"}) + _, _ = client.Seasons.ListOutbox(ctx, key) + _, _ = client.Seasons.ReplayOutbox(ctx, key, "occurrence/1") + _, _ = client.Seasons.ListAudit(ctx, key) + _, _ = client.Seasons.ExportSupportSnapshot(ctx, key) + + want := []string{ + "GET /v1/seasons?cursor=c%2F1&limit=20&structureState=draft&workspaceId=ws+1", + "POST /v1/seasons/validate", "POST /v1/seasons", "GET /v1/seasons/sea%2Fa", + "PATCH /v1/seasons/sea%2Fa", "DELETE /v1/seasons/sea%2Fa", + "POST /v1/seasons/sea%2Fa/activate", "POST /v1/seasons/sea%2Fa/close", + "POST /v1/seasons/sea%2Fa/archive", "GET /v1/seasons/sea%2Fa/lifecycle/life%2F1", + "POST /v1/seasons/sea%2Fa/plans", "GET /v1/seasons/sea%2Fa/plans/plan%2F1", + "POST /v1/seasons/sea%2Fa/plans/plan%2F1/publish", "POST /v1/seasons/sea%2Fa/plans/plan%2F1/supersede", + "POST /v1/seasons/sea%2Fa/sales/open", "POST /v1/seasons/sea%2Fa/sales/pause", + "POST /v1/seasons/sea%2Fa/sales/resume", "POST /v1/seasons/sea%2Fa/sales/end", + "POST /v1/seasons/sea%2Fa/duplicate-to-live", "POST /v1/seasons/sea%2Fa/buyer-access-sessions", + "GET /v1/seasons/sea%2Fa/buyer-access-sessions?limit=10", + "DELETE /v1/seasons/sea%2Fa/buyer-access-sessions/session%2F1", + "GET /v1/seasons/sea%2Fa/holds/hold%2F1", "POST /v1/seasons/sea%2Fa/holds/hold%2F1/book", + "GET /v1/seasons/sea%2Fa/bookings/book%2F1", "POST /v1/seasons/sea%2Fa/bookings/book%2F1/cancel", + "POST /v1/seasons/sea%2Fa/buyer-rehearsals/validate", "POST /v1/seasons/sea%2Fa/imports", + "GET /v1/seasons/sea%2Fa/imports/import%2F1", "POST /v1/seasons/sea%2Fa/renewal-offers", + "GET /v1/seasons/sea%2Fa/renewal-offers", "GET /v1/seasons/sea%2Fa/renewal-offers/offer%2F1", + "POST /v1/seasons/sea%2Fa/renewal-offers/offer%2F1/extend", + "GET /v1/seasons/sea%2Fa/renewal-offers/offer%2F1/inspect", + "POST /v1/seasons/sea%2Fa/renewal-offers/offer%2F1/commit", + "POST /v1/seasons/sea%2Fa/renewal-offers/offer%2F1/decline", + "POST /v1/seasons/sea%2Fa/renewal-offers/offer%2F1/release", + "GET /v1/seasons/sea%2Fa/occurrences", "POST /v1/seasons/sea%2Fa/amendments", + "GET /v1/seasons/sea%2Fa/amendments", "GET /v1/seasons/sea%2Fa/amendments/amend%2F1", + "GET /v1/seasons/sea%2Fa/reports", "GET /v1/seasons/sea%2Fa/operations", + "GET /v1/seasons/sea%2Fa/support-lookups?holderRef=holder+a%2Fb", "GET /v1/seasons/sea%2Fa/outbox", + "POST /v1/seasons/sea%2Fa/outbox/occurrence%2F1/replay", "GET /v1/seasons/sea%2Fa/audit", + "GET /v1/seasons/sea%2Fa/export", + } + if len(*calls) != len(want) { + t.Fatalf("calls = %d, want %d", len(*calls), len(want)) + } + if got := call(t, calls, 26).body; got != "" { + t.Fatalf("rehearsal body = %q, want empty", got) + } + replay := map[int]bool{2: true, 4: true, 5: true, 10: true, 18: true, 27: true, 29: true, 38: true} + for i, expected := range want { + got := call(t, calls, i) + actual := got.method + " " + got.escapedPath + if got.query != "" { + actual += "?" + got.query + } + if actual != expected { + t.Errorf("call %d = %q, want %q", i, actual, expected) + } + if replay[i] && got.header.Get("Idempotency-Key") == "" { + t.Errorf("call %d missing Idempotency-Key", i) + } + if !replay[i] && got.header.Get("Idempotency-Key") != "" { + t.Errorf("call %d unexpectedly has Idempotency-Key", i) + } + } + if call(t, calls, 4).method != http.MethodPatch || call(t, calls, 5).method != http.MethodDelete { + t.Fatal("update/delete methods drifted") + } +} From 4e0169bc37333929298aea04f0084d7e75c5955b Mon Sep 17 00:00:00 2001 From: navin10sharma <3096611+navin10sharma@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:34:46 +0530 Subject: [PATCH 2/2] release: prepare 0.7.0 --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b790f5a..98b30ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ # Changelog -## Unreleased +## 0.7.0 — 2026-08-30 -- Added source-candidate coverage for all 48 Fixed Renewable Season server +- Added coverage for all 48 Fixed Renewable Season server operations under `Client.Seasons`, with exact path encoding and - operation-specific retry/idempotency behavior. No module tag has been published. + operation-specific retry/idempotency behavior. - Season allocations are identity-only and the API response declares host pricing authority. Buyer rehearsal validation sends no evidence body because SeatLayer discovers the retained hold, booking, cancellation, and delivered