terminal-shop 2.0.0 → 2.1.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 (177) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -8
  3. data/lib/terminal-shop/client.rb +1 -1
  4. data/lib/terminal-shop/models/address_create_params.rb +1 -1
  5. data/lib/terminal-shop/models/address_delete_params.rb +1 -1
  6. data/lib/terminal-shop/models/address_get_params.rb +1 -1
  7. data/lib/terminal-shop/models/address_list_params.rb +1 -1
  8. data/lib/terminal-shop/models/app_create_params.rb +1 -1
  9. data/lib/terminal-shop/models/app_delete_params.rb +1 -1
  10. data/lib/terminal-shop/models/app_get_params.rb +1 -1
  11. data/lib/terminal-shop/models/app_list_params.rb +1 -1
  12. data/lib/terminal-shop/models/card_collect_params.rb +1 -1
  13. data/lib/terminal-shop/models/card_create_params.rb +1 -1
  14. data/lib/terminal-shop/models/card_delete_params.rb +1 -1
  15. data/lib/terminal-shop/models/card_get_params.rb +1 -1
  16. data/lib/terminal-shop/models/card_list_params.rb +1 -1
  17. data/lib/terminal-shop/models/cart_clear_params.rb +1 -1
  18. data/lib/terminal-shop/models/cart_convert_params.rb +1 -1
  19. data/lib/terminal-shop/models/cart_get_params.rb +1 -1
  20. data/lib/terminal-shop/models/cart_set_address_params.rb +1 -1
  21. data/lib/terminal-shop/models/cart_set_card_params.rb +1 -1
  22. data/lib/terminal-shop/models/cart_set_item_params.rb +1 -1
  23. data/lib/terminal-shop/models/email_create_params.rb +1 -1
  24. data/lib/terminal-shop/models/order_create_params.rb +1 -1
  25. data/lib/terminal-shop/models/order_get_params.rb +1 -1
  26. data/lib/terminal-shop/models/order_list_params.rb +1 -1
  27. data/lib/terminal-shop/models/product_get_params.rb +1 -1
  28. data/lib/terminal-shop/models/product_list_params.rb +1 -1
  29. data/lib/terminal-shop/models/profile_me_params.rb +1 -1
  30. data/lib/terminal-shop/models/profile_update_params.rb +1 -1
  31. data/lib/terminal-shop/models/subscription_create_params.rb +1 -1
  32. data/lib/terminal-shop/models/subscription_delete_params.rb +1 -1
  33. data/lib/terminal-shop/models/subscription_get_params.rb +1 -1
  34. data/lib/terminal-shop/models/subscription_list_params.rb +1 -1
  35. data/lib/terminal-shop/models/token_create_params.rb +1 -1
  36. data/lib/terminal-shop/models/token_delete_params.rb +1 -1
  37. data/lib/terminal-shop/models/token_get_params.rb +1 -1
  38. data/lib/terminal-shop/models/token_list_params.rb +1 -1
  39. data/lib/terminal-shop/models/view_init_params.rb +1 -1
  40. data/lib/terminal-shop/request_options.rb +0 -33
  41. data/lib/terminal-shop/transport/base_client.rb +459 -0
  42. data/lib/terminal-shop/transport/pooled_net_requester.rb +182 -0
  43. data/lib/terminal-shop/type/array_of.rb +110 -0
  44. data/lib/terminal-shop/type/base_model.rb +362 -0
  45. data/lib/terminal-shop/type/base_page.rb +61 -0
  46. data/lib/terminal-shop/type/boolean_model.rb +52 -0
  47. data/lib/terminal-shop/type/converter.rb +217 -0
  48. data/lib/terminal-shop/type/enum.rb +101 -0
  49. data/lib/terminal-shop/type/hash_of.rb +136 -0
  50. data/lib/terminal-shop/type/request_parameters.rb +38 -0
  51. data/lib/terminal-shop/type/union.rb +183 -0
  52. data/lib/terminal-shop/type/unknown.rb +56 -0
  53. data/lib/terminal-shop/type.rb +23 -0
  54. data/lib/terminal-shop/util.rb +3 -5
  55. data/lib/terminal-shop/version.rb +1 -1
  56. data/lib/terminal-shop.rb +14 -6
  57. data/rbi/lib/terminal-shop/client.rbi +1 -1
  58. data/rbi/lib/terminal-shop/models/address_create_params.rbi +12 -2
  59. data/rbi/lib/terminal-shop/models/address_delete_params.rbi +1 -1
  60. data/rbi/lib/terminal-shop/models/address_get_params.rbi +1 -1
  61. data/rbi/lib/terminal-shop/models/address_list_params.rbi +1 -1
  62. data/rbi/lib/terminal-shop/models/app_create_params.rbi +1 -1
  63. data/rbi/lib/terminal-shop/models/app_delete_params.rbi +1 -1
  64. data/rbi/lib/terminal-shop/models/app_get_params.rbi +1 -1
  65. data/rbi/lib/terminal-shop/models/app_list_params.rbi +1 -1
  66. data/rbi/lib/terminal-shop/models/card_collect_params.rbi +1 -1
  67. data/rbi/lib/terminal-shop/models/card_create_params.rbi +1 -1
  68. data/rbi/lib/terminal-shop/models/card_delete_params.rbi +1 -1
  69. data/rbi/lib/terminal-shop/models/card_get_params.rbi +1 -1
  70. data/rbi/lib/terminal-shop/models/card_list_params.rbi +1 -1
  71. data/rbi/lib/terminal-shop/models/cart_clear_params.rbi +1 -1
  72. data/rbi/lib/terminal-shop/models/cart_convert_params.rbi +1 -1
  73. data/rbi/lib/terminal-shop/models/cart_get_params.rbi +1 -1
  74. data/rbi/lib/terminal-shop/models/cart_set_address_params.rbi +1 -1
  75. data/rbi/lib/terminal-shop/models/cart_set_card_params.rbi +1 -1
  76. data/rbi/lib/terminal-shop/models/cart_set_item_params.rbi +1 -1
  77. data/rbi/lib/terminal-shop/models/email_create_params.rbi +1 -1
  78. data/rbi/lib/terminal-shop/models/order_create_params.rbi +1 -1
  79. data/rbi/lib/terminal-shop/models/order_get_params.rbi +1 -1
  80. data/rbi/lib/terminal-shop/models/order_list_params.rbi +1 -1
  81. data/rbi/lib/terminal-shop/models/product_get_params.rbi +1 -1
  82. data/rbi/lib/terminal-shop/models/product_list_params.rbi +1 -1
  83. data/rbi/lib/terminal-shop/models/profile_me_params.rbi +1 -1
  84. data/rbi/lib/terminal-shop/models/profile_update_params.rbi +1 -1
  85. data/rbi/lib/terminal-shop/models/subscription_create_params.rbi +1 -1
  86. data/rbi/lib/terminal-shop/models/subscription_delete_params.rbi +1 -1
  87. data/rbi/lib/terminal-shop/models/subscription_get_params.rbi +1 -1
  88. data/rbi/lib/terminal-shop/models/subscription_list_params.rbi +1 -1
  89. data/rbi/lib/terminal-shop/models/token_create_params.rbi +1 -1
  90. data/rbi/lib/terminal-shop/models/token_delete_params.rbi +1 -1
  91. data/rbi/lib/terminal-shop/models/token_get_params.rbi +1 -1
  92. data/rbi/lib/terminal-shop/models/token_list_params.rbi +1 -1
  93. data/rbi/lib/terminal-shop/models/view_init_params.rbi +1 -1
  94. data/rbi/lib/terminal-shop/models/view_init_response.rbi +12 -1
  95. data/rbi/lib/terminal-shop/request_options.rbi +0 -15
  96. data/rbi/lib/terminal-shop/transport/base_client.rbi +208 -0
  97. data/rbi/lib/terminal-shop/transport/pooled_net_requester.rbi +64 -0
  98. data/rbi/lib/terminal-shop/type/array_of.rbi +82 -0
  99. data/rbi/lib/terminal-shop/type/base_model.rbi +194 -0
  100. data/rbi/lib/terminal-shop/type/base_page.rbi +38 -0
  101. data/rbi/lib/terminal-shop/type/boolean_model.rbi +41 -0
  102. data/rbi/lib/terminal-shop/type/converter.rbi +101 -0
  103. data/rbi/lib/terminal-shop/type/enum.rbi +58 -0
  104. data/rbi/lib/terminal-shop/type/hash_of.rbi +85 -0
  105. data/rbi/lib/terminal-shop/type/request_parameters.rbi +20 -0
  106. data/rbi/lib/terminal-shop/type/union.rbi +68 -0
  107. data/rbi/lib/terminal-shop/type/unknown.rbi +37 -0
  108. data/rbi/lib/terminal-shop/type.rbi +23 -0
  109. data/rbi/lib/terminal-shop/version.rbi +1 -1
  110. data/sig/terminal-shop/client.rbs +1 -1
  111. data/sig/terminal-shop/models/address_create_params.rbs +1 -1
  112. data/sig/terminal-shop/models/address_delete_params.rbs +1 -1
  113. data/sig/terminal-shop/models/address_get_params.rbs +1 -1
  114. data/sig/terminal-shop/models/address_list_params.rbs +1 -1
  115. data/sig/terminal-shop/models/app_create_params.rbs +1 -1
  116. data/sig/terminal-shop/models/app_delete_params.rbs +1 -1
  117. data/sig/terminal-shop/models/app_get_params.rbs +1 -1
  118. data/sig/terminal-shop/models/app_list_params.rbs +1 -1
  119. data/sig/terminal-shop/models/card_collect_params.rbs +1 -1
  120. data/sig/terminal-shop/models/card_create_params.rbs +1 -1
  121. data/sig/terminal-shop/models/card_delete_params.rbs +1 -1
  122. data/sig/terminal-shop/models/card_get_params.rbs +1 -1
  123. data/sig/terminal-shop/models/card_list_params.rbs +1 -1
  124. data/sig/terminal-shop/models/cart_clear_params.rbs +1 -1
  125. data/sig/terminal-shop/models/cart_convert_params.rbs +1 -1
  126. data/sig/terminal-shop/models/cart_get_params.rbs +1 -1
  127. data/sig/terminal-shop/models/cart_set_address_params.rbs +1 -1
  128. data/sig/terminal-shop/models/cart_set_card_params.rbs +1 -1
  129. data/sig/terminal-shop/models/cart_set_item_params.rbs +1 -1
  130. data/sig/terminal-shop/models/email_create_params.rbs +1 -1
  131. data/sig/terminal-shop/models/order_create_params.rbs +1 -1
  132. data/sig/terminal-shop/models/order_get_params.rbs +1 -1
  133. data/sig/terminal-shop/models/order_list_params.rbs +1 -1
  134. data/sig/terminal-shop/models/product_get_params.rbs +1 -1
  135. data/sig/terminal-shop/models/product_list_params.rbs +1 -1
  136. data/sig/terminal-shop/models/profile_me_params.rbs +1 -1
  137. data/sig/terminal-shop/models/profile_update_params.rbs +1 -1
  138. data/sig/terminal-shop/models/subscription_create_params.rbs +1 -1
  139. data/sig/terminal-shop/models/subscription_delete_params.rbs +1 -1
  140. data/sig/terminal-shop/models/subscription_get_params.rbs +1 -1
  141. data/sig/terminal-shop/models/subscription_list_params.rbs +1 -1
  142. data/sig/terminal-shop/models/token_create_params.rbs +1 -1
  143. data/sig/terminal-shop/models/token_delete_params.rbs +1 -1
  144. data/sig/terminal-shop/models/token_get_params.rbs +1 -1
  145. data/sig/terminal-shop/models/token_list_params.rbs +1 -1
  146. data/sig/terminal-shop/models/view_init_params.rbs +1 -1
  147. data/sig/terminal-shop/request_options.rbs +0 -10
  148. data/sig/terminal-shop/transport/base_client.rbs +110 -0
  149. data/sig/terminal-shop/transport/pooled_net_requester.rbs +39 -0
  150. data/sig/terminal-shop/type/array_of.rbs +36 -0
  151. data/sig/terminal-shop/type/base_model.rbs +73 -0
  152. data/sig/terminal-shop/type/base_page.rbs +22 -0
  153. data/sig/terminal-shop/type/boolean_model.rbs +18 -0
  154. data/sig/terminal-shop/type/converter.rbs +42 -0
  155. data/sig/terminal-shop/type/enum.rbs +22 -0
  156. data/sig/terminal-shop/type/hash_of.rbs +36 -0
  157. data/sig/terminal-shop/type/request_parameters.rbs +13 -0
  158. data/sig/terminal-shop/type/union.rbs +40 -0
  159. data/sig/terminal-shop/type/unknown.rbs +18 -0
  160. data/sig/terminal-shop/type.rbs +22 -0
  161. data/sig/terminal-shop/version.rbs +1 -1
  162. metadata +41 -17
  163. data/lib/terminal-shop/base_client.rb +0 -457
  164. data/lib/terminal-shop/base_model.rb +0 -1201
  165. data/lib/terminal-shop/base_page.rb +0 -59
  166. data/lib/terminal-shop/extern.rb +0 -7
  167. data/lib/terminal-shop/pooled_net_requester.rb +0 -180
  168. data/rbi/lib/terminal-shop/base_client.rbi +0 -197
  169. data/rbi/lib/terminal-shop/base_model.rbi +0 -645
  170. data/rbi/lib/terminal-shop/base_page.rbi +0 -36
  171. data/rbi/lib/terminal-shop/extern.rbi +0 -7
  172. data/rbi/lib/terminal-shop/pooled_net_requester.rbi +0 -59
  173. data/sig/terminal-shop/base_client.rbs +0 -108
  174. data/sig/terminal-shop/base_model.rbs +0 -262
  175. data/sig/terminal-shop/base_page.rbs +0 -20
  176. data/sig/terminal-shop/extern.rbs +0 -4
  177. data/sig/terminal-shop/pooled_net_requester.rbs +0 -37
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terminal-shop
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Terminal
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-26 00:00:00.000000000 Z
11
+ date: 2025-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool
@@ -33,12 +33,8 @@ extra_rdoc_files:
33
33
  files:
