square.rb 45.0.1.20260122 → 45.2.0.pre.rc.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.fern/metadata.json +2 -2
  3. data/.fern/replay.lock +10 -0
  4. data/.fernignore +7 -0
  5. data/README.md +54 -0
  6. data/lib/square/client.rb +6 -1
  7. data/lib/square/inventory/client.rb +39 -26
  8. data/lib/square/payments/types/create_payment_request.rb +1 -0
  9. data/lib/square/refunds/types/refund_payment_request.rb +1 -0
  10. data/lib/square/reporting/client.rb +83 -0
  11. data/lib/square/reporting/types/load_request.rb +13 -0
  12. data/lib/square/reporting_helper.rb +147 -0
  13. data/lib/square/transfer_orders/client.rb +24 -16
  14. data/lib/square/types/cache_mode.rb +14 -0
  15. data/lib/square/types/card_payment_details.rb +2 -0
  16. data/lib/square/types/card_surcharge_details.rb +14 -0
  17. data/lib/square/types/cube.rb +20 -0
  18. data/lib/square/types/cube_join.rb +10 -0
  19. data/lib/square/types/cube_type.rb +12 -0
  20. data/lib/square/types/custom_numeric_format.rb +12 -0
  21. data/lib/square/types/custom_time_format.rb +11 -0
  22. data/lib/square/types/digital_wallet_details.rb +1 -0
  23. data/lib/square/types/dimension.rb +21 -0
  24. data/lib/square/types/dimension_granularity.rb +14 -0
  25. data/lib/square/types/dimension_order.rb +12 -0
  26. data/lib/square/types/electronic_money_details.rb +10 -0
  27. data/lib/square/types/felica_details.rb +12 -0
  28. data/lib/square/types/felica_details_felica_brand.rb +14 -0
  29. data/lib/square/types/folder.rb +10 -0
  30. data/lib/square/types/format.rb +16 -0
  31. data/lib/square/types/format_description.rb +12 -0
  32. data/lib/square/types/hierarchy.rb +12 -0
  33. data/lib/square/types/join_hint.rb +23 -0
  34. data/lib/square/types/join_subquery.rb +12 -0
  35. data/lib/square/types/lightning_details.rb +10 -0
  36. data/lib/square/types/link_format.rb +11 -0
  37. data/lib/square/types/load_response.rb +19 -0
  38. data/lib/square/types/load_result_annotation.rb +12 -0
  39. data/lib/square/types/load_result_data.rb +15 -0
  40. data/lib/square/types/load_result_data_columnar.rb +12 -0
  41. data/lib/square/types/load_result_data_compact.rb +12 -0
  42. data/lib/square/types/load_result_data_row.rb +23 -0
  43. data/lib/square/types/measure.rb +19 -0
  44. data/lib/square/types/metadata_response.rb +10 -0
  45. data/lib/square/types/nested_folder.rb +10 -0
  46. data/lib/square/types/payment.rb +3 -0
  47. data/lib/square/types/payment_refund.rb +1 -0
  48. data/lib/square/types/query.rb +20 -0
  49. data/lib/square/types/query_filter.rb +13 -0
  50. data/lib/square/types/query_filter_and.rb +9 -0
  51. data/lib/square/types/query_filter_condition.rb +11 -0
  52. data/lib/square/types/query_filter_or.rb +9 -0
  53. data/lib/square/types/reporting_error.rb +11 -0
  54. data/lib/square/types/search_orders_source_filter.rb +0 -2
  55. data/lib/square/types/segment.rb +13 -0
  56. data/lib/square/types/simple_format.rb +16 -0
  57. data/lib/square/types/time_dimension.rb +11 -0
  58. data/lib/square/types/time_dimension_date_range.rb +13 -0
  59. data/lib/square/version.rb +1 -1
  60. data/lib/square.rb +42 -0
  61. data/reference.md +159 -0
  62. data/wiremock/wiremock-mappings.json +1 -1
  63. metadata +48 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 056af038668564ff8d7f1860bcc35e44449e21f4784d91846121c139d4c139d5
