seatlayer 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +109 -0
- data/README.md +167 -42
- data/lib/seatlayer/event_series.rb +78 -0
- data/lib/seatlayer/holders.rb +109 -0
- data/lib/seatlayer/http_client.rb +29 -14
- data/lib/seatlayer/inventory.rb +19 -2
- data/lib/seatlayer/orders.rb +75 -0
- data/lib/seatlayer/resources.rb +103 -2
- data/lib/seatlayer/seasons.rb +432 -11
- data/lib/seatlayer/version.rb +1 -1
- data/lib/seatlayer.rb +7 -1
- metadata +6 -2
data/lib/seatlayer/seasons.rb
CHANGED
|
@@ -5,7 +5,36 @@ module SeatLayer
|
|
|
5
5
|
#
|
|
6
6
|
# Browser selection belongs in the distinct SeasonPicker. Give it only the
|
|
7
7
|
# show-once scoped token returned by +create_season_buyer_access_session+.
|
|
8
|
+
#
|
|
9
|
+
# One thin method per public Season operation (90 of them), so the class is
|
|
10
|
+
# long by design rather than doing too much.
|
|
11
|
+
# rubocop:disable-next Metrics/ClassLength
|
|
8
12
|
class Seasons < Resource
|
|
13
|
+
# The job voiding a cancelled date's tickets: "sbj_void_" and the cancel's
|
|
14
|
+
# "amendmentId". Cancelling a date starts it (a repeated cancel joins it);
|
|
15
|
+
# follow it with +retrieve_season_bulk_job+. Each Season ticket it voids is
|
|
16
|
+
# told by "season.ticket.voided" with reason "date_cancelled".
|
|
17
|
+
def self.date_cancel_void_job_id(amendment_id)
|
|
18
|
+
"sbj_void_#{amendment_id}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The most rows one +create_season_holder_import+ call takes for a package of
|
|
22
|
+
# +dates+ dates. Each row is fenced on every date: 100 up to 20 dates, then
|
|
23
|
+
# floor(2300 / (dates + 3)), 43 at 50. A call with more answers 422
|
|
24
|
+
# invalid_rows with "maxRows".
|
|
25
|
+
def self.holder_import_rows_per_call(dates)
|
|
26
|
+
(2300 / ([dates, 1].max + 3)).floor.clamp(1, 100)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# True for the 202 a season-seats season's date cancel (+create_season_amendment+)
|
|
30
|
+
# or past date (+mark_season_performance_past+) answers while the date is still
|
|
31
|
+
# leaving every season seat: "state" "finishing", "code" "block_date_leave_pending",
|
|
32
|
+
# with "eventKey", "shardsLeft" and "shardCount". It finishes by itself: send the
|
|
33
|
+
# same request again after Retry-After to read how it ended.
|
|
34
|
+
def self.date_leave_pending?(answer)
|
|
35
|
+
answer.is_a?(Hash) && answer["state"] == "finishing" && answer["code"] == "block_date_leave_pending"
|
|
36
|
+
end
|
|
37
|
+
|
|
9
38
|
def list_seasons(workspace_id: nil, structure_state: nil, limit: nil, cursor: nil)
|
|
10
39
|
@client.get("/v1/seasons", compact({ "workspaceId" => workspace_id,
|
|
11
40
|
"structureState" => structure_state,
|
|
@@ -18,10 +47,18 @@ module SeatLayer
|
|
|
18
47
|
selection(event_keys, source_performance_group_keys))
|
|
19
48
|
end
|
|
20
49
|
|
|
50
|
+
# Create a draft Season. +single_tickets+ true makes each date also sell its own
|
|
51
|
+
# single tickets once the Season is on sale; the default is Season only.
|
|
52
|
+
# +inventory_mode+ "block" turns season seats on for this Season; the default is
|
|
53
|
+
# "per_hold", and anything else is refused with 422 invalid_inventory_mode. Every
|
|
54
|
+
# Season answer carries "inventoryMode": how the season's seats are decided, "block"
|
|
55
|
+
# once season seats are turned on for it (inventoryMode "block" on create or update)
|
|
56
|
+
# or it sells from its block, else "per_hold".
|
|
21
57
|
def create_season(name:, event_keys: nil, source_performance_group_keys: nil,
|
|
22
|
-
edition: UNSET, idempotency_key: nil)
|
|
58
|
+
edition: UNSET, idempotency_key: nil, single_tickets: nil, inventory_mode: nil)
|
|
23
59
|
body = selection(event_keys, source_performance_group_keys).merge("name" => name)
|
|
24
60
|
body.merge!(supplied({ "edition" => edition }))
|
|
61
|
+
body.merge!(compact({ "singleTickets" => single_tickets, "inventoryMode" => inventory_mode }))
|
|
25
62
|
@client.post("/v1/seasons", body, idempotency_key: idempotency_key,
|
|
26
63
|
retry_policy: :header_replay)
|
|
27
64
|
end
|
|
@@ -30,10 +67,33 @@ module SeatLayer
|
|
|
30
67
|
@client.get(path(season_key))
|
|
31
68
|
end
|
|
32
69
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
70
|
+
# Rename a draft, or set how the Season sells. +checkout_mode+ ("seatlayer" or
|
|
71
|
+
# "server") cannot go back to undecided and is fixed once a live booking exists.
|
|
72
|
+
# The cutoffs are hours (0 to 720) before a date starts; pass +nil+ to send an
|
|
73
|
+
# explicit JSON null, which means the club default (Settings › Ticket site), or
|
|
74
|
+
# until the date starts when the club has none. +audience+ "public" lists
|
|
75
|
+
# the Season on your SeatLayer website ("embed_only" does not); an ended Season
|
|
76
|
+
# cannot become public (409 sales_ended). +single_tickets+ turned on while the Season
|
|
77
|
+
# is on sale opens each upcoming date's own sales through its readiness checks; turned
|
|
78
|
+
# off, they stop. The answer's "singleTicketDates" says which dates opened or stopped,
|
|
79
|
+
# and carries "jobRefused" when its season_singles job could not start: the change
|
|
80
|
+
# still stands, and sending +single_tickets+ true again opens any date still not open.
|
|
81
|
+
# A retry with the same +expected_revision+ and +idempotency_key+ is not applied twice.
|
|
82
|
+
# +inventory_mode+ "block" turns season seats on for this Season; "per_hold" turns them
|
|
83
|
+
# off again, refused with 409 season_block_exists once seats are set aside. It does not
|
|
84
|
+
# change the Season's "revision". Refused with 409 season_block_packages (more than one
|
|
85
|
+
# package), 409 season_block_on_sale (sales already opened without season seats),
|
|
86
|
+
# 409 season_not_draft_or_active, or 422 invalid_inventory_mode.
|
|
87
|
+
def update_season( # rubocop:disable Metrics/ParameterLists
|
|
88
|
+
season_key, expected_revision:, name: nil, edition: UNSET, checkout_mode: nil,
|
|
89
|
+
transfer_cutoff_hours: UNSET, return_cutoff_hours: UNSET, audience: nil, idempotency_key: nil,
|
|
90
|
+
single_tickets: nil, inventory_mode: nil
|
|
91
|
+
)
|
|
92
|
+
body = compact({ "expectedRevision" => expected_revision, "name" => name,
|
|
93
|
+
"checkoutMode" => checkout_mode, "audience" => audience,
|
|
94
|
+
"singleTickets" => single_tickets, "inventoryMode" => inventory_mode })
|
|
95
|
+
body.merge!(supplied({ "edition" => edition, "transferCutoffHours" => transfer_cutoff_hours,
|
|
96
|
+
"returnCutoffHours" => return_cutoff_hours }))
|
|
37
97
|
@client.mutation_with_header_replay(
|
|
38
98
|
"PATCH", path(season_key), body, idempotency_key: idempotency_key
|
|
39
99
|
)
|
|
@@ -82,6 +142,11 @@ module SeatLayer
|
|
|
82
142
|
{ "expectedRevision" => expected_revision })
|
|
83
143
|
end
|
|
84
144
|
|
|
145
|
+
# Open sales. The answer's "warnings" say what is advised but not required, such
|
|
146
|
+
# as "checkout_test_missing" until a checkout test or rehearsal has passed.
|
|
147
|
+
# Refused with 409 plan_not_published or checkout_mode_not_chosen until a package
|
|
148
|
+
# is published and a checkout mode is chosen, and with 409 block_not_ready when
|
|
149
|
+
# season seats are turned on (inventory_mode "block") but the seats are not set aside yet.
|
|
85
150
|
def open_season_sales(season_key, expected_revision:)
|
|
86
151
|
sales(season_key, "open", expected_revision)
|
|
87
152
|
end
|
|
@@ -147,6 +212,11 @@ module SeatLayer
|
|
|
147
212
|
@client.post(path(season_key, "/buyer-rehearsals/validate"))
|
|
148
213
|
end
|
|
149
214
|
|
|
215
|
+
# Import incumbent holders. Each row hash takes "rowId", "holderRef",
|
|
216
|
+
# "priorPlanActivationId", "priorContractRef" and "labels", plus optional
|
|
217
|
+
# "existingBookingRef", "holderEmail" and "holderName". A call takes at most
|
|
218
|
+
# +Seasons.holder_import_rows_per_call(dates)+ rows (100 up to 20 dates, 43 at 50);
|
|
219
|
+
# more answers 422 invalid_rows with "maxRows" on the error's +body+.
|
|
150
220
|
def create_season_holder_import(season_key, successor_plan_activation_id:, rows:,
|
|
151
221
|
dry_run: UNSET, idempotency_key: nil)
|
|
152
222
|
body = { "successorPlanActivationId" => successor_plan_activation_id, "rows" => rows }
|
|
@@ -159,6 +229,10 @@ module SeatLayer
|
|
|
159
229
|
@client.get(path(season_key, "/imports/#{encode(import_id)}"))
|
|
160
230
|
end
|
|
161
231
|
|
|
232
|
+
# Answers 201 {"offers"} for 100 contracts or fewer. Above 100 it answers 202
|
|
233
|
+
# {"job", "batchId"}: a renewal_run job makes the offers. Follow it with
|
|
234
|
+
# +retrieve_season_bulk_job+, then page the offers with +list_season_renewal_offers+.
|
|
235
|
+
# Retried with one Idempotency-Key, which answers the same job.
|
|
162
236
|
def create_season_renewal_offers(season_key, deadline_at:,
|
|
163
237
|
successor_plan_activation_id: nil, contract_ids: nil,
|
|
164
238
|
idempotency_key: nil)
|
|
@@ -168,8 +242,10 @@ module SeatLayer
|
|
|
168
242
|
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
169
243
|
end
|
|
170
244
|
|
|
171
|
-
|
|
172
|
-
|
|
245
|
+
# One page of renewal offers, newest first. Pass +cursor+ from the previous
|
|
246
|
+
# page's +nextCursor+ (nil on the last page); +limit+ is 1 to 100 (default 100).
|
|
247
|
+
def list_season_renewal_offers(season_key, cursor: nil, limit: nil)
|
|
248
|
+
@client.get(path(season_key, "/renewal-offers"), compact({ "cursor" => cursor, "limit" => limit }))
|
|
173
249
|
end
|
|
174
250
|
|
|
175
251
|
def retrieve_season_renewal_offer(season_key, offer_id)
|
|
@@ -192,8 +268,12 @@ module SeatLayer
|
|
|
192
268
|
"bookingRef" => booking_ref, "planActivationId" => plan_activation_id })
|
|
193
269
|
end
|
|
194
270
|
|
|
195
|
-
|
|
196
|
-
|
|
271
|
+
# Decline a renewal offer and release its seats at once. +reason+ (1 to 200
|
|
272
|
+
# characters) is kept on the offer as +declineReason+; a repeated decline
|
|
273
|
+
# keeps the first reason.
|
|
274
|
+
def decline_season_renewal_offer(season_key, offer_id, reason: nil)
|
|
275
|
+
@client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/decline"),
|
|
276
|
+
compact({ "reason" => reason }))
|
|
197
277
|
end
|
|
198
278
|
|
|
199
279
|
def release_season_renewal_offer(season_key, offer_id)
|
|
@@ -204,11 +284,23 @@ module SeatLayer
|
|
|
204
284
|
@client.get(path(season_key, "/occurrences"))
|
|
205
285
|
end
|
|
206
286
|
|
|
287
|
+
# Amend one date. +kind+ is "reschedule", "replace", "cancel_exception",
|
|
288
|
+
# "postpone" (no new time yet) or "cancel" (the date will not happen). With
|
|
289
|
+
# "cancel" only, +refund+ ("none" or "per_date") refunds each SeatLayer order's
|
|
290
|
+
# share of the date as a job, and +reason+ is kept on it. A whole-order refund
|
|
291
|
+
# is the order refund. A "cancel" also voids every live ticket for the date as a
|
|
292
|
+
# date_cancel_void job, +Seasons.date_cancel_void_job_id(amendment_id)+ (the answer
|
|
293
|
+
# does not name it); follow it with +retrieve_season_bulk_job+. A cancelled date
|
|
294
|
+
# can't be moved ("reschedule"), postponed or replaced: 409 performance_cancelled.
|
|
295
|
+
# On a season-seats season, a "cancel" first takes the date off every season seat:
|
|
296
|
+
# while that finishes the answer is a 202 (check with +Seasons.date_leave_pending?+);
|
|
297
|
+
# send the same request again to read the amendment.
|
|
207
298
|
def create_season_amendment(season_key, event_key:, kind:, starts_at: nil, name: nil,
|
|
208
|
-
idempotency_key: nil)
|
|
299
|
+
refund: nil, reason: nil, idempotency_key: nil)
|
|
209
300
|
@client.post(path(season_key, "/amendments"),
|
|
210
301
|
compact({ "eventKey" => event_key, "kind" => kind,
|
|
211
|
-
"startsAt" => starts_at, "name" => name
|
|
302
|
+
"startsAt" => starts_at, "name" => name,
|
|
303
|
+
"refund" => refund, "reason" => reason }),
|
|
212
304
|
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
213
305
|
end
|
|
214
306
|
|
|
@@ -249,6 +341,335 @@ module SeatLayer
|
|
|
249
341
|
@client.get(path(season_key, "/export"))
|
|
250
342
|
end
|
|
251
343
|
|
|
344
|
+
# Dry-run or commit renewal rights derived from a previous Season's holders.
|
|
345
|
+
# A commit of more than 100 holders answers 202 {"job"}: a holder_import job
|
|
346
|
+
# brings them across, and its +summary+ has the same counts once it completes.
|
|
347
|
+
# Retried with one Idempotency-Key, which answers the same job.
|
|
348
|
+
def create_season_holder_import_from_season(season_key, source_season_id:,
|
|
349
|
+
successor_plan_activation_id:, dry_run: UNSET,
|
|
350
|
+
idempotency_key: nil)
|
|
351
|
+
body = { "sourceSeasonId" => source_season_id,
|
|
352
|
+
"successorPlanActivationId" => successor_plan_activation_id }
|
|
353
|
+
body.merge!(supplied({ "dryRun" => dry_run }))
|
|
354
|
+
@client.post(path(season_key, "/imports/from-season"), body,
|
|
355
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# List the Season's performances and package membership, in every lifecycle state.
|
|
359
|
+
def list_season_performances(season_key)
|
|
360
|
+
@client.get(path(season_key, "/performances"))
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Create performances inside a draft Season from a repeat rule. Pass +nil+
|
|
364
|
+
# for +venue+ or +duration_minutes+ to send an explicit JSON null.
|
|
365
|
+
# rubocop:disable-next Metrics/ParameterLists
|
|
366
|
+
def create_season_performances(season_key, title_pattern:, timezone:, repeat:,
|
|
367
|
+
first_starts_at: nil, count: nil, dates: nil, venue: UNSET,
|
|
368
|
+
duration_minutes: UNSET, region: nil,
|
|
369
|
+
attach_to_plan_keys: nil, idempotency_key: nil)
|
|
370
|
+
body = compact({ "titlePattern" => title_pattern, "firstStartsAt" => first_starts_at,
|
|
371
|
+
"timezone" => timezone, "repeat" => repeat, "count" => count,
|
|
372
|
+
"dates" => dates, "region" => region,
|
|
373
|
+
"attachToPlanKeys" => attach_to_plan_keys })
|
|
374
|
+
body.merge!(supplied({ "venue" => venue, "durationMinutes" => duration_minutes }))
|
|
375
|
+
@client.post(path(season_key, "/performances"), body,
|
|
376
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Attach existing events to a draft Season as performances.
|
|
380
|
+
def attach_season_performances(season_key, event_keys:, attach_to_plan_keys: nil,
|
|
381
|
+
idempotency_key: nil)
|
|
382
|
+
body = compact({ "eventKeys" => event_keys, "attachToPlanKeys" => attach_to_plan_keys })
|
|
383
|
+
@client.post(path(season_key, "/performances/attach"), body,
|
|
384
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Remove one performance from a draft Season.
|
|
388
|
+
def delete_season_performance(season_key, event_key, idempotency_key: nil)
|
|
389
|
+
@client.mutation_with_header_replay(
|
|
390
|
+
"DELETE", path(season_key, "/performances/#{encode(event_key)}"),
|
|
391
|
+
idempotency_key: idempotency_key
|
|
392
|
+
)
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Rename a draft package or change the performances it covers.
|
|
396
|
+
def update_season_plan(season_key, plan_key, name: nil, event_keys: nil, idempotency_key: nil)
|
|
397
|
+
@client.mutation_with_header_replay(
|
|
398
|
+
"PATCH", path(season_key, "/plans/#{encode(plan_key)}"),
|
|
399
|
+
compact({ "name" => name, "eventKeys" => event_keys }), idempotency_key: idempotency_key
|
|
400
|
+
)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Delete a draft package. The API answers 204, so this returns an empty Hash.
|
|
404
|
+
def delete_season_plan(season_key, plan_key, idempotency_key: nil)
|
|
405
|
+
@client.mutation_with_header_replay(
|
|
406
|
+
"DELETE", path(season_key, "/plans/#{encode(plan_key)}"), idempotency_key: idempotency_key
|
|
407
|
+
)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Publish a draft Season. Sends no body.
|
|
411
|
+
def publish_season(season_key, idempotency_key: nil)
|
|
412
|
+
@client.post(path(season_key, "/publish"), nil,
|
|
413
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Run a test checkout against the Season.
|
|
417
|
+
def create_season_checkout_test(season_key, plan_key: nil, quantity: nil, idempotency_key: nil)
|
|
418
|
+
@client.post(path(season_key, "/checkout-tests"),
|
|
419
|
+
compact({ "planKey" => plan_key, "quantity" => quantity }),
|
|
420
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# List the Season's test checkouts.
|
|
424
|
+
def list_season_checkout_tests(season_key)
|
|
425
|
+
@client.get(path(season_key, "/checkout-tests"))
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Replace every price of one package. Each price hash takes "categoryKey" and
|
|
429
|
+
# "unitPrice" (minor units), plus an optional "priceTypeKey": "adult" (the
|
|
430
|
+
# default), "child", "concession", "senior" or "student".
|
|
431
|
+
def replace_season_package_prices(season_key, plan_key, prices:, currency: nil,
|
|
432
|
+
idempotency_key: nil)
|
|
433
|
+
@client.mutation_with_header_replay(
|
|
434
|
+
"PUT", path(season_key, "/plans/#{encode(plan_key)}/prices"),
|
|
435
|
+
compact({ "prices" => prices, "currency" => currency }), idempotency_key: idempotency_key
|
|
436
|
+
)
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
# List the prices of every package in the Season, each with its
|
|
440
|
+
# "priceTypeKey" and "priceTypeLabel".
|
|
441
|
+
def list_season_prices(season_key)
|
|
442
|
+
@client.get(path(season_key, "/prices"))
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Add one performance to a package.
|
|
446
|
+
def attach_season_plan_performance(season_key, plan_key, event_key:, idempotency_key: nil)
|
|
447
|
+
@client.post(path(season_key, "/plans/#{encode(plan_key)}/performances"),
|
|
448
|
+
{ "eventKey" => event_key },
|
|
449
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# Remove one performance from a package.
|
|
453
|
+
def delete_season_plan_performance(season_key, plan_key, event_key, idempotency_key: nil)
|
|
454
|
+
@client.mutation_with_header_replay(
|
|
455
|
+
"DELETE", path(season_key, "/plans/#{encode(plan_key)}/performances/#{encode(event_key)}"),
|
|
456
|
+
idempotency_key: idempotency_key
|
|
457
|
+
)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
# Mark a performance past so it stops blocking new Season sales. A cancelled date
|
|
461
|
+
# answers 409 performance_cancelled. On a season-seats season the date first leaves
|
|
462
|
+
# every season seat: while that finishes the answer is a 202 (check with
|
|
463
|
+
# +Seasons.date_leave_pending?+).
|
|
464
|
+
def mark_season_performance_past(season_key, event_key, force: nil, idempotency_key: nil)
|
|
465
|
+
@client.post(path(season_key, "/performances/#{encode(event_key)}/past"),
|
|
466
|
+
compact({ "force" => force }),
|
|
467
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Return one Season occurrence allocation to ordinary Event inventory.
|
|
471
|
+
# Single-attempt: keep +action_id+ stable and retry it yourself if needed.
|
|
472
|
+
def return_season_occurrence_inventory(season_key, event_key, action_id:, booking_ref:,
|
|
473
|
+
plan_activation_id:, labels:, source:)
|
|
474
|
+
@client.post(path(season_key, "/occurrences/#{encode(event_key)}/returns"),
|
|
475
|
+
{ "actionId" => action_id, "bookingRef" => booking_ref,
|
|
476
|
+
"planActivationId" => plan_activation_id, "labels" => labels,
|
|
477
|
+
"source" => source })
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# Reclaim returned inventory for a Season occurrence. Single-attempt, like
|
|
481
|
+
# +return_season_occurrence_inventory+.
|
|
482
|
+
def reclaim_season_occurrence_inventory(season_key, event_key, action_id:, return_action_id:,
|
|
483
|
+
booking_ref:, plan_activation_id:, labels:, source:)
|
|
484
|
+
@client.post(path(season_key, "/occurrences/#{encode(event_key)}/reclaims"),
|
|
485
|
+
{ "actionId" => action_id, "returnActionId" => return_action_id,
|
|
486
|
+
"bookingRef" => booking_ref, "planActivationId" => plan_activation_id,
|
|
487
|
+
"labels" => labels, "source" => source })
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# One page of Season holders. +search+ is sent as the API's +q+ parameter.
|
|
491
|
+
def list_season_holders(season_key, search: nil, status: nil, cursor: nil, limit: nil)
|
|
492
|
+
@client.get(path(season_key, "/holders"),
|
|
493
|
+
compact({ "q" => search, "status" => status, "cursor" => cursor,
|
|
494
|
+
"limit" => limit }))
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# Refund a Season order, in full or by +amount_minor+.
|
|
498
|
+
def refund_season_order(season_key, order_id, amount_minor: nil, idempotency_key: nil)
|
|
499
|
+
@client.post(path(season_key, "/orders/#{encode(order_id)}/refund"),
|
|
500
|
+
compact({ "amountMinor" => amount_minor }),
|
|
501
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
# Cancel a Season order.
|
|
505
|
+
def cancel_season_order(season_key, order_id, reason:, note: nil, refund_confirmed: nil,
|
|
506
|
+
used_tickets_acknowledged: nil, notify_buyer: nil, idempotency_key: nil)
|
|
507
|
+
body = compact({ "reason" => reason, "note" => note, "refundConfirmed" => refund_confirmed,
|
|
508
|
+
"usedTicketsAcknowledged" => used_tickets_acknowledged,
|
|
509
|
+
"notifyBuyer" => notify_buyer })
|
|
510
|
+
@client.post(path(season_key, "/orders/#{encode(order_id)}/cancel"), body,
|
|
511
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# Cancel one date of a Season order.
|
|
515
|
+
def cancel_season_order_date(season_key, order_id, event_key, action_id: nil,
|
|
516
|
+
idempotency_key: nil)
|
|
517
|
+
@client.post(path(season_key, "/orders/#{encode(order_id)}/dates/#{encode(event_key)}/cancel"),
|
|
518
|
+
compact({ "actionId" => action_id }),
|
|
519
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Send a Season order's tickets again. Single-attempt: never retried automatically.
|
|
523
|
+
def resend_season_order_tickets(season_key, order_id, email: nil)
|
|
524
|
+
@client.post(path(season_key, "/orders/#{encode(order_id)}/resend"),
|
|
525
|
+
compact({ "email" => email }))
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
# Read the state of hosted renewals for one page of offers, newest first. Pass
|
|
529
|
+
# +cursor+ from the previous page's +nextCursor+ (nil on the last page); +limit+
|
|
530
|
+
# is 1 to 500 (default 500).
|
|
531
|
+
def retrieve_season_hosted_renewals(season_key, cursor: nil, limit: nil)
|
|
532
|
+
@client.get(path(season_key, "/renewal-hosted"), compact({ "cursor" => cursor, "limit" => limit }))
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# Replace a renewal offer's link. The new link is shown once, so this is
|
|
536
|
+
# deliberately single-attempt.
|
|
537
|
+
def rotate_season_renewal_offer_link(season_key, offer_id)
|
|
538
|
+
@client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/link"))
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
# Set the contact on a renewal offer. Pass +nil+ to send an explicit JSON null.
|
|
542
|
+
def update_season_renewal_offer_contact(season_key, offer_id, email: UNSET, name: UNSET)
|
|
543
|
+
@client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/contact"),
|
|
544
|
+
supplied({ "email" => email, "name" => name }))
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
# Send renewal notices, to every open offer or only to +offer_ids+ (at most
|
|
548
|
+
# 20,000; more is refused with 422 too_many_offer_ids).
|
|
549
|
+
# +batch_id+, instead of +offer_ids+: every open offer one renewal offer batch made.
|
|
550
|
+
def create_season_renewal_notices(season_key, offer_ids: nil, batch_id: nil)
|
|
551
|
+
@client.post(path(season_key, "/renewal-notices"),
|
|
552
|
+
compact({ "offerIds" => offer_ids, "batchId" => batch_id }))
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
# Send one renewal offer again. Single-attempt: never retried automatically.
|
|
556
|
+
def resend_season_renewal_offer(season_key, offer_id)
|
|
557
|
+
@client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/resend"))
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# What renewing an offer costs, priced as the SeatLayer checkout will charge
|
|
561
|
+
# it. +problem+ is nil when SeatLayer checkout can take the renewal now.
|
|
562
|
+
def retrieve_season_renewal_quote(season_key, offer_id)
|
|
563
|
+
@client.get(path(season_key, "/renewal-offers/#{encode(offer_id)}/quote"))
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
# Record that the holder means to renew, for a renewal taken by phone or at
|
|
567
|
+
# the box office, so your own checkout can commit the offer. Single-attempt;
|
|
568
|
+
# a repeat answers the same offer.
|
|
569
|
+
def create_season_renewal_intent(season_key, offer_id)
|
|
570
|
+
@client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/intent"))
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
# Start a SeatLayer checkout that renews an offer, with no hosted page. Send
|
|
574
|
+
# the buyer to +payment.url+. Retried with one Idempotency-Key, which answers
|
|
575
|
+
# the same order.
|
|
576
|
+
def create_season_renewal_checkout(season_key, offer_id, return_url:, buyer_email: nil,
|
|
577
|
+
buyer_name: nil, provider: nil, idempotency_key: nil)
|
|
578
|
+
@client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/checkout"),
|
|
579
|
+
compact({ "returnUrl" => return_url, "buyerEmail" => buyer_email,
|
|
580
|
+
"buyerName" => buyer_name, "provider" => provider }),
|
|
581
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# Read the Season's sales report.
|
|
585
|
+
def retrieve_season_sales_report(season_key)
|
|
586
|
+
@client.get(path(season_key, "/reports/sales"))
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
# Read the Season's seats report.
|
|
590
|
+
def retrieve_season_seats_report(season_key)
|
|
591
|
+
@client.get(path(season_key, "/reports/seats"))
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# Download the Season's holders as CSV. Returns the raw CSV text.
|
|
595
|
+
def export_season_holders_csv(season_key, search: nil, status: nil)
|
|
596
|
+
@client.get_text(path(season_key, "/reports/holders.csv"),
|
|
597
|
+
compact({ "q" => search, "status" => status }))
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
# Download every Season order SeatLayer took as CSV. Returns the raw CSV text.
|
|
601
|
+
def export_season_orders_csv(season_key)
|
|
602
|
+
@client.get_text(path(season_key, "/reports/orders.csv"))
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# Read the Season's seat counts.
|
|
606
|
+
def retrieve_season_seat_counts(season_key)
|
|
607
|
+
@client.get(path(season_key, "/seat-counts"))
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
# Refund every SeatLayer order holding a cancelled date, as a job. +amount+ is
|
|
611
|
+
# "per_date" (the date's share of each order); "full" is refused (422
|
|
612
|
+
# date_refund_per_date_only), so refund a whole order with the order refund.
|
|
613
|
+
# Retried with one Idempotency-Key, which replays the same job.
|
|
614
|
+
def refund_season_performance(season_key, event_key, amount:, reason: nil, idempotency_key: nil)
|
|
615
|
+
@client.post(path(season_key, "/performances/#{encode(event_key)}/refunds"),
|
|
616
|
+
compact({ "amount" => amount, "reason" => reason }),
|
|
617
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
# Release the seats of every lapsed renewal offer, as a job; an offer extended
|
|
621
|
+
# before its turn is skipped and keeps its seat. Follow it with
|
|
622
|
+
# +retrieve_season_bulk_job+. Retried with one Idempotency-Key, which replays the same job.
|
|
623
|
+
def release_season_lapsed_offers(season_key, idempotency_key: nil)
|
|
624
|
+
@client.post(path(season_key, "/renewal-offers/release-lapsed"), nil,
|
|
625
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
# List the Season's bulk jobs, newest first (100 at most).
|
|
629
|
+
def list_season_bulk_jobs(season_key)
|
|
630
|
+
@client.get(path(season_key, "/jobs"))
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
# Read one bulk job with its counts and up to 500 failed items.
|
|
634
|
+
def retrieve_season_bulk_job(season_key, job_id)
|
|
635
|
+
@client.get(path(season_key, "/jobs/#{encode(job_id)}"))
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
# Set the Season's seats aside on every date: every seat its packages price, minus
|
|
639
|
+
# +keep_for_single_tickets+ ({ "sectionIds", "categoryKeys", "labels" }). Answers 202
|
|
640
|
+
# with "job" (a season_block_set_aside job to follow with +retrieve_season_bulk_job+)
|
|
641
|
+
# and "block". On an active Season block mode turns on by itself once every date has
|
|
642
|
+
# the seats (season.block.ready); on a draft, Publish turns it on. Season seats must be
|
|
643
|
+
# on for the Season (409 season_block_not_enabled otherwise). Retried with one
|
|
644
|
+
# Idempotency-Key, which replays the same answer.
|
|
645
|
+
def set_aside_season_block(season_key, expected_revision:, keep_for_single_tickets: nil,
|
|
646
|
+
idempotency_key: nil)
|
|
647
|
+
@client.post(path(season_key, "/block"),
|
|
648
|
+
compact({ "expectedRevision" => expected_revision,
|
|
649
|
+
"keepForSingleTickets" => keep_for_single_tickets }),
|
|
650
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
# The Season's seats set aside on every date: "block" with its "mode" ("block" once
|
|
654
|
+
# on, else "per_hold"), "state", "counts" and each date's "state".
|
|
655
|
+
def retrieve_season_block(season_key)
|
|
656
|
+
@client.get(path(season_key, "/block"))
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
# Release season seats to single sale: every unsold seat (+scope+ { "all" => true }),
|
|
660
|
+
# or the sections, categories or seats named. +event_keys+ limits it to those dates
|
|
661
|
+
# (left out: every date still to come); +at+ (an ISO time) releases then instead of
|
|
662
|
+
# now. Answers 202 with "job" (a season_block_release job to follow with
|
|
663
|
+
# +retrieve_season_bulk_job+) and "block". season.block.released is sent once it has
|
|
664
|
+
# run on every date. Retried with one Idempotency-Key, which replays the same release.
|
|
665
|
+
def release_season_block(season_key, expected_revision:, scope:, event_keys: nil, at: nil,
|
|
666
|
+
reason: nil, idempotency_key: nil)
|
|
667
|
+
@client.post(path(season_key, "/block/release"),
|
|
668
|
+
compact({ "expectedRevision" => expected_revision, "scope" => scope,
|
|
669
|
+
"eventKeys" => event_keys, "at" => at, "reason" => reason }),
|
|
670
|
+
idempotency_key: idempotency_key, retry_policy: :header_replay)
|
|
671
|
+
end
|
|
672
|
+
|
|
252
673
|
private
|
|
253
674
|
|
|
254
675
|
def path(season_key, suffix = "")
|
data/lib/seatlayer/version.rb
CHANGED
data/lib/seatlayer.rb
CHANGED
|
@@ -8,6 +8,9 @@ require_relative "seatlayer/inventory"
|
|
|
8
8
|
require_relative "seatlayer/channels"
|
|
9
9
|
require_relative "seatlayer/performance_groups"
|
|
10
10
|
require_relative "seatlayer/seasons"
|
|
11
|
+
require_relative "seatlayer/holders"
|
|
12
|
+
require_relative "seatlayer/orders"
|
|
13
|
+
require_relative "seatlayer/event_series"
|
|
11
14
|
require_relative "seatlayer/webhook"
|
|
12
15
|
|
|
13
16
|
# Official Ruby server SDK for the SeatLayer reserved-seating API.
|
|
@@ -22,7 +25,7 @@ module SeatLayer
|
|
|
22
25
|
# The SeatLayer client.
|
|
23
26
|
class Client
|
|
24
27
|
attr_reader :charts, :events, :inventory, :channels, :performance_groups, :sessions,
|
|
25
|
-
:seasons, :webhooks, :workspaces, :templates
|
|
28
|
+
:seasons, :webhooks, :workspaces, :templates, :holders, :orders, :event_series
|
|
26
29
|
|
|
27
30
|
def initialize(secret_key, base_url: HTTPClient::DEFAULT_BASE_URL,
|
|
28
31
|
max_retries: HTTPClient::DEFAULT_MAX_RETRIES,
|
|
@@ -36,6 +39,9 @@ module SeatLayer
|
|
|
36
39
|
@channels = Channels.new(@http)
|
|
37
40
|
@performance_groups = PerformanceGroups.new(@http)
|
|
38
41
|
@seasons = Seasons.new(@http)
|
|
42
|
+
@holders = Holders.new(@http)
|
|
43
|
+
@orders = Orders.new(@http)
|
|
44
|
+
@event_series = EventSeries.new(@http)
|
|
39
45
|
@sessions = Sessions.new(@http)
|
|
40
46
|
@templates = Templates.new(@http)
|
|
41
47
|
@webhooks = Webhooks.new(@http)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: seatlayer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SeatLayer
|
|
@@ -24,8 +24,11 @@ files:
|
|
|
24
24
|
- lib/seatlayer.rb
|
|
25
25
|
- lib/seatlayer/channels.rb
|
|
26
26
|
- lib/seatlayer/errors.rb
|
|
27
|
+
- lib/seatlayer/event_series.rb
|
|
28
|
+
- lib/seatlayer/holders.rb
|
|
27
29
|
- lib/seatlayer/http_client.rb
|
|
28
30
|
- lib/seatlayer/inventory.rb
|
|
31
|
+
- lib/seatlayer/orders.rb
|
|
29
32
|
- lib/seatlayer/performance_groups.rb
|
|
30
33
|
- lib/seatlayer/resources.rb
|
|
31
34
|
- lib/seatlayer/seasons.rb
|
|
@@ -57,5 +60,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
57
60
|
requirements: []
|
|
58
61
|
rubygems_version: 3.6.9
|
|
59
62
|
specification_version: 4
|
|
60
|
-
summary: Official Ruby server SDK for the SeatLayer reserved-seating API
|
|
63
|
+
summary: 'Official Ruby server SDK for the SeatLayer reserved-seating API: seating
|
|
64
|
+
charts, seat maps, holds and seat booking.'
|
|
61
65
|
test_files: []
|