34
34
  - README.md
35
35
  - lib/terminal-shop.rb
36
- - lib/terminal-shop/base_client.rb
37
- - lib/terminal-shop/base_model.rb
38
- - lib/terminal-shop/base_page.rb
39
36
  - lib/terminal-shop/client.rb
40
37
  - lib/terminal-shop/errors.rb
41
- - lib/terminal-shop/extern.rb
42
38
  - lib/terminal-shop/models/address.rb
43
39
  - lib/terminal-shop/models/address_create_params.rb
44
40
  - lib/terminal-shop/models/address_create_response.rb
@@ -122,7 +118,6 @@ files:
122
118
  - lib/terminal-shop/models/token_list_response.rb
123
119
  - lib/terminal-shop/models/view_init_params.rb
124
120
  - lib/terminal-shop/models/view_init_response.rb
125
- - lib/terminal-shop/pooled_net_requester.rb
126
121
  - lib/terminal-shop/request_options.rb
127
122
  - lib/terminal-shop/resources/address.rb
128
123
  - lib/terminal-shop/resources/app.rb
@@ -135,15 +130,24 @@ files:
135
130
  - lib/terminal-shop/resources/subscription.rb
136
131
  - lib/terminal-shop/resources/token.rb
137
132
  - lib/terminal-shop/resources/view.rb