4
- data.tar.gz: 357823902c08c710857fe636e58f92cc223ef2269e1795b888077cea2d79114b
3
+ metadata.gz: 33286aa52968fd20a87e53747ec5f19b0769a955fae8be8bdb0d984d8d0e00cd
4
+ data.tar.gz: 1cf5fd062db619a9e1ebe949bffe6e724173526fd82d6af6bb1e07eef6d69e8d
5
5
  SHA512:
6
- metadata.gz: 86385d14a820fcc51363fad79429b7f543b80329559128f3c609ced4c30cbcb06c4e5bce954b379f628ea34ffe69163e1231bedd7b41c12f3e46162f479694ec
7
- data.tar.gz: e93cc124da8f72be75e60cf8e7401ad706edcde4efe464579081a684fafb86c28792dc6e46728eb2bf5531b4c02d4eac68e53ecb6204b597e15489013f4ffccd
6
+ metadata.gz: 8ebd02e23396a301d4cb91cdc2592d10112d419d84ba3581e69fba5d5222bbe58916c614e60d71692aeb4440079e7f8153582eba549d7f10e68d2c653cb49888
7
+ data.tar.gz: 7f5819b9a81bb6b683d64134b09649c698ccecc01db56b5d0e7ea58c328083da6ee669d8c5b034086d2a2a98ec4bc63ae73aac3054a3596d60b6f80fe22aa46c
data/.fern/metadata.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
- "cliVersion": "3.49.3",
2
+ "cliVersion": "3.98.5",
3
3
  "generatorName": "fernapi/fern-ruby-sdk",
4
4
  "generatorVersion": "1.0.0-rc84",
5
5
  "generatorConfig": {
6
6
  "enableWireTests": true
7
7
  },
8
- "sdkVersion": "45.0.1.20260122"
8
+ "sdkVersion": "45.2.0-rc.0"
9
9
  }
data/.fern/replay.lock ADDED
@@ -0,0 +1,10 @@
1
+ # DO NOT EDIT MANUALLY - Managed by Fern Replay
2
+ version: "1.0"
3
+ generations:
4
+ - commit_sha: f631923c98d9161665156652789a58902009b805
5
+ tree_hash: 872074a659baa229a5564e34250e9b648d1751fd
6
+ timestamp: 2026-05-19T21:18:26.039Z
7
+ cli_version: unknown
8
+ generator_versions: {}
9
+ current_generation: f631923c98d9161665156652789a58902009b805
10
+ patches: []
data/.fernignore CHANGED
@@ -7,6 +7,10 @@ test/custom
7
7
  .github/workflows
8
8
  README.md
9
9
  lib/square/file_param.rb
10
+
11
+ # Custom polling helper for the Reporting API (load_and_wait + Continue-wait
12
+ # retry loop). Unit test lives under the already-ignored test/custom. FER-11257.
13
+ lib/square/reporting_helper.rb
10
14
  Gemfile.custom
11
15
  custom.gemspec.rb
12
16
 
@@ -18,3 +22,6 @@ lib/square/transfer_orders/client.rb
18
22
  # Iterator does not handle nil item fields (when API returns nil instead of empty array for no items)
19
23
  # Manually added nil checks to avoid calling .shift/.empty? on nil
20
24
  lib/square/internal/iterators/item_iterator.rb
25
+ .fern/replay.lock
26
+ .fern/replay.yml
27
+ .gitattributes
data/README.md CHANGED
@@ -255,6 +255,60 @@ response = client.payments.create(..., {
255
255
  })
