stigg 0.1.0.pre.alpha.1

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 (230) hide show
  1. checksums.yaml +7 -0
  2. data/.ignore +2 -0
  3. data/CHANGELOG.md +45 -0
  4. data/README.md +233 -0
  5. data/SECURITY.md +23 -0
  6. data/lib/stigg/client.rb +72 -0
  7. data/lib/stigg/errors.rb +228 -0
  8. data/lib/stigg/file_part.rb +58 -0
  9. data/lib/stigg/internal/my_cursor_id_page.rb +82 -0
  10. data/lib/stigg/internal/transport/base_client.rb +570 -0
  11. data/lib/stigg/internal/transport/pooled_net_requester.rb +210 -0
  12. data/lib/stigg/internal/type/array_of.rb +168 -0
  13. data/lib/stigg/internal/type/base_model.rb +529 -0
  14. data/lib/stigg/internal/type/base_page.rb +55 -0
  15. data/lib/stigg/internal/type/boolean.rb +77 -0
  16. data/lib/stigg/internal/type/converter.rb +327 -0
  17. data/lib/stigg/internal/type/enum.rb +131 -0
  18. data/lib/stigg/internal/type/file_input.rb +110 -0
  19. data/lib/stigg/internal/type/hash_of.rb +188 -0
  20. data/lib/stigg/internal/type/request_parameters.rb +42 -0
  21. data/lib/stigg/internal/type/union.rb +237 -0
  22. data/lib/stigg/internal/type/unknown.rb +81 -0
  23. data/lib/stigg/internal/util.rb +920 -0
  24. data/lib/stigg/internal.rb +20 -0
  25. data/lib/stigg/models/v1/coupon_create_params.rb +221 -0
  26. data/lib/stigg/models/v1/coupon_create_response.rb +317 -0
  27. data/lib/stigg/models/v1/coupon_list_params.rb +40 -0
  28. data/lib/stigg/models/v1/coupon_list_response.rb +324 -0
  29. data/lib/stigg/models/v1/coupon_retrieve_params.rb +16 -0
  30. data/lib/stigg/models/v1/coupon_retrieve_response.rb +317 -0
  31. data/lib/stigg/models/v1/customer_archive_params.rb +16 -0
  32. data/lib/stigg/models/v1/customer_create_params.rb +185 -0
  33. data/lib/stigg/models/v1/customer_list_params.rb +40 -0
  34. data/lib/stigg/models/v1/customer_list_response.rb +223 -0
  35. data/lib/stigg/models/v1/customer_response.rb +216 -0
  36. data/lib/stigg/models/v1/customer_retrieve_params.rb +16 -0
  37. data/lib/stigg/models/v1/customer_unarchive_params.rb +16 -0
  38. data/lib/stigg/models/v1/customer_update_params.rb +107 -0
  39. data/lib/stigg/models/v1/customers/payment_method_attach_params.rb +197 -0
  40. data/lib/stigg/models/v1/customers/payment_method_detach_params.rb +18 -0
  41. data/lib/stigg/models/v1/customers/promotional_create_params.rb +294 -0
  42. data/lib/stigg/models/v1/customers/promotional_create_response.rb +268 -0
  43. data/lib/stigg/models/v1/customers/promotional_revoke_params.rb +24 -0
  44. data/lib/stigg/models/v1/customers/promotional_revoke_response.rb +268 -0
  45. data/lib/stigg/models/v1/customers/usage_retrieve_params.rb +56 -0
  46. data/lib/stigg/models/v1/customers/usage_retrieve_response.rb +145 -0
  47. data/lib/stigg/models/v1/subscription_create_params.rb +214 -0
  48. data/lib/stigg/models/v1/subscription_create_response.rb +338 -0
  49. data/lib/stigg/models/v1/subscription_delegate_params.rb +24 -0
  50. data/lib/stigg/models/v1/subscription_delegate_response.rb +274 -0
  51. data/lib/stigg/models/v1/subscription_list_params.rb +60 -0
  52. data/lib/stigg/models/v1/subscription_list_response.rb +281 -0
  53. data/lib/stigg/models/v1/subscription_migrate_params.rb +37 -0
  54. data/lib/stigg/models/v1/subscription_migrate_response.rb +274 -0
  55. data/lib/stigg/models/v1/subscription_preview_params.rb +644 -0
  56. data/lib/stigg/models/v1/subscription_preview_response.rb +431 -0
  57. data/lib/stigg/models/v1/subscription_retrieve_params.rb +16 -0
  58. data/lib/stigg/models/v1/subscription_retrieve_response.rb +274 -0
  59. data/lib/stigg/models/v1/subscription_transfer_params.rb +28 -0
  60. data/lib/stigg/models/v1/subscription_transfer_response.rb +274 -0
  61. data/lib/stigg/models/v1/subscriptions/future_update_cancel_pending_payment_params.rb +18 -0
  62. data/lib/stigg/models/v1/subscriptions/future_update_cancel_pending_payment_response.rb +32 -0
  63. data/lib/stigg/models/v1/subscriptions/future_update_cancel_schedule_params.rb +18 -0
  64. data/lib/stigg/models/v1/subscriptions/future_update_cancel_schedule_response.rb +32 -0
  65. data/lib/stigg/models/v1_create_event_params.rb +87 -0
  66. data/lib/stigg/models/v1_create_event_response.rb +16 -0
  67. data/lib/stigg/models/v1_create_usage_params.rb +96 -0
  68. data/lib/stigg/models/v1_create_usage_response.rb +112 -0
  69. data/lib/stigg/models.rb +47 -0
  70. data/lib/stigg/request_options.rb +77 -0
  71. data/lib/stigg/resources/v1/coupons.rb +95 -0
  72. data/lib/stigg/resources/v1/customers/payment_method.rb +67 -0
  73. data/lib/stigg/resources/v1/customers/promotional.rb +69 -0
  74. data/lib/stigg/resources/v1/customers/usage.rb +59 -0
  75. data/lib/stigg/resources/v1/customers.rb +177 -0
  76. data/lib/stigg/resources/v1/subscriptions/future_update.rb +56 -0
  77. data/lib/stigg/resources/v1/subscriptions.rb +241 -0
  78. data/lib/stigg/resources/v1.rb +70 -0
  79. data/lib/stigg/version.rb +5 -0
  80. data/lib/stigg.rb +108 -0
  81. data/manifest.yaml +17 -0
  82. data/rbi/stigg/client.rbi +49 -0
  83. data/rbi/stigg/errors.rbi +205 -0
  84. data/rbi/stigg/file_part.rbi +37 -0
  85. data/rbi/stigg/internal/my_cursor_id_page.rbi +19 -0
  86. data/rbi/stigg/internal/transport/base_client.rbi +297 -0
  87. data/rbi/stigg/internal/transport/pooled_net_requester.rbi +82 -0
  88. data/rbi/stigg/internal/type/array_of.rbi +104 -0
  89. data/rbi/stigg/internal/type/base_model.rbi +299 -0
  90. data/rbi/stigg/internal/type/base_page.rbi +42 -0
  91. data/rbi/stigg/internal/type/boolean.rbi +58 -0
  92. data/rbi/stigg/internal/type/converter.rbi +204 -0
  93. data/rbi/stigg/internal/type/enum.rbi +82 -0
  94. data/rbi/stigg/internal/type/file_input.rbi +58 -0
  95. data/rbi/stigg/internal/type/hash_of.rbi +104 -0
  96. data/rbi/stigg/internal/type/request_parameters.rbi +29 -0
  97. data/rbi/stigg/internal/type/union.rbi +126 -0
  98. data/rbi/stigg/internal/type/unknown.rbi +58 -0
  99. data/rbi/stigg/internal/util.rbi +478 -0
  100. data/rbi/stigg/internal.rbi +16 -0
  101. data/rbi/stigg/models/v1/coupon_create_params.rbi +758 -0
  102. data/rbi/stigg/models/v1/coupon_create_response.rbi +986 -0
  103. data/rbi/stigg/models/v1/coupon_list_params.rbi +70 -0
  104. data/rbi/stigg/models/v1/coupon_list_response.rbi +983 -0
  105. data/rbi/stigg/models/v1/coupon_retrieve_params.rbi +29 -0
  106. data/rbi/stigg/models/v1/coupon_retrieve_response.rbi +986 -0
  107. data/rbi/stigg/models/v1/customer_archive_params.rbi +29 -0
  108. data/rbi/stigg/models/v1/customer_create_params.rbi +376 -0
  109. data/rbi/stigg/models/v1/customer_list_params.rbi +70 -0
  110. data/rbi/stigg/models/v1/customer_list_response.rbi +447 -0
  111. data/rbi/stigg/models/v1/customer_response.rbi +423 -0
  112. data/rbi/stigg/models/v1/customer_retrieve_params.rbi +29 -0
  113. data/rbi/stigg/models/v1/customer_unarchive_params.rbi +29 -0
  114. data/rbi/stigg/models/v1/customer_update_params.rbi +226 -0
  115. data/rbi/stigg/models/v1/customers/payment_method_attach_params.rbi +769 -0
  116. data/rbi/stigg/models/v1/customers/payment_method_detach_params.rbi +34 -0
  117. data/rbi/stigg/models/v1/customers/promotional_create_params.rbi +639 -0
  118. data/rbi/stigg/models/v1/customers/promotional_create_response.rbi +495 -0
  119. data/rbi/stigg/models/v1/customers/promotional_revoke_params.rbi +42 -0
  120. data/rbi/stigg/models/v1/customers/promotional_revoke_response.rbi +497 -0
  121. data/rbi/stigg/models/v1/customers/usage_retrieve_params.rbi +84 -0
  122. data/rbi/stigg/models/v1/customers/usage_retrieve_response.rbi +353 -0
  123. data/rbi/stigg/models/v1/subscription_create_params.rbi +394 -0
  124. data/rbi/stigg/models/v1/subscription_create_response.rbi +668 -0
  125. data/rbi/stigg/models/v1/subscription_delegate_params.rbi +48 -0
  126. data/rbi/stigg/models/v1/subscription_delegate_response.rbi +535 -0
  127. data/rbi/stigg/models/v1/subscription_list_params.rbi +94 -0
  128. data/rbi/stigg/models/v1/subscription_list_response.rbi +543 -0
  129. data/rbi/stigg/models/v1/subscription_migrate_params.rbi +99 -0
  130. data/rbi/stigg/models/v1/subscription_migrate_response.rbi +535 -0
  131. data/rbi/stigg/models/v1/subscription_preview_params.rbi +1786 -0
  132. data/rbi/stigg/models/v1/subscription_preview_response.rbi +929 -0
  133. data/rbi/stigg/models/v1/subscription_retrieve_params.rbi +32 -0
  134. data/rbi/stigg/models/v1/subscription_retrieve_response.rbi +535 -0
  135. data/rbi/stigg/models/v1/subscription_transfer_params.rbi +50 -0
  136. data/rbi/stigg/models/v1/subscription_transfer_response.rbi +535 -0
  137. data/rbi/stigg/models/v1/subscriptions/future_update_cancel_pending_payment_params.rbi +34 -0
  138. data/rbi/stigg/models/v1/subscriptions/future_update_cancel_pending_payment_response.rbi +79 -0
  139. data/rbi/stigg/models/v1/subscriptions/future_update_cancel_schedule_params.rbi +34 -0
  140. data/rbi/stigg/models/v1/subscriptions/future_update_cancel_schedule_response.rbi +79 -0
  141. data/rbi/stigg/models/v1_create_event_params.rbi +160 -0
  142. data/rbi/stigg/models/v1_create_event_response.rbi +23 -0
  143. data/rbi/stigg/models/v1_create_usage_params.rbi +177 -0
  144. data/rbi/stigg/models/v1_create_usage_response.rbi +150 -0
  145. data/rbi/stigg/models.rbi +9 -0
  146. data/rbi/stigg/request_options.rbi +55 -0
  147. data/rbi/stigg/resources/v1/coupons.rbi +79 -0
  148. data/rbi/stigg/resources/v1/customers/payment_method.rbi +54 -0
  149. data/rbi/stigg/resources/v1/customers/promotional.rbi +53 -0
  150. data/rbi/stigg/resources/v1/customers/usage.rbi +45 -0
  151. data/rbi/stigg/resources/v1/customers.rbi +138 -0
  152. data/rbi/stigg/resources/v1/subscriptions/future_update.rbi +40 -0
  153. data/rbi/stigg/resources/v1/subscriptions.rbi +199 -0
  154. data/rbi/stigg/resources/v1.rbi +49 -0
  155. data/rbi/stigg/version.rbi +5 -0
  156. data/sig/stigg/client.rbs +26 -0
  157. data/sig/stigg/errors.rbs +117 -0
  158. data/sig/stigg/file_part.rbs +21 -0
  159. data/sig/stigg/internal/my_cursor_id_page.rbs +11 -0
  160. data/sig/stigg/internal/transport/base_client.rbs +133 -0
  161. data/sig/stigg/internal/transport/pooled_net_requester.rbs +48 -0
  162. data/sig/stigg/internal/type/array_of.rbs +48 -0
  163. data/sig/stigg/internal/type/base_model.rbs +102 -0
  164. data/sig/stigg/internal/type/base_page.rbs +24 -0
  165. data/sig/stigg/internal/type/boolean.rbs +26 -0
  166. data/sig/stigg/internal/type/converter.rbs +79 -0
  167. data/sig/stigg/internal/type/enum.rbs +32 -0
  168. data/sig/stigg/internal/type/file_input.rbs +25 -0
  169. data/sig/stigg/internal/type/hash_of.rbs +48 -0
  170. data/sig/stigg/internal/type/request_parameters.rbs +17 -0
  171. data/sig/stigg/internal/type/union.rbs +52 -0
  172. data/sig/stigg/internal/type/unknown.rbs +26 -0
  173. data/sig/stigg/internal/util.rbs +185 -0
  174. data/sig/stigg/internal.rbs +9 -0
  175. data/sig/stigg/models/v1/coupon_create_params.rbs +323 -0
  176. data/sig/stigg/models/v1/coupon_create_response.rbs +392 -0
  177. data/sig/stigg/models/v1/coupon_list_params.rbs +40 -0
  178. data/sig/stigg/models/v1/coupon_list_response.rbs +399 -0
  179. data/sig/stigg/models/v1/coupon_retrieve_params.rbs +17 -0
  180. data/sig/stigg/models/v1/coupon_retrieve_response.rbs +394 -0
  181. data/sig/stigg/models/v1/customer_archive_params.rbs +17 -0
  182. data/sig/stigg/models/v1/customer_create_params.rbs +169 -0
  183. data/sig/stigg/models/v1/customer_list_params.rbs +40 -0
  184. data/sig/stigg/models/v1/customer_list_response.rbs +198 -0
  185. data/sig/stigg/models/v1/customer_response.rbs +188 -0
  186. data/sig/stigg/models/v1/customer_retrieve_params.rbs +17 -0
  187. data/sig/stigg/models/v1/customer_unarchive_params.rbs +17 -0
  188. data/sig/stigg/models/v1/customer_update_params.rbs +110 -0
  189. data/sig/stigg/models/v1/customers/payment_method_attach_params.rbs +315 -0
  190. data/sig/stigg/models/v1/customers/payment_method_detach_params.rbs +19 -0
  191. data/sig/stigg/models/v1/customers/promotional_create_params.rbs +234 -0
  192. data/sig/stigg/models/v1/customers/promotional_create_response.rbs +209 -0
  193. data/sig/stigg/models/v1/customers/promotional_revoke_params.rbs +27 -0
  194. data/sig/stigg/models/v1/customers/promotional_revoke_response.rbs +209 -0
  195. data/sig/stigg/models/v1/customers/usage_retrieve_params.rbs +54 -0
  196. data/sig/stigg/models/v1/customers/usage_retrieve_response.rbs +134 -0
  197. data/sig/stigg/models/v1/subscription_create_params.rbs +199 -0
  198. data/sig/stigg/models/v1/subscription_create_response.rbs +283 -0
  199. data/sig/stigg/models/v1/subscription_delegate_params.rbs +26 -0
  200. data/sig/stigg/models/v1/subscription_delegate_response.rbs +224 -0
  201. data/sig/stigg/models/v1/subscription_list_params.rbs +58 -0
  202. data/sig/stigg/models/v1/subscription_list_response.rbs +229 -0
  203. data/sig/stigg/models/v1/subscription_migrate_params.rbs +43 -0
  204. data/sig/stigg/models/v1/subscription_migrate_response.rbs +224 -0
  205. data/sig/stigg/models/v1/subscription_preview_params.rbs +821 -0
  206. data/sig/stigg/models/v1/subscription_preview_response.rbs +466 -0
  207. data/sig/stigg/models/v1/subscription_retrieve_params.rbs +17 -0
  208. data/sig/stigg/models/v1/subscription_retrieve_response.rbs +224 -0
  209. data/sig/stigg/models/v1/subscription_transfer_params.rbs +26 -0
  210. data/sig/stigg/models/v1/subscription_transfer_response.rbs +224 -0
  211. data/sig/stigg/models/v1/subscriptions/future_update_cancel_pending_payment_params.rbs +19 -0
  212. data/sig/stigg/models/v1/subscriptions/future_update_cancel_pending_payment_response.rbs +34 -0
  213. data/sig/stigg/models/v1/subscriptions/future_update_cancel_schedule_params.rbs +19 -0
  214. data/sig/stigg/models/v1/subscriptions/future_update_cancel_schedule_response.rbs +34 -0
  215. data/sig/stigg/models/v1_create_event_params.rbs +80 -0
  216. data/sig/stigg/models/v1_create_event_response.rbs +13 -0
  217. data/sig/stigg/models/v1_create_usage_params.rbs +90 -0
  218. data/sig/stigg/models/v1_create_usage_response.rbs +85 -0
  219. data/sig/stigg/models.rbs +7 -0
  220. data/sig/stigg/request_options.rbs +34 -0
  221. data/sig/stigg/resources/v1/coupons.rbs +32 -0
  222. data/sig/stigg/resources/v1/customers/payment_method.rbs +25 -0
  223. data/sig/stigg/resources/v1/customers/promotional.rbs +23 -0
  224. data/sig/stigg/resources/v1/customers/usage.rbs +21 -0
  225. data/sig/stigg/resources/v1/customers.rbs +58 -0
  226. data/sig/stigg/resources/v1/subscriptions/future_update.rbs +21 -0
  227. data/sig/stigg/resources/v1/subscriptions.rbs +76 -0
  228. data/sig/stigg/resources/v1.rbs +23 -0
  229. data/sig/stigg/version.rbs +3 -0
  230. metadata +301 -0
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stigg
4
+ module Internal
5
+ # @generic Elem
6
+ #
7
+ # @example
8
+ # if my_cursor_id_page.has_next?
9
+ # my_cursor_id_page = my_cursor_id_page.next_page
10
+ # end
11
+ #
12
+ # @example
13
+ # my_cursor_id_page.auto_paging_each do |item|
14
+ # puts(item)
15
+ # end
16
+ class MyCursorIDPage
17
+ include Stigg::Internal::Type::BasePage
18
+
19
+ # @return [Array<generic<Elem>>, nil]
20
+ attr_accessor :data
21
+
22
+ # @return [Boolean]
23
+ def next_page?
24
+ !data.to_a.empty?
25
+ end
26
+
27
+ # @raise [Stigg::HTTP::Error]
28
+ # @return [self]
29
+ def next_page
30
+ unless next_page?
31
+ message = "No more pages available. Please check #next_page? before calling ##{__method__}"
32
+ raise RuntimeError.new(message)
33
+ end
34
+
35
+ req = Stigg::Internal::Util.deep_merge(@req, {query: {starting_after: data&.last&.cursor_id}})
36
+ @client.request(req)
37
+ end
38
+
39
+ # @param blk [Proc]
40
+ #
41
+ # @yieldparam [generic<Elem>]
42
+ def auto_paging_each(&blk)
43
+ unless block_given?
44
+ raise ArgumentError.new("A block must be given to ##{__method__}")
45
+ end
46
+
47
+ page = self
48
+ loop do
49
+ page.data&.each(&blk)
50
+
51
+ break unless page.next_page?
52
+ page = page.next_page
53
+ end
54
+ end
55
+
56
+ # @api private
57
+ #
58
+ # @param client [Stigg::Internal::Transport::BaseClient]
59
+ # @param req [Hash{Symbol=>Object}]
60
+ # @param headers [Hash{String=>String}]
61
+ # @param page_data [Hash{Symbol=>Object}]
62
+ def initialize(client:, req:, headers:, page_data:)
63
+ super
64
+
65
+ case page_data
66
+ in {data: Array => data}
67
+ @data = data.map { Stigg::Internal::Type::Converter.coerce(@model, _1) }
68
+ else
69
+ end
70
+ end
71
+
72
+ # @api private
73
+ #
74
+ # @return [String]
75
+ def inspect
76
+ model = Stigg::Internal::Type::Converter.inspect(@model, depth: 1)
77
+
78
+ "#<#{self.class}[#{model}]:0x#{object_id.to_s(16)}>"
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,570 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stigg
4
+ module Internal
5
+ module Transport
6
+ # @api private
7
+ #
8
+ # @abstract
9
+ class BaseClient
10
+ extend Stigg::Internal::Util::SorbetRuntimeSupport
11
+
12
+ # from whatwg fetch spec
13
+ MAX_REDIRECTS = 20
14
+
15
+ # rubocop:disable Style/MutableConstant
16
+ PLATFORM_HEADERS =
17
+ {
18
+ "x-stainless-arch" => Stigg::Internal::Util.arch,
19
+ "x-stainless-lang" => "ruby",
20
+ "x-stainless-os" => Stigg::Internal::Util.os,
21
+ "x-stainless-package-version" => Stigg::VERSION,
22
+ "x-stainless-runtime" => ::RUBY_ENGINE,
23
+ "x-stainless-runtime-version" => ::RUBY_ENGINE_VERSION
24
+ }
25
+ # rubocop:enable Style/MutableConstant
26
+
27
+ class << self
28
+ # @api private
29
+ #
30
+ # @param req [Hash{Symbol=>Object}]
31
+ #
32
+ # @raise [ArgumentError]
33
+ def validate!(req)
34
+ keys = [:method, :path, :query, :headers, :body, :unwrap, :page, :stream, :model, :options]
35
+ case req
36
+ in Hash
37
+ req.each_key do |k|
38
+ unless keys.include?(k)
39
+ raise ArgumentError.new("Request `req` keys must be one of #{keys}, got #{k.inspect}")
40
+ end
41
+ end
42
+ else
43
+ raise ArgumentError.new("Request `req` must be a Hash or RequestOptions, got #{req.inspect}")
44
+ end
45
+ end
46
+
47
+ # @api private
48
+ #
49
+ # @param status [Integer]
50
+ # @param headers [Hash{String=>String}]
51
+ #
52
+ # @return [Boolean]
53
+ def should_retry?(status, headers:)
54
+ coerced = Stigg::Internal::Util.coerce_boolean(headers["x-should-retry"])
55
+ case [coerced, status]
56
+ in [true | false, _]
57
+ coerced
58
+ in [_, 408 | 409 | 429 | (500..)]
59
+ # retry on:
60
+ # 408: timeouts
61
+ # 409: locks
62
+ # 429: rate limits
63
+ # 500+: unknown errors
64
+ true
65
+ else
66
+ false
67
+ end
68
+ end
69
+
70
+ # @api private
71
+ #
72
+ # @param request [Hash{Symbol=>Object}] .
73
+ #
74
+ # @option request [Symbol] :method
75
+ #
76
+ # @option request [URI::Generic] :url
77
+ #
78
+ # @option request [Hash{String=>String}] :headers
79
+ #
80
+ # @option request [Object] :body
81
+ #
82
+ # @option request [Integer] :max_retries
83
+ #
84
+ # @option request [Float] :timeout
85
+ #
86
+ # @param status [Integer]
87
+ #
88
+ # @param response_headers [Hash{String=>String}]
89
+ #
90
+ # @return [Hash{Symbol=>Object}]
91
+ def follow_redirect(request, status:, response_headers:)
92
+ method, url, headers = request.fetch_values(:method, :url, :headers)
93
+ location =
94
+ Kernel.then do
95
+ URI.join(url, response_headers["location"])
96
+ rescue ArgumentError
97
+ message = "Server responded with status #{status} but no valid location header."
98
+ raise Stigg::Errors::APIConnectionError.new(
99
+ url: url,
100
+ response: response_headers,
101
+ message: message
102
+ )
103
+ end
104
+
105
+ request = {**request, url: location}
106
+
107
+ case [url.scheme, location.scheme]
108
+ in ["https", "http"]
109
+ message = "Tried to redirect to a insecure URL"
110
+ raise Stigg::Errors::APIConnectionError.new(
111
+ url: url,
112
+ response: response_headers,
113
+ message: message
114
+ )
115
+ else
116
+ nil
117
+ end
118
+
119
+ # from whatwg fetch spec
120
+ case [status, method]
121
+ in [301 | 302, :post] | [303, _]
122
+ drop = %w[content-encoding content-language content-length content-location content-type]
123
+ request = {
124
+ **request,
125
+ method: method == :head ? :head : :get,
126
+ headers: headers.except(*drop),
127
+ body: nil
128
+ }
129
+ else
130
+ end
131
+
132
+ # from undici
133
+ if Stigg::Internal::Util.uri_origin(url) != Stigg::Internal::Util.uri_origin(location)
134
+ drop = %w[authorization cookie host proxy-authorization]
135
+ request = {**request, headers: request.fetch(:headers).except(*drop)}
136
+ end
137
+
138
+ request
139
+ end
140
+
141
+ # @api private
142
+ #
143
+ # @param status [Integer, Stigg::Errors::APIConnectionError]
144
+ # @param stream [Enumerable<String>, nil]
145
+ def reap_connection!(status, stream:)
146
+ case status
147
+ in (..199) | (300..499)
148
+ stream&.each { next }
149
+ in Stigg::Errors::APIConnectionError | (500..)
150
+ Stigg::Internal::Util.close_fused!(stream)
151
+ else
152
+ end
153
+ end
154
+ end
155
+
156
+ # @return [URI::Generic]
157
+ attr_reader :base_url
158
+
159
+ # @return [Float]
160
+ attr_reader :timeout
161
+
162
+ # @return [Integer]
163
+ attr_reader :max_retries
164
+
165
+ # @return [Float]
166
+ attr_reader :initial_retry_delay
167
+
168
+ # @return [Float]
169
+ attr_reader :max_retry_delay
170
+
171
+ # @return [Hash{String=>String}]
172
+ attr_reader :headers
173
+
174
+ # @return [String, nil]
175
+ attr_reader :idempotency_header
176
+
177
+ # @api private
178
+ # @return [Stigg::Internal::Transport::PooledNetRequester]
179
+ attr_reader :requester
180
+
181
+ # @api private
182
+ #
183
+ # @param base_url [String]
184
+ # @param timeout [Float]
185
+ # @param max_retries [Integer]
186
+ # @param initial_retry_delay [Float]
187
+ # @param max_retry_delay [Float]
188
+ # @param headers [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}]
189
+ # @param idempotency_header [String, nil]
190
+ def initialize(
191
+ base_url:,
192
+ timeout: 0.0,
193
+ max_retries: 0,
194
+ initial_retry_delay: 0.0,
195
+ max_retry_delay: 0.0,
196
+ headers: {},
197
+ idempotency_header: nil
198
+ )
199
+ @requester = Stigg::Internal::Transport::PooledNetRequester.new
200
+ @headers = Stigg::Internal::Util.normalized_headers(
201
+ self.class::PLATFORM_HEADERS,
202
+ {
203
+ "accept" => "application/json",
204
+ "content-type" => "application/json",
205
+ "user-agent" => user_agent
206
+ },
207
+ headers
208
+ )
209
+ @base_url_components = Stigg::Internal::Util.parse_uri(base_url)
210
+ @base_url = Stigg::Internal::Util.unparse_uri(@base_url_components)
211
+ @idempotency_header = idempotency_header&.to_s&.downcase
212
+ @timeout = timeout
213
+ @max_retries = max_retries
214
+ @initial_retry_delay = initial_retry_delay
215
+ @max_retry_delay = max_retry_delay
216
+ end
217
+
218
+ # @api private
219
+ #
220
+ # @return [Hash{String=>String}]
221
+ private def auth_headers = {}
222
+
223
+ # @api private
224
+ #
225
+ # @return [String]
226
+ private def user_agent = "#{self.class.name}/Ruby #{Stigg::VERSION}"
227
+
228
+ # @api private
229
+ #
230
+ # @return [String]
231
+ private def generate_idempotency_key = "stainless-ruby-retry-#{SecureRandom.uuid}"
232
+
233
+ # @api private
234
+ #
235
+ # @param req [Hash{Symbol=>Object}] .
236
+ #
237
+ # @option req [Symbol] :method
238
+ #
239
+ # @option req [String, Array<String>] :path
240
+ #
241
+ # @option req [Hash{String=>Array<String>, String, nil}, nil] :query
242
+ #
243
+ # @option req [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil] :headers
244
+ #
245
+ # @option req [Object, nil] :body
246
+ #
247
+ # @option req [Symbol, Integer, Array<Symbol, Integer>, Proc, nil] :unwrap
248
+ #
249
+ # @option req [Class<Stigg::Internal::Type::BasePage>, nil] :page
250
+ #
251
+ # @option req [Class<Stigg::Internal::Type::BaseStream>, nil] :stream
252
+ #
253
+ # @option req [Stigg::Internal::Type::Converter, Class, nil] :model
254
+ #
255
+ # @param opts [Hash{Symbol=>Object}] .
256
+ #
257
+ # @option opts [String, nil] :idempotency_key
258
+ #
259
+ # @option opts [Hash{String=>Array<String>, String, nil}, nil] :extra_query
260
+ #
261
+ # @option opts [Hash{String=>String, nil}, nil] :extra_headers
262
+ #
263
+ # @option opts [Object, nil] :extra_body
264
+ #
265
+ # @option opts [Integer, nil] :max_retries
266
+ #
267
+ # @option opts [Float, nil] :timeout
268
+ #
269
+ # @return [Hash{Symbol=>Object}]
270
+ private def build_request(req, opts)
271
+ method, uninterpolated_path = req.fetch_values(:method, :path)
272
+
273
+ path = Stigg::Internal::Util.interpolate_path(uninterpolated_path)
274
+
275
+ query = Stigg::Internal::Util.deep_merge(req[:query].to_h, opts[:extra_query].to_h)
276
+
277
+ headers = Stigg::Internal::Util.normalized_headers(
278
+ @headers,
279
+ auth_headers,
280
+ req[:headers].to_h,
281
+ opts[:extra_headers].to_h
282
+ )
283
+
284
+ if @idempotency_header &&
285
+ !headers.key?(@idempotency_header) &&
286
+ (!Net::HTTP::IDEMPOTENT_METHODS_.include?(method.to_s.upcase) || opts.key?(:idempotency_key))
287
+ headers[@idempotency_header] = opts.fetch(:idempotency_key) { generate_idempotency_key }
288
+ end
289
+
290
+ unless headers.key?("x-stainless-retry-count")
291
+ headers["x-stainless-retry-count"] = "0"
292
+ end
293
+
294
+ timeout = opts.fetch(:timeout, @timeout).to_f.clamp(0..)
295
+ unless headers.key?("x-stainless-timeout") || timeout.zero?
296
+ headers["x-stainless-timeout"] = timeout.to_s
297
+ end
298
+
299
+ headers.reject! { |_, v| v.to_s.empty? }
300
+
301
+ body =
302
+ case method
303
+ in :get | :head | :options | :trace
304
+ nil
305
+ else
306
+ Stigg::Internal::Util.deep_merge(*[req[:body], opts[:extra_body]].compact)
307
+ end
308
+
309
+ url = Stigg::Internal::Util.join_parsed_uri(@base_url_components, {**req, path: path, query: query})
310
+ headers, encoded = Stigg::Internal::Util.encode_content(headers, body)
311
+ {
312
+ method: method,
313
+ url: url,
314
+ headers: headers,
315
+ body: encoded,
316
+ max_retries: opts.fetch(:max_retries, @max_retries),
317
+ timeout: timeout
318
+ }
319
+ end
320
+
321
+ # @api private
322
+ #
323
+ # @param headers [Hash{String=>String}]
324
+ # @param retry_count [Integer]
325
+ #
326
+ # @return [Float]
327
+ private def retry_delay(headers, retry_count:)
328
+ # Non-standard extension
329
+ span = Float(headers["retry-after-ms"], exception: false)&.then { _1 / 1000 }
330
+ return span if span
331
+
332
+ retry_header = headers["retry-after"]
333
+ return span if (span = Float(retry_header, exception: false))
334
+
335
+ span = retry_header&.then do
336
+ Time.httpdate(_1) - Time.now
337
+ rescue ArgumentError
338
+ nil
339
+ end
340
+ return span if span
341
+
342
+ scale = retry_count**2
343
+ jitter = 1 - (0.25 * rand)
344
+ (@initial_retry_delay * scale * jitter).clamp(0, @max_retry_delay)
345
+ end
346
+
347
+ # @api private
348
+ #
349
+ # @param request [Hash{Symbol=>Object}] .
350
+ #
351
+ # @option request [Symbol] :method
352
+ #
353
+ # @option request [URI::Generic] :url
354
+ #
355
+ # @option request [Hash{String=>String}] :headers
356
+ #
357
+ # @option request [Object] :body
358
+ #
359
+ # @option request [Integer] :max_retries
360
+ #
361
+ # @option request [Float] :timeout
362
+ #
363
+ # @param redirect_count [Integer]
364
+ #
365
+ # @param retry_count [Integer]
366
+ #
367
+ # @param send_retry_header [Boolean]
368
+ #
369
+ # @raise [Stigg::Errors::APIError]
370
+ # @return [Array(Integer, Net::HTTPResponse, Enumerable<String>)]
371
+ def send_request(request, redirect_count:, retry_count:, send_retry_header:)
372
+ url, headers, max_retries, timeout = request.fetch_values(:url, :headers, :max_retries, :timeout)
373
+ input = {**request.except(:timeout), deadline: Stigg::Internal::Util.monotonic_secs + timeout}
374
+
375
+ if send_retry_header
376
+ headers["x-stainless-retry-count"] = retry_count.to_s
377
+ end
378
+
379
+ begin
380
+ status, response, stream = @requester.execute(input)
381
+ rescue Stigg::Errors::APIConnectionError => e
382
+ status = e
383
+ end
384
+ headers = Stigg::Internal::Util.normalized_headers(response&.each_header&.to_h)
385
+
386
+ case status
387
+ in ..299
388
+ [status, response, stream]
389
+ in 300..399 if redirect_count >= self.class::MAX_REDIRECTS
390
+ self.class.reap_connection!(status, stream: stream)
391
+
392
+ message = "Failed to complete the request within #{self.class::MAX_REDIRECTS} redirects."
393
+ raise Stigg::Errors::APIConnectionError.new(url: url, response: response, message: message)
394
+ in 300..399
395
+ self.class.reap_connection!(status, stream: stream)
396
+
397
+ request = self.class.follow_redirect(request, status: status, response_headers: headers)
398
+ send_request(
399
+ request,
400
+ redirect_count: redirect_count + 1,
401
+ retry_count: retry_count,
402
+ send_retry_header: send_retry_header
403
+ )
404
+ in Stigg::Errors::APIConnectionError if retry_count >= max_retries
405
+ raise status
406
+ in (400..) if retry_count >= max_retries || !self.class.should_retry?(status, headers: headers)
407
+ decoded = Kernel.then do
408
+ Stigg::Internal::Util.decode_content(headers, stream: stream, suppress_error: true)
409
+ ensure
410
+ self.class.reap_connection!(status, stream: stream)
411
+ end
412
+
413
+ raise Stigg::Errors::APIStatusError.for(
414
+ url: url,
415
+ status: status,
416
+ headers: headers,
417
+ body: decoded,
418
+ request: nil,
419
+ response: response
420
+ )
421
+ in (400..) | Stigg::Errors::APIConnectionError
422
+ self.class.reap_connection!(status, stream: stream)
423
+
424
+ delay = retry_delay(response || {}, retry_count: retry_count)
425
+ sleep(delay)
426
+
427
+ send_request(
428
+ request,
429
+ redirect_count: redirect_count,
430
+ retry_count: retry_count + 1,
431
+ send_retry_header: send_retry_header
432
+ )
433
+ end
434
+ end
435
+
436
+ # Execute the request specified by `req`. This is the method that all resource
437
+ # methods call into.
438
+ #
439
+ # @overload request(method, path, query: {}, headers: {}, body: nil, unwrap: nil, page: nil, stream: nil, model: Stigg::Internal::Type::Unknown, options: {})
440
+ #
441
+ # @param method [Symbol]
442
+ #
443
+ # @param path [String, Array<String>]
444
+ #
445
+ # @param query [Hash{String=>Array<String>, String, nil}, nil]
446
+ #
447
+ # @param headers [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil]
448
+ #
449
+ # @param body [Object, nil]
450
+ #
451
+ # @param unwrap [Symbol, Integer, Array<Symbol, Integer>, Proc, nil]
452
+ #
453
+ # @param page [Class<Stigg::Internal::Type::BasePage>, nil]
454
+ #
455
+ # @param stream [Class<Stigg::Internal::Type::BaseStream>, nil]
456
+ #
457
+ # @param model [Stigg::Internal::Type::Converter, Class, nil]
458
+ #
459
+ # @param options [Stigg::RequestOptions, Hash{Symbol=>Object}, nil] .
460
+ #
461
+ # @option options [String, nil] :idempotency_key
462
+ #
463
+ # @option options [Hash{String=>Array<String>, String, nil}, nil] :extra_query
464
+ #
465
+ # @option options [Hash{String=>String, nil}, nil] :extra_headers
466
+ #
467
+ # @option options [Object, nil] :extra_body
468
+ #
469
+ # @option options [Integer, nil] :max_retries
470
+ #
471
+ # @option options [Float, nil] :timeout
472
+ #
473
+ # @raise [Stigg::Errors::APIError]
474
+ # @return [Object]
475
+ def request(req)
476
+ self.class.validate!(req)
477
+ model = req.fetch(:model) { Stigg::Internal::Type::Unknown }
478
+ opts = req[:options].to_h
479
+ unwrap = req[:unwrap]
480
+ Stigg::RequestOptions.validate!(opts)
481
+ request = build_request(req.except(:options), opts)
482
+ url = request.fetch(:url)
483
+
484
+ # Don't send the current retry count in the headers if the caller modified the header defaults.
485
+ send_retry_header = request.fetch(:headers)["x-stainless-retry-count"] == "0"
486
+ status, response, stream = send_request(
487
+ request,
488
+ redirect_count: 0,
489
+ retry_count: 0,
490
+ send_retry_header: send_retry_header
491
+ )
492
+
493
+ headers = Stigg::Internal::Util.normalized_headers(response.each_header.to_h)
494
+ decoded = Stigg::Internal::Util.decode_content(headers, stream: stream)
495
+ case req
496
+ in {stream: Class => st}
497
+ st.new(
498
+ model: model,
499
+ url: url,
500
+ status: status,
501
+ headers: headers,
502
+ response: response,
503
+ unwrap: unwrap,
504
+ stream: decoded
505
+ )
506
+ in {page: Class => page}
507
+ page.new(client: self, req: req, headers: headers, page_data: decoded)
508
+ else
509
+ unwrapped = Stigg::Internal::Util.dig(decoded, unwrap)
510
+ Stigg::Internal::Type::Converter.coerce(model, unwrapped)
511
+ end
512
+ end
513
+
514
+ # @api private
515
+ #
516
+ # @return [String]
517
+ def inspect
518
+ # rubocop:disable Layout/LineLength
519
+ "#<#{self.class.name}:0x#{object_id.to_s(16)} base_url=#{@base_url} max_retries=#{@max_retries} timeout=#{@timeout}>"
520
+ # rubocop:enable Layout/LineLength
521
+ end
522
+
523
+ define_sorbet_constant!(:RequestComponents) do
524
+ T.type_alias do
525
+ {
526
+ method: Symbol,
527
+ path: T.any(String, T::Array[String]),
528
+ query: T.nilable(T::Hash[String, T.nilable(T.any(T::Array[String], String))]),
529
+ headers: T.nilable(
530
+ T::Hash[String,
531
+ T.nilable(
532
+ T.any(
533
+ String,
534
+ Integer,
535
+ T::Array[T.nilable(T.any(String, Integer))]
536
+ )
537
+ )]
538
+ ),
539
+ body: T.nilable(T.anything),
540
+ unwrap: T.nilable(
541
+ T.any(
542
+ Symbol,
543
+ Integer,
544
+ T::Array[T.any(Symbol, Integer)],
545
+ T.proc.params(arg0: T.anything).returns(T.anything)
546
+ )
547
+ ),
548
+ page: T.nilable(T::Class[Stigg::Internal::Type::BasePage[Stigg::Internal::Type::BaseModel]]),
549
+ stream: T.nilable(T::Class[T.anything]),
550
+ model: T.nilable(Stigg::Internal::Type::Converter::Input),
551
+ options: T.nilable(Stigg::RequestOptions::OrHash)
552
+ }
553
+ end
554
+ end
555
+ define_sorbet_constant!(:RequestInput) do
556
+ T.type_alias do
557
+ {
558
+ method: Symbol,
559
+ url: URI::Generic,
560
+ headers: T::Hash[String, String],
561
+ body: T.anything,
562
+ max_retries: Integer,
563
+ timeout: Float
564
+ }
565
+ end
566
+ end
567
+ end
568
+ end
569
+ end
570
+ end