133
+ - lib/terminal-shop/transport/base_client.rb
134
+ - lib/terminal-shop/transport/pooled_net_requester.rb
135
+ - lib/terminal-shop/type.rb
136
+ - lib/terminal-shop/type/array_of.rb
137
+ - lib/terminal-shop/type/base_model.rb
138
+ - lib/terminal-shop/type/base_page.rb
139
+ - lib/terminal-shop/type/boolean_model.rb
140
+ - lib/terminal-shop/type/converter.rb
141
+ - lib/terminal-shop/type/enum.rb
142
+ - lib/terminal-shop/type/hash_of.rb
143
+ - lib/terminal-shop/type/request_parameters.rb
144
+ - lib/terminal-shop/type/union.rb
145
+ - lib/terminal-shop/type/unknown.rb
138
146
  - lib/terminal-shop/util.rb
139
147
  - lib/terminal-shop/version.rb
140
148
  - manifest.yaml
141
- - rbi/lib/terminal-shop/base_client.rbi
142
- - rbi/lib/terminal-shop/base_model.rbi
143
- - rbi/lib/terminal-shop/base_page.rbi
144
149
  - rbi/lib/terminal-shop/client.rbi
145
150
  - rbi/lib/terminal-shop/errors.rbi
146
- - rbi/lib/terminal-shop/extern.rbi
147
151
  - rbi/lib/terminal-shop/models/address.rbi