256
256
  ```
257
257
 
258
+ ## Reporting API
259
+
260
+ The [Reporting API](https://developer.squareup.com/docs/reporting-api/overview) exposes two
261
+ polling endpoints under `client.reporting`:
262
+
263
+ - `get_metadata` — describes the data you can query (cubes, measures, dimensions, segments).
264
+ Call it first to discover the schema.
265
+ - `load` — runs a reporting query and returns the aggregated result rows in `data`.
266
+
267
+ `load` is asynchronous: while a query is still being computed it returns an HTTP 200 whose body
268
+ is `{ "error": "Continue wait" }` rather than the results. Callers are expected to re-send the
269
+ identical request, with backoff, until real results arrive.
270
+
271
+ The SDK ships a `.fernignore`-protected helper that owns that retry loop. Require it explicitly
272
+ to add `load_and_wait` to `client.reporting`:
273
+
274
+ ```ruby
275
+ require "square"
276
+ require "square/reporting_helper"
277
+
278
+ client = Square::Client.new(token: ENV.fetch("SQUARE_TOKEN"))
279
+
280
+ # 1. Discover the schema.
281
+ metadata = client.reporting.get_metadata
282
+
283
+ # 2. Run a query and transparently poll until it resolves.
284
+ response = client.reporting.load_and_wait(
285
+ query: {
286
+ measures: ["orders.count"],
287
+ time_dimensions: [{ dimension: "orders.created_at", granularity: "day" }]
288
+ }
289
+ )
290
+
291
+ response.data.each { |row| puts row }
292
+ ```
293
+
294
+ `load_and_wait` accepts optional polling parameters (defaults shown): `max_attempts: 20`,
295
+ `initial_delay: 2.0`, `max_delay: 20.0`, `backoff_factor: 2.0` (seconds). It raises
296
+ `Square::Reporting::ContinueWaitTimeoutError` if the query has not resolved within
297
+ `max_attempts`. Pass a `should_cancel` predicate to abort the loop early (the Ruby idiom for an
298
+ abort signal); it raises `Square::Reporting::PollingCancelledError` when the predicate returns a
299
+ truthy value:
300
+
301
+ ```ruby
302
+ deadline = Time.now + 60
303
+ response = client.reporting.load_and_wait(
304
+ query: { measures: ["orders.count"] },
305
+ should_cancel: -> { Time.now > deadline }
306
+ )
307
+ ```
308
+
309
+ > **Note:** The Reporting API is production-only — sandbox requests return 404, and a sandbox
310
+ > token against production returns 401. Use a production, reporting-provisioned token.
311
+
258
312
  ## Contributing
259
313
 
260
314
  While we value open-source contributions to this SDK, this library is generated programmatically. Additions made directly to this library would have to be moved over to our generation code, otherwise they would be overwritten upon the next generated release. Feel free to open a PR as a proof of concept, but know that we will not be able to merge it as-is. We suggest opening an issue first to discuss with us!
data/lib/square/client.rb CHANGED
@@ -10,7 +10,7 @@ module Square
10
10
  @raw_client = Square::Internal::Http::RawClient.new(
11
11
  base_url: base_url || Square::Environment::PRODUCTION,
12
12
  headers: {
13
- "User-Agent" => "square.rb/45.0.1.20260122",
13
+ "User-Agent" => "square.rb/45.2.0-rc.0",
14
14
  "X-Fern-Language" => "Ruby",
15
15
  Authorization: "Bearer #{token}"
16
16
  }
@@ -182,6 +182,11 @@ module Square
182
182
  @vendors ||= Square::Vendors::Client.new(client: @raw_client)
183
183
  end
184
184
 
185
+ # @return [Square::Reporting::Client]
186
+ def reporting
187
+ @reporting ||= Square::Reporting::Client.new(client: @raw_client)
188
+ end
189
+
185
190
  # @return [Square::CashDrawers::Client]
186
191
  def cash_drawers
187
192
  @cash_drawers ||= Square::CashDrawers::Client.new(client: @raw_client)
@@ -14,9 +14,10 @@ module Square
14
14
  # @return [Square::Types::GetInventoryAdjustmentResponse]
15
15
  def deprecated_get_adjustment(request_options: {}, **params)
16
16
  _request = Square::Internal::JSON::Request.new(
17
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
17
+ base_url: request_options[:base_url],
18
18
  method: "GET",
19
- path: "v2/inventory/adjustment/#{params[:adjustment_id]}"
19
+ path: "v2/inventory/adjustment/#{params[:adjustment_id]}",
20
+ request_options: request_options
20
21
  )
21
22
  begin
22
23
  _response = @client.send(_request)
@@ -38,9 +39,10 @@ module Square
38
39
  # @return [Square::Types::GetInventoryAdjustmentResponse]
39
40
  def get_adjustment(request_options: {}, **params)
40
41
  _request = Square::Internal::JSON::Request.new(
41
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
42
+ base_url: request_options[:base_url],
42
43
  method: "GET",
43
- path: "v2/inventory/adjustments/#{params[:adjustment_id]}"
44
+ path: "v2/inventory/adjustments/#{params[:adjustment_id]}",
45
+ request_options: request_options
44
46
  )
45
47
  begin
46
48
  _response = @client.send(_request)
@@ -62,10 +64,11 @@ module Square
62
64
  # @return [Square::Types::BatchChangeInventoryResponse]
63
65
  def deprecated_batch_change(request_options: {}, **params)
64
66
  _request = Square::Internal::JSON::Request.new(
65
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
67
+ base_url: request_options[:base_url],
66
68
  method: "POST",
67
69
  path: "v2/inventory/batch-change",
68
- body: Square::Types::BatchChangeInventoryRequest.new(params).to_h
70
+ body: Square::Types::BatchChangeInventoryRequest.new(params).to_h,
71
+ request_options: request_options
69
72
  )
70
73
  begin
71
74
  _response = @client.send(_request)
@@ -87,10 +90,11 @@ module Square
87
90
  # @return [Square::Types::BatchGetInventoryChangesResponse]
88
91
  def deprecated_batch_get_changes(request_options: {}, **params)
89
92
  _request = Square::Internal::JSON::Request.new(
90
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
93
+ base_url: request_options[:base_url],
91
94
  method: "POST",
92
95
  path: "v2/inventory/batch-retrieve-changes",
93
- body: Square::Types::BatchRetrieveInventoryChangesRequest.new(params).to_h
96
+ body: Square::Types::BatchRetrieveInventoryChangesRequest.new(params).to_h,
97
+ request_options: request_options
94
98
  )
95
99
  begin
96
100
  _response = @client.send(_request)
@@ -112,10 +116,11 @@ module Square
112
116
  # @return [Square::Types::BatchGetInventoryCountsResponse]
113
117
  def deprecated_batch_get_counts(request_options: {}, **params)
114
118
  _request = Square::Internal::JSON::Request.new(
115
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
119
+ base_url: request_options[:base_url],
116
120
  method: "POST",
117
121
  path: "v2/inventory/batch-retrieve-counts",
118
- body: Square::Types::BatchGetInventoryCountsRequest.new(params).to_h
122
+ body: Square::Types::BatchGetInventoryCountsRequest.new(params).to_h,
123
+ request_options: request_options
119
124
  )
120
125
  begin
121
126
  _response = @client.send(_request)
@@ -140,10 +145,11 @@ module Square
140
145
  # @return [Square::Types::BatchChangeInventoryResponse]
141
146
  def batch_create_changes(request_options: {}, **params)
142
147
  _request = Square::Internal::JSON::Request.new(
143
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
148
+ base_url: request_options[:base_url],
144
149
  method: "POST",
145
150
  path: "v2/inventory/changes/batch-create",
146
- body: Square::Types::BatchChangeInventoryRequest.new(params).to_h
151
+ body: Square::Types::BatchChangeInventoryRequest.new(params).to_h,
152
+ request_options: request_options
147
153
  )
148
154
  begin
149
155
  _response = @client.send(_request)
@@ -177,10 +183,11 @@ module Square
177
183
  ) do |next_cursor|
178
184
  params[:cursor] = next_cursor
179
185
  _request = Square::Internal::JSON::Request.new(
180
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
186
+ base_url: request_options[:base_url],
181
187
  method: "POST",
182
188
  path: "v2/inventory/changes/batch-retrieve",
183
- body: Square::Types::BatchRetrieveInventoryChangesRequest.new(params).to_h
189
+ body: Square::Types::BatchRetrieveInventoryChangesRequest.new(params).to_h,
190
+ request_options: request_options
184
191
  )
185
192
  begin
186
193
  _response = @client.send(_request)
@@ -218,10 +225,11 @@ module Square
218
225
  ) do |next_cursor|
219
226
  params[:cursor] = next_cursor
220
227
  _request = Square::Internal::JSON::Request.new(
221
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
228
+ base_url: request_options[:base_url],
222
229
  method: "POST",
223
230
  path: "v2/inventory/counts/batch-retrieve",
224
- body: Square::Types::BatchGetInventoryCountsRequest.new(params).to_h
231
+ body: Square::Types::BatchGetInventoryCountsRequest.new(params).to_h,
232
+ request_options: request_options
225
233
  )
226
234
  begin
227
235
  _response = @client.send(_request)
@@ -244,9 +252,10 @@ module Square
244
252
  # @return [Square::Types::GetInventoryPhysicalCountResponse]
245
253
  def deprecated_get_physical_count(request_options: {}, **params)
246
254
  _request = Square::Internal::JSON::Request.new(
247
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
255
+ base_url: request_options[:base_url],
248
256
  method: "GET",
249
- path: "v2/inventory/physical-count/#{params[:physical_count_id]}"
257
+ path: "v2/inventory/physical-count/#{params[:physical_count_id]}",
258
+ request_options: request_options
250
259
  )
251
260
  begin
252
261
  _response = @client.send(_request)
@@ -268,9 +277,10 @@ module Square
268
277
  # @return [Square::Types::GetInventoryPhysicalCountResponse]
269
278
  def get_physical_count(request_options: {}, **params)
270
279
  _request = Square::Internal::JSON::Request.new(
271
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
280
+ base_url: request_options[:base_url],
272
281
  method: "GET",
273
- path: "v2/inventory/physical-counts/#{params[:physical_count_id]}"
282
+ path: "v2/inventory/physical-counts/#{params[:physical_count_id]}",
283
+ request_options: request_options
274
284
  )
275
285
  begin
276
286
  _response = @client.send(_request)
@@ -292,9 +302,10 @@ module Square
292
302
  # @return [Square::Types::GetInventoryTransferResponse]
293
303
  def get_transfer(request_options: {}, **params)
294
304
  _request = Square::Internal::JSON::Request.new(
295
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
305
+ base_url: request_options[:base_url],
296
306
  method: "GET",
297
- path: "v2/inventory/transfers/#{params[:transfer_id]}"
307
+ path: "v2/inventory/transfers/#{params[:transfer_id]}",
308
+ request_options: request_options
298
309
  )
299
310
  begin
300
311
  _response = @client.send(_request)
@@ -329,10 +340,11 @@ module Square
329
340
  ) do |next_cursor|
330
341
  _query[:cursor] = next_cursor
331
342
  _request = Square::Internal::JSON::Request.new(
332
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
343
+ base_url: request_options[:base_url],
333
344
  method: "GET",
334
345
  path: "v2/inventory/#{params[:catalog_object_id]}",
335
- query: _query
346
+ query: _query,
347
+ request_options: request_options
336
348
  )
337
349
  begin
338
350
  _response = @client.send(_request)
@@ -377,10 +389,11 @@ module Square
377
389
  ) do |next_cursor|
378
390
  _query[:cursor] = next_cursor
379
391
  _request = Square::Internal::JSON::Request.new(
380
- base_url: request_options[:base_url] || Square::Environment::PRODUCTION,
392
+ base_url: request_options[:base_url],
381
393
  method: "GET",
382
394
  path: "v2/inventory/#{params[:catalog_object_id]}/changes",
383
- query: _query
395
+ query: _query,
396
+ request_options: request_options
384
397
  )
385
398
  begin
386
399
  _response = @client.send(_request)
@@ -9,6 +9,7 @@ module Square
9
9
  field :amount_money, -> { Square::Types::Money }, optional: true, nullable: false
10
10
  field :tip_money, -> { Square::Types::Money }, optional: true, nullable: false
11
11
  field :app_fee_money, -> { Square::Types::Money }, optional: true, nullable: false
12
+ field :app_fee_allocations, -> { Internal::Types::Array[Object] }, optional: true, nullable: false
12
13
  field :delay_duration, -> { String }, optional: true, nullable: false
13
14
  field :delay_action, -> { String }, optional: true, nullable: false
14
15
  field :autocomplete, -> { Internal::Types::Boolean }, optional: true, nullable: false
@@ -7,6 +7,7 @@ module Square
7
7
  field :idempotency_key, -> { String }, optional: false, nullable: false
8
8
  field :amount_money, -> { Square::Types::Money }, optional: false, nullable: false
9
9
  field :app_fee_money, -> { Square::Types::Money }, optional: true, nullable: false
10
+ field :app_fee_allocations, -> { Internal::Types::Array[Object] }, optional: true, nullable: false
10
11
  field :payment_id, -> { String }, optional: true, nullable: false
11
12
  field :destination_id, -> { String }, optional: true, nullable: false
12
13
  field :unlinked, -> { Internal::Types::Boolean }, optional: true, nullable: false
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Square
4
+ module Reporting
5
+ class Client
6
+ # @param client [Square::Internal::Http::RawClient]
7
+ #
8
+ # @return [void]
9
+ def initialize(client:)
10
+ @client = client
11
+ end
12
+
13
+ # Describes the data available to query: the cubes, views, measures, dimensions, and segments you can reference in
14
+ # a reporting query. Call this first to discover the schema, then pass the members you need to `load`.
15
+ #
16
+ # @param request_options [Hash]
17
+ # @param params [Hash]
18
+ # @option request_options [String] :base_url
19
+ # @option request_options [Hash{String => Object}] :additional_headers
20
+ # @option request_options [Hash{String => Object}] :additional_query_parameters
21
+ # @option request_options [Hash{String => Object}] :additional_body_parameters
22
+ # @option request_options [Integer] :timeout_in_seconds
23
+ #
24
+ # @return [Square::Types::MetadataResponse]
25
+ def get_metadata(request_options: {}, **params)
26
+ Square::Internal::Types::Utils.normalize_keys(params)
27
+ request = Square::Internal::JSON::Request.new(
28
+ base_url: request_options[:base_url],
29
+ method: "GET",
30
+ path: "reporting/v1/meta",
31
+ request_options: request_options
32
+ )
33
+ begin
34
+ response = @client.send(request)
35
+ rescue Net::HTTPRequestTimeout
36
+ raise Square::Errors::TimeoutError
37
+ end
38
+ code = response.code.to_i
39
+ if code.between?(200, 299)
40
+ Square::Types::MetadataResponse.load(response.body)
41
+ else
42
+ error_class = Square::Errors::ResponseError.subclass_for_code(code)
43
+ raise error_class.new(response.body, code: code)
44
+ end
45
+ end
46
+
47
+ # Runs a reporting query against the discovered schema and returns the aggregated results. Long-running queries
48
+ # may return a "Continue wait" response while processing — retry the same request until results are ready.
49
+ #
50
+ # @param request_options [Hash]
51
+ # @param params [Square::Reporting::Types::LoadRequest]
52
+ # @option request_options [String] :base_url
53
+ # @option request_options [Hash{String => Object}] :additional_headers
54
+ # @option request_options [Hash{String => Object}] :additional_query_parameters
55
+ # @option request_options [Hash{String => Object}] :additional_body_parameters
56
+ # @option request_options [Integer] :timeout_in_seconds
57
+ #
58
+ # @return [Square::Types::LoadResponse]
59
+ def load(request_options: {}, **params)
60
+ params = Square::Internal::Types::Utils.normalize_keys(params)
61
+ request = Square::Internal::JSON::Request.new(
62
+ base_url: request_options[:base_url],
63
+ method: "POST",
64
+ path: "reporting/v1/load",
65
+ body: Square::Reporting::Types::LoadRequest.new(params).to_h,
66
+ request_options: request_options
67
+ )
68
+ begin
69
+ response = @client.send(request)
70
+ rescue Net::HTTPRequestTimeout
71
+ raise Square::Errors::TimeoutError
72
+ end
73
+ code = response.code.to_i
74
+ if code.between?(200, 299)
75
+ Square::Types::LoadResponse.load(response.body)
76
+ else
77
+ error_class = Square::Errors::ResponseError.subclass_for_code(code)
78
+ raise error_class.new(response.body, code: code)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Square
4
+ module Reporting
5
+ module Types
6
+ class LoadRequest < Internal::Types::Model
7
+ field :query_type, -> { String }, optional: true, nullable: false, api_name: "queryType"
8
+ field :cache, -> { Square::Types::CacheMode }, optional: true, nullable: false
9
+ field :query, -> { Square::Types::Query }, optional: true, nullable: false
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../square"
4
+
5
+ module Square
6
+ module Reporting
7
+ # Raised by {Client#load_and_wait} when a reporting query has not finished
8
+ # after the configured maximum number of attempts.
9
+ class ContinueWaitTimeoutError < StandardError; end
10
+
11
+ # Raised by {Client#load_and_wait} when the caller-supplied +should_cancel+
12
+ # predicate returns a truthy value, aborting the polling loop.
13
+ class PollingCancelledError < StandardError; end
14
+
15
+ # Custom, +.fernignore+-protected polling helper for the Reporting API.
16
+ #
17
+ # The +/reporting/v1/load+ endpoint is asynchronous: a query that is still
18
+ # being computed comes back as an HTTP 200 whose body is
19
+ # +{ "error": "Continue wait" }+ rather than the results. Callers are expected
20
+ # to re-send the identical request, with backoff, until real results arrive.
21
+ # This file reopens the generated {Square::Reporting::Client} to add
22
+ # +load_and_wait+, which owns that retry loop.
23
+ #
24
+ # @example
25
+ # require "square"
26
+ # require "square/reporting_helper"
27
+ #
28
+ # client = Square::Client.new(token: ENV.fetch("SQUARE_TOKEN"))
29
+ # response = client.reporting.load_and_wait(query: { ... })
30
+ class Client
31
+ # The value of the +error+ field the Reporting API returns on an HTTP 200
32
+ # while a +/reporting/v1/load+ query is still processing. It is NOT a
33
+ # failure — the identical request should be re-sent until real results
34
+ # arrive. See https://developer.squareup.com/docs/reporting-api/overview.
35
+ CONTINUE_WAIT_SENTINEL = "Continue wait"
36
+
37
+ # Default polling parameters for {#load_and_wait}.
38
+ DEFAULT_MAX_ATTEMPTS = 20
39
+ DEFAULT_INITIAL_DELAY = 2.0
40
+ DEFAULT_MAX_DELAY = 20.0
41
+ DEFAULT_BACKOFF_FACTOR = 2.0
42
+
43
+ # Granularity (seconds) at which a backoff wait checks +should_cancel+, so
44
+ # cancellation stays responsive even during a long delay.
45
+ POLL_TICK = 0.1
46
+
47
+ # Runs a reporting query and transparently polls until it resolves,
48
+ # returning the final {Square::Types::LoadResponse}.
49
+ #
50
+ # A query that is still being computed comes back as an HTTP 200 whose body
51
+ # is +{ "error": "Continue wait" }+. That body deserializes into a
52
+ # +LoadResponse+ whose +error+ key lands in the model's extra fields (it is
53
+ # not a declared field) while +results+ stays +nil+; that is the signal to
54
+ # retry. +load_and_wait+ re-sends the identical request with exponential
55
+ # backoff until real results arrive.
56
+ #
57
+ # @param request_options [Hash] forwarded verbatim to each underlying
58
+ # {#load} call.
59
+ # @param max_attempts [Integer] maximum number of +load+ calls before
60
+ # giving up. Defaults to {DEFAULT_MAX_ATTEMPTS}.
61
+ # @param initial_delay [Numeric] delay (seconds) before the first retry.
62
+ # Defaults to {DEFAULT_INITIAL_DELAY}.
63
+ # @param max_delay [Numeric] upper bound (seconds) on the backoff delay.
64
+ # Defaults to {DEFAULT_MAX_DELAY}.
65
+ # @param backoff_factor [Numeric] multiplier applied to the delay after each
66
+ # attempt. Defaults to {DEFAULT_BACKOFF_FACTOR}.
67
+ # @param should_cancel [#call, nil] optional predicate checked before each
68
+ # attempt and during each backoff wait; when it returns a truthy value the
69
+ # loop aborts with {PollingCancelledError}. This is the Ruby idiom for
70
+ # TypeScript's +AbortSignal+ / Go's +context.Context+.
71
+ # @param params [Hash] the reporting query, forwarded verbatim to {#load}
72
+ # (same keyword arguments as a direct +load+ call).
73
+ #
74
+ # @return [Square::Types::LoadResponse] the resolved response.
75
+ #
76
+ # @raise [ContinueWaitTimeoutError] if the query has not resolved within
77
+ # +max_attempts+ attempts.
78
+ # @raise [PollingCancelledError] if +should_cancel+ returns a truthy value.
79
+ def load_and_wait(
80
+ request_options: {},
81
+ max_attempts: DEFAULT_MAX_ATTEMPTS,
82
+ initial_delay: DEFAULT_INITIAL_DELAY,
83
+ max_delay: DEFAULT_MAX_DELAY,
84
+ backoff_factor: DEFAULT_BACKOFF_FACTOR,
85
+ should_cancel: nil,
86
+ **params
87
+ )
88
+ delay = initial_delay
89
+ attempt = 1
90
+
91
+ loop do
92
+ raise PollingCancelledError if should_cancel&.call
93
+
94
+ response = load(request_options: request_options, **params)
95
+ return response unless continue_wait?(response)
96
+
97
+ break if attempt >= max_attempts
98
+
99
+ cancellable_sleep(delay, should_cancel)
100
+
101
+ attempt += 1
102
+ delay = [delay * backoff_factor, max_delay].min
103
+ end
104
+
105
+ raise ContinueWaitTimeoutError,
106
+ "Reporting query did not complete after #{max_attempts} attempts " \
107
+ "(#{CONTINUE_WAIT_SENTINEL.inspect})"
108
+ end
109
+
110
+ private
111
+
112
+ # @param response [Square::Types::LoadResponse]
113
+ # @return [Boolean] whether +response+ is the "still processing" sentinel
114
+ # rather than a real result.
115
+ def continue_wait?(response)
116
+ return false if response.nil?
117
+
118
+ response.to_h["error"] == CONTINUE_WAIT_SENTINEL
119
+ end
120
+
121
+ # Sleeps for +seconds+, waking every {POLL_TICK} to honor +should_cancel+.
122
+ #
123
+ # @param seconds [Numeric]
124
+ # @param should_cancel [#call, nil]
125
+ # @return [void]
126
+ # @raise [PollingCancelledError] if +should_cancel+ becomes truthy.
127
+ def cancellable_sleep(seconds, should_cancel)
128
+ return if seconds <= 0
129
+
130
+ deadline = monotonic_now + seconds
131
+ loop do
132
+ raise PollingCancelledError if should_cancel&.call
133
+
134
+ remaining = deadline - monotonic_now
135
+ break if remaining <= 0
136
+
137
+ sleep([remaining, POLL_TICK].min)
138
+ end
139
+ end
140
+
141
+ # @return [Float] a monotonic clock reading in seconds.
142
+ def monotonic_now
143
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
144
+ end
145
+ end
146
+ end
147
+ end