148
152
  - rbi/lib/terminal-shop/models/address_create_params.rbi
149
153
  - rbi/lib/terminal-shop/models/address_create_response.rbi
@@ -227,7 +231,6 @@ files:
227
231
  - rbi/lib/terminal-shop/models/token_list_response.rbi
228
232
  - rbi/lib/terminal-shop/models/view_init_params.rbi
229
233
  - rbi/lib/terminal-shop/models/view_init_response.rbi
230
- - rbi/lib/terminal-shop/pooled_net_requester.rbi
231
234
  - rbi/lib/terminal-shop/request_options.rbi
232
235
  - rbi/lib/terminal-shop/resources/address.rbi
233
236
  - rbi/lib/terminal-shop/resources/app.rbi
@@ -240,14 +243,23 @@ files:
240
243
  - rbi/lib/terminal-shop/resources/subscription.rbi
241
244
  - rbi/lib/terminal-shop/resources/token.rbi
242
245
  - rbi/lib/terminal-shop/resources/view.rbi
246
+ - rbi/lib/terminal-shop/transport/base_client.rbi
247
+ - rbi/lib/terminal-shop/transport/pooled_net_requester.rbi
248
+ - rbi/lib/terminal-shop/type.rbi
249
+ - rbi/lib/terminal-shop/type/array_of.rbi
250
+ - rbi/lib/terminal-shop/type/base_model.rbi
251
+ - rbi/lib/terminal-shop/type/base_page.rbi
252
+ - rbi/lib/terminal-shop/type/boolean_model.rbi
253
+ - rbi/lib/terminal-shop/type/converter.rbi
254
+ - rbi/lib/terminal-shop/type/enum.rbi
255
+ - rbi/lib/terminal-shop/type/hash_of.rbi
256
+ - rbi/lib/terminal-shop/type/request_parameters.rbi
257
+ - rbi/lib/terminal-shop/type/union.rbi
258
+ - rbi/lib/terminal-shop/type/unknown.rbi
243
259
  - rbi/lib/terminal-shop/util.rbi
244
260
  - rbi/lib/terminal-shop/version.rbi
245
- - sig/terminal-shop/base_client.rbs
246
- - sig/terminal-shop/base_model.rbs
247
- - sig/terminal-shop/base_page.rbs
248
261
  - sig/terminal-shop/client.rbs
249
262
  - sig/terminal-shop/errors.rbs
250
- - sig/terminal-shop/extern.rbs
251
263
  - sig/terminal-shop/models/address.rbs
252
264
  - sig/terminal-shop/models/address_create_params.rbs
253
265
  - sig/terminal-shop/models/address_create_response.rbs
@@ -331,7 +343,6 @@ files:
331
343
  - sig/terminal-shop/models/token_list_response.rbs
332
344
  - sig/terminal-shop/models/view_init_params.rbs
333
345
  - sig/terminal-shop/models/view_init_response.rbs
334
- - sig/terminal-shop/pooled_net_requester.rbs
335
346
  - sig/terminal-shop/request_options.rbs
336
347
  - sig/terminal-shop/resources/address.rbs
337
348
  - sig/terminal-shop/resources/app.rbs
@@ -344,6 +355,19 @@ files:
344
355
  - sig/terminal-shop/resources/subscription.rbs
345
356
  - sig/terminal-shop/resources/token.rbs
346
357
  - sig/terminal-shop/resources/view.rbs
358
+ - sig/terminal-shop/transport/base_client.rbs
359
+ - sig/terminal-shop/transport/pooled_net_requester.rbs
360
+ - sig/terminal-shop/type.rbs
361
+ - sig/terminal-shop/type/array_of.rbs
362
+ - sig/terminal-shop/type/base_model.rbs
363
+ - sig/terminal-shop/type/base_page.rbs
364
+ - sig/terminal-shop/type/boolean_model.rbs
365
+ - sig/terminal-shop/type/converter.rbs
366
+ - sig/terminal-shop/type/enum.rbs
367
+ - sig/terminal-shop/type/hash_of.rbs
368
+ - sig/terminal-shop/type/request_parameters.rbs
369
+ - sig/terminal-shop/type/union.rbs
370
+ - sig/terminal-shop/type/unknown.rbs
347
371
  - sig/terminal-shop/util.rbs
348
372
  - sig/terminal-shop/version.rbs
349
373
  homepage: https://gemdocs.org/gems/terminal-shop
@@ -1,457 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TerminalShop
4
- # @api private
5
- #
6
- # @abstract
7
- class BaseClient
8
- # from whatwg fetch spec
9
- MAX_REDIRECTS = 20
10
-
11
- # rubocop:disable Style/MutableConstant
12
- PLATFORM_HEADERS =
13
- {
14
- "x-stainless-arch" => TerminalShop::Util.arch,
15
- "x-stainless-lang" => "ruby",
16
- "x-stainless-os" => TerminalShop::Util.os,
17
- "x-stainless-package-version" => TerminalShop::VERSION,
18
- "x-stainless-runtime" => ::RUBY_ENGINE,
19
- "x-stainless-runtime-version" => ::RUBY_ENGINE_VERSION
20
- }
21
- # rubocop:enable Style/MutableConstant
22
-
23
- class << self
24
- # @api private
25
- #
26
- # @param req [Hash{Symbol=>Object}]
27
- #
28
- # @raise [ArgumentError]
29
- def validate!(req)
30
- keys = [:method, :path, :query, :headers, :body, :unwrap, :page, :stream, :model, :options]
31
- case req
32
- in Hash
33
- req.each_key do |k|
34
- unless keys.include?(k)
35
- raise ArgumentError.new("Request `req` keys must be one of #{keys}, got #{k.inspect}")
36
- end
37
- end
38
- else
39
- raise ArgumentError.new("Request `req` must be a Hash or RequestOptions, got #{req.inspect}")
40
- end
41
- end
42
-
43
- # @api private
44
- #
45
- # @param status [Integer]
46
- # @param headers [Hash{String=>String}, Net::HTTPHeader]
47
- #
48
- # @return [Boolean]
49
- def should_retry?(status, headers:)
50
- coerced = TerminalShop::Util.coerce_boolean(headers["x-should-retry"])
51
- case [coerced, status]
52
- in [true | false, _]
53
- coerced
54
- in [_, 408 | 409 | 429 | (500..)]
55
- # retry on:
56
- # 408: timeouts
57
- # 409: locks
58
- # 429: rate limits
59
- # 500+: unknown errors
60
- true
61
- else
62
- false
63
- end
64
- end
65
-
66
- # @api private
67
- #
68
- # @param request [Hash{Symbol=>Object}] .
69
- #
70
- # @option request [Symbol] :method
71
- #
72
- # @option request [URI::Generic] :url
73
- #
74
- # @option request [Hash{String=>String}] :headers
75
- #
76
- # @option request [Object] :body
77
- #
78
- # @option request [Integer] :max_retries
79
- #
80
- # @option request [Float] :timeout
81
- #
82
- # @param status [Integer]
83
- #
84
- # @param response_headers [Hash{String=>String}, Net::HTTPHeader]
85
- #
86
- # @return [Hash{Symbol=>Object}]
87
- def follow_redirect(request, status:, response_headers:)
88
- method, url, headers = request.fetch_values(:method, :url, :headers)
89
- location =
90
- Kernel.then do
91
- URI.join(url, response_headers["location"])
92
- rescue ArgumentError
93
- message = "Server responded with status #{status} but no valid location header."
94
- raise TerminalShop::APIConnectionError.new(url: url, message: message)
95
- end
96
-
97
- request = {**request, url: location}
98
-
99
- case [url.scheme, location.scheme]
100
- in ["https", "http"]
101
- message = "Tried to redirect to a insecure URL"
102
- raise TerminalShop::APIConnectionError.new(url: url, message: message)
103
- else
104
- nil
105
- end
106
-
107
- # from whatwg fetch spec
108
- case [status, method]
109
- in [301 | 302, :post] | [303, _]
110
- drop = %w[content-encoding content-language content-length content-location content-type]
111
- request = {
112
- **request,
113
- method: method == :head ? :head : :get,
114
- headers: headers.except(*drop),
115
- body: nil
116
- }
117
- else
118
- end
119
-
120
- # from undici
121
- if TerminalShop::Util.uri_origin(url) != TerminalShop::Util.uri_origin(location)
122
- drop = %w[authorization cookie host proxy-authorization]
123
- request = {**request, headers: request.fetch(:headers).except(*drop)}
124
- end
125
-
126
- request
127
- end
128
-
129
- # @api private
130
- #
131
- # @param status [Integer, TerminalShop::APIConnectionError]
132
- # @param stream [Enumerable, nil]
133
- def reap_connection!(status, stream:)
134
- case status
135
- in (..199) | (300..499)
136
- stream&.each { next }
137
- in TerminalShop::APIConnectionError | (500..)
138
- TerminalShop::Util.close_fused!(stream)
139
- else
140
- end
141
- end
142
- end
143
-
144
- # @api private
145
- # @return [TerminalShop::PooledNetRequester]
146
- attr_accessor :requester
147
-
148
- # @api private
149
- #
150
- # @param base_url [String]
151
- # @param timeout [Float]
152
- # @param max_retries [Integer]
153
- # @param initial_retry_delay [Float]
154
- # @param max_retry_delay [Float]
155
- # @param headers [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}]
156
- # @param idempotency_header [String, nil]
157
- def initialize(
158
- base_url:,
159
- timeout: 0.0,
160
- max_retries: 0,
161
- initial_retry_delay: 0.0,
162
- max_retry_delay: 0.0,
163
- headers: {},
164
- idempotency_header: nil
165
- )
166
- @requester = TerminalShop::PooledNetRequester.new
167
- @headers = TerminalShop::Util.normalized_headers(
168
- self.class::PLATFORM_HEADERS,
169
- {
170
- "accept" => "application/json",
171
- "content-type" => "application/json"
172
- },
173
- headers
174
- )
175
- @base_url = TerminalShop::Util.parse_uri(base_url)
176
- @idempotency_header = idempotency_header&.to_s&.downcase
177
- @max_retries = max_retries
178
- @timeout = timeout
179
- @initial_retry_delay = initial_retry_delay
180
- @max_retry_delay = max_retry_delay
181
- end
182
-
183
- # @api private
184
- #
185
- # @return [Hash{String=>String}]
186
- private def auth_headers = {}
187
-
188
- # @api private
189
- #
190
- # @return [String]
191
- private def generate_idempotency_key = "stainless-ruby-retry-#{SecureRandom.uuid}"
192
-
193
- # @api private
194
- #
195
- # @param req [Hash{Symbol=>Object}] .
196
- #
197
- # @option req [Symbol] :method
198
- #
199
- # @option req [String, Array<String>] :path
200
- #
201
- # @option req [Hash{String=>Array<String>, String, nil}, nil] :query
202
- #
203
- # @option req [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil] :headers
204
- #
205
- # @option req [Object, nil] :body
206
- #
207
- # @option req [Symbol, nil] :unwrap
208
- #
209
- # @option req [Class, nil] :page
210
- #
211
- # @option req [Class, nil] :stream
212
- #
213
- # @option req [TerminalShop::Converter, Class, nil] :model
214
- #
215
- # @param opts [Hash{Symbol=>Object}] .
216
- #
217
- # @option opts [String, nil] :idempotency_key
218
- #
219
- # @option opts [Hash{String=>Array<String>, String, nil}, nil] :extra_query
220
- #
221
- # @option opts [Hash{String=>String, nil}, nil] :extra_headers
222
- #
223
- # @option opts [Object, nil] :extra_body
224
- #
225
- # @option opts [Integer, nil] :max_retries
226
- #
227
- # @option opts [Float, nil] :timeout
228
- #
229
- # @return [Hash{Symbol=>Object}]
230
- private def build_request(req, opts)
231
- method, uninterpolated_path = req.fetch_values(:method, :path)
232
-
233
- path = TerminalShop::Util.interpolate_path(uninterpolated_path)
234
-
235
- query = TerminalShop::Util.deep_merge(req[:query].to_h, opts[:extra_query].to_h)
236
-
237
- headers = TerminalShop::Util.normalized_headers(
238
- @headers,
239
- auth_headers,
240
- req[:headers].to_h,
241
- opts[:extra_headers].to_h
242
- )
243
-
244
- if @idempotency_header &&
245
- !headers.key?(@idempotency_header) &&
246
- !Net::HTTP::IDEMPOTENT_METHODS_.include?(method.to_s.upcase)
247
- headers[@idempotency_header] = opts.fetch(:idempotency_key) { generate_idempotency_key }
248
- end
249
-
250
- unless headers.key?("x-stainless-retry-count")
251
- headers["x-stainless-retry-count"] = "0"
252
- end
253
-
254
- timeout = opts.fetch(:timeout, @timeout).to_f.clamp((0..))
255
- unless headers.key?("x-stainless-timeout") || timeout.zero?
256
- headers["x-stainless-timeout"] = timeout.to_s
257
- end
258
-
259
- headers.reject! { |_, v| v.to_s.empty? }
260
-
261
- body =
262
- case method
263
- in :get | :head | :options | :trace
264
- nil
265
- else
266
- TerminalShop::Util.deep_merge(*[req[:body], opts[:extra_body]].compact)
267
- end
268
-
269
- headers, encoded = TerminalShop::Util.encode_content(headers, body)
270
- {
271
- method: method,
272
- url: TerminalShop::Util.join_parsed_uri(@base_url, {**req, path: path, query: query}),
273
- headers: headers,
274
- body: encoded,
275
- max_retries: opts.fetch(:max_retries, @max_retries),
276
- timeout: timeout
277
- }
278
- end
279
-
280
- # @api private
281
- #
282
- # @param headers [Hash{String=>String}]
283
- # @param retry_count [Integer]
284
- #
285
- # @return [Float]
286
- private def retry_delay(headers, retry_count:)
287
- # Non-standard extension
288
- span = Float(headers["retry-after-ms"], exception: false)&.then { _1 / 1000 }
289
- return span if span
290
-
291
- retry_header = headers["retry-after"]
292
- return span if (span = Float(retry_header, exception: false))
293
-
294
- span = retry_header&.then do
295
- Time.httpdate(_1) - Time.now
296
- rescue ArgumentError
297
- nil
298
- end
299
- return span if span
300
-
301
- scale = retry_count**2
302
- jitter = 1 - (0.25 * rand)
303
- (@initial_retry_delay * scale * jitter).clamp(0, @max_retry_delay)
304
- end
305
-
306
- # @api private
307
- #
308
- # @param request [Hash{Symbol=>Object}] .
309
- #
310
- # @option request [Symbol] :method
311
- #
312
- # @option request [URI::Generic] :url
313
- #
314
- # @option request [Hash{String=>String}] :headers
315
- #
316
- # @option request [Object] :body
317
- #
318
- # @option request [Integer] :max_retries
319
- #
320
- # @option request [Float] :timeout
321
- #
322
- # @param redirect_count [Integer]
323
- #
324
- # @param retry_count [Integer]
325
- #
326
- # @param send_retry_header [Boolean]
327
- #
328
- # @raise [TerminalShop::APIError]
329
- # @return [Array(Integer, Net::HTTPResponse, Enumerable)]
330
- private def send_request(request, redirect_count:, retry_count:, send_retry_header:)
331
- url, headers, max_retries, timeout = request.fetch_values(:url, :headers, :max_retries, :timeout)
332
- input = {**request.except(:timeout), deadline: TerminalShop::Util.monotonic_secs + timeout}
333
-
334
- if send_retry_header
335
- headers["x-stainless-retry-count"] = retry_count.to_s
336
- end
337
-
338
- begin
339
- status, response, stream = @requester.execute(input)
340
- rescue TerminalShop::APIConnectionError => e
341
- status = e
342
- end
343
-
344
- case status
345
- in ..299
346
- [status, response, stream]
347
- in 300..399 if redirect_count >= self.class::MAX_REDIRECTS
348
- self.class.reap_connection!(status, stream: stream)
349
-
350
- message = "Failed to complete the request within #{self.class::MAX_REDIRECTS} redirects."
351
- raise TerminalShop::APIConnectionError.new(url: url, message: message)
352
- in 300..399
353
- self.class.reap_connection!(status, stream: stream)
354
-
355
- request = self.class.follow_redirect(request, status: status, response_headers: response)
356
- send_request(
357
- request,
358
- redirect_count: redirect_count + 1,
359
- retry_count: retry_count,
360
- send_retry_header: send_retry_header
361
- )
362
- in TerminalShop::APIConnectionError if retry_count >= max_retries
363
- raise status
364
- in (400..) if retry_count >= max_retries || !self.class.should_retry?(status, headers: response)
365
- decoded = Kernel.then do
366
- TerminalShop::Util.decode_content(response, stream: stream, suppress_error: true)
367
- ensure
368
- self.class.reap_connection!(status, stream: stream)
369
- end
370
-
371
- raise TerminalShop::APIStatusError.for(
372
- url: url,
373
- status: status,
374
- body: decoded,
375
- request: nil,
376
- response: response
377
- )
378
- in (400..) | TerminalShop::APIConnectionError
379
- self.class.reap_connection!(status, stream: stream)
380
-
381
- delay = retry_delay(response, retry_count: retry_count)
382
- sleep(delay)
383
-
384
- send_request(
385
- request,
386
- redirect_count: redirect_count,
387
- retry_count: retry_count + 1,
388
- send_retry_header: send_retry_header
389
- )
390
- end
391
- end
392
-
393
- # Execute the request specified by `req`. This is the method that all resource
394
- # methods call into.
395
- #
396
- # @param req [Hash{Symbol=>Object}] .
397
- #
398
- # @option req [Symbol] :method
399
- #
400
- # @option req [String, Array<String>] :path
401
- #
402
- # @option req [Hash{String=>Array<String>, String, nil}, nil] :query
403
- #
404
- # @option req [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil] :headers
405
- #
406
- # @option req [Object, nil] :body
407
- #
408
- # @option req [Symbol, nil] :unwrap
409
- #
410
- # @option req [Class, nil] :page
411
- #
412
- # @option req [Class, nil] :stream
413
- #
414
- # @option req [TerminalShop::Converter, Class, nil] :model
415
- #
416
- # @option req [TerminalShop::RequestOptions, Hash{Symbol=>Object}, nil] :options
417
- #
418
- # @raise [TerminalShop::APIError]
419
- # @return [Object]
420
- def request(req)
421
- self.class.validate!(req)
422
- model = req.fetch(:model) { TerminalShop::Unknown }
423
- opts = req[:options].to_h
424
- TerminalShop::RequestOptions.validate!(opts)
425
- request = build_request(req.except(:options), opts)
426
- url = request.fetch(:url)
427
-
428
- # Don't send the current retry count in the headers if the caller modified the header defaults.
429
- send_retry_header = request.fetch(:headers)["x-stainless-retry-count"] == "0"
430
- status, response, stream = send_request(
431
- request,
432
- redirect_count: 0,
433
- retry_count: 0,
434
- send_retry_header: send_retry_header
435
- )
436
-
437
- decoded = TerminalShop::Util.decode_content(response, stream: stream)
438
- case req
439
- in { stream: Class => st }
440
- st.new(model: model, url: url, status: status, response: response, stream: decoded)
441
- in { page: Class => page }
442
- page.new(client: self, req: req, headers: response, page_data: decoded)
443
- else
444
- unwrapped = TerminalShop::Util.dig(decoded, req[:unwrap])
445
- TerminalShop::Converter.coerce(model, unwrapped)
446
- end
447
- end
448
-
449
- # @return [String]
450
- def inspect
451
- # rubocop:disable Layout/LineLength
452
- base_url = TerminalShop::Util.unparse_uri(@base_url)
453
- "#<#{self.class.name}:0x#{object_id.to_s(16)} base_url=#{base_url} max_retries=#{@max_retries} timeout=#{@timeout}>"
454
- # rubocop:enable Layout/LineLength
455
- end
456
- end
457
- end