amritk-galaxy-staging 0.2.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 (148) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +13 -0
  3. data/LICENSE +201 -0
  4. data/README.md +169 -0
  5. data/SECURITY.md +19 -0
  6. data/SKILL.md +83 -0
  7. data/api.md +186 -0
  8. data/lib/amritk-galaxy-staging/client.rb +238 -0
  9. data/lib/amritk-galaxy-staging/errors.rb +227 -0
  10. data/lib/amritk-galaxy-staging/file_part.rb +58 -0
  11. data/lib/amritk-galaxy-staging/internal/transport/base_client.rb +588 -0
  12. data/lib/amritk-galaxy-staging/internal/transport/pooled_net_requester.rb +224 -0
  13. data/lib/amritk-galaxy-staging/internal/type/array_of.rb +164 -0
  14. data/lib/amritk-galaxy-staging/internal/type/base_model.rb +534 -0
  15. data/lib/amritk-galaxy-staging/internal/type/base_page.rb +55 -0
  16. data/lib/amritk-galaxy-staging/internal/type/boolean.rb +77 -0
  17. data/lib/amritk-galaxy-staging/internal/type/converter.rb +333 -0
  18. data/lib/amritk-galaxy-staging/internal/type/enum.rb +132 -0
  19. data/lib/amritk-galaxy-staging/internal/type/file_input.rb +111 -0
  20. data/lib/amritk-galaxy-staging/internal/type/hash_of.rb +186 -0
  21. data/lib/amritk-galaxy-staging/internal/type/request_parameters.rb +42 -0
  22. data/lib/amritk-galaxy-staging/internal/type/union.rb +238 -0
  23. data/lib/amritk-galaxy-staging/internal/type/unknown.rb +81 -0
  24. data/lib/amritk-galaxy-staging/internal/util.rb +965 -0
  25. data/lib/amritk-galaxy-staging/internal.rb +18 -0
  26. data/lib/amritk-galaxy-staging/models/authentication_create_token_params.rb +26 -0
  27. data/lib/amritk-galaxy-staging/models/authentication_create_user_params.rb +32 -0
  28. data/lib/amritk-galaxy-staging/models/authentication_list_me_params.rb +14 -0
  29. data/lib/amritk-galaxy-staging/models/celestial_body.rb +21 -0
  30. data/lib/amritk-galaxy-staging/models/celestial_body_create_params.rb +20 -0
  31. data/lib/amritk-galaxy-staging/models/new_planet_webhook_event.rb +9 -0
  32. data/lib/amritk-galaxy-staging/models/paginated_resource.rb +46 -0
  33. data/lib/amritk-galaxy-staging/models/planet.rb +221 -0
  34. data/lib/amritk-galaxy-staging/models/planet_create_params.rb +14 -0
  35. data/lib/amritk-galaxy-staging/models/planet_delete_params.rb +20 -0
  36. data/lib/amritk-galaxy-staging/models/planet_list_params.rb +30 -0
  37. data/lib/amritk-galaxy-staging/models/planet_list_response.rb +16 -0
  38. data/lib/amritk-galaxy-staging/models/planet_retrieve_params.rb +20 -0
  39. data/lib/amritk-galaxy-staging/models/planet_update_params.rb +20 -0
  40. data/lib/amritk-galaxy-staging/models/planet_upload_image_params.rb +29 -0
  41. data/lib/amritk-galaxy-staging/models/planet_upload_image_response.rb +48 -0
  42. data/lib/amritk-galaxy-staging/models/satellite.rb +96 -0
  43. data/lib/amritk-galaxy-staging/models/token.rb +18 -0
  44. data/lib/amritk-galaxy-staging/models/unwrap_webhook_event.rb +14 -0
  45. data/lib/amritk-galaxy-staging/models/user.rb +26 -0
  46. data/lib/amritk-galaxy-staging/models/webhook_unwrap_params.rb +14 -0
  47. data/lib/amritk-galaxy-staging/models.rb +87 -0
  48. data/lib/amritk-galaxy-staging/request_options.rb +79 -0
  49. data/lib/amritk-galaxy-staging/resources/authentication.rb +80 -0
  50. data/lib/amritk-galaxy-staging/resources/celestial_bodies.rb +36 -0
  51. data/lib/amritk-galaxy-staging/resources/planets.rb +215 -0
  52. data/lib/amritk-galaxy-staging/resources/webhooks.rb +37 -0
  53. data/lib/amritk-galaxy-staging/version.rb +5 -0
  54. data/lib/amritk-galaxy-staging.rb +79 -0
  55. data/rbi/amritk-galaxy-staging/client.rbi +130 -0
  56. data/rbi/amritk-galaxy-staging/errors.rbi +205 -0
  57. data/rbi/amritk-galaxy-staging/file_part.rbi +37 -0
  58. data/rbi/amritk-galaxy-staging/internal/transport/base_client.rbi +312 -0
  59. data/rbi/amritk-galaxy-staging/internal/transport/pooled_net_requester.rbi +84 -0
  60. data/rbi/amritk-galaxy-staging/internal/type/array_of.rbi +108 -0
  61. data/rbi/amritk-galaxy-staging/internal/type/base_model.rbi +319 -0
  62. data/rbi/amritk-galaxy-staging/internal/type/base_page.rbi +43 -0
  63. data/rbi/amritk-galaxy-staging/internal/type/boolean.rbi +59 -0
  64. data/rbi/amritk-galaxy-staging/internal/type/converter.rbi +225 -0
  65. data/rbi/amritk-galaxy-staging/internal/type/enum.rbi +82 -0
  66. data/rbi/amritk-galaxy-staging/internal/type/file_input.rbi +60 -0
  67. data/rbi/amritk-galaxy-staging/internal/type/hash_of.rbi +108 -0
  68. data/rbi/amritk-galaxy-staging/internal/type/request_parameters.rbi +33 -0
  69. data/rbi/amritk-galaxy-staging/internal/type/union.rbi +134 -0
  70. data/rbi/amritk-galaxy-staging/internal/type/unknown.rbi +59 -0
  71. data/rbi/amritk-galaxy-staging/internal/util.rbi +515 -0
  72. data/rbi/amritk-galaxy-staging/internal.rbi +18 -0
  73. data/rbi/amritk-galaxy-staging/models/authentication_create_token_params.rbi +46 -0
  74. data/rbi/amritk-galaxy-staging/models/authentication_create_user_params.rbi +54 -0
  75. data/rbi/amritk-galaxy-staging/models/authentication_list_me_params.rbi +34 -0
  76. data/rbi/amritk-galaxy-staging/models/celestial_body.rbi +21 -0
  77. data/rbi/amritk-galaxy-staging/models/celestial_body_create_params.rbi +53 -0
  78. data/rbi/amritk-galaxy-staging/models/new_planet_webhook_event.rbi +23 -0
  79. data/rbi/amritk-galaxy-staging/models/paginated_resource.rbi +93 -0
  80. data/rbi/amritk-galaxy-staging/models/planet.rbi +373 -0
  81. data/rbi/amritk-galaxy-staging/models/planet_create_params.rbi +34 -0
  82. data/rbi/amritk-galaxy-staging/models/planet_delete_params.rbi +41 -0
  83. data/rbi/amritk-galaxy-staging/models/planet_list_params.rbi +60 -0
  84. data/rbi/amritk-galaxy-staging/models/planet_list_response.rbi +33 -0
  85. data/rbi/amritk-galaxy-staging/models/planet_retrieve_params.rbi +41 -0
  86. data/rbi/amritk-galaxy-staging/models/planet_update_params.rbi +41 -0
  87. data/rbi/amritk-galaxy-staging/models/planet_upload_image_params.rbi +55 -0
  88. data/rbi/amritk-galaxy-staging/models/planet_upload_image_response.rbi +85 -0
  89. data/rbi/amritk-galaxy-staging/models/satellite.rbi +160 -0
  90. data/rbi/amritk-galaxy-staging/models/token.rbi +30 -0
  91. data/rbi/amritk-galaxy-staging/models/unwrap_webhook_event.rbi +20 -0
  92. data/rbi/amritk-galaxy-staging/models/user.rbi +36 -0
  93. data/rbi/amritk-galaxy-staging/models/webhook_unwrap_params.rbi +34 -0
  94. data/rbi/amritk-galaxy-staging/models.rbi +45 -0
  95. data/rbi/amritk-galaxy-staging/request_options.rbi +64 -0
  96. data/rbi/amritk-galaxy-staging/resources/authentication.rbi +49 -0
  97. data/rbi/amritk-galaxy-staging/resources/celestial_bodies.rbi +29 -0
  98. data/rbi/amritk-galaxy-staging/resources/planets.rbi +180 -0
  99. data/rbi/amritk-galaxy-staging/resources/webhooks.rbi +31 -0
  100. data/rbi/amritk-galaxy-staging/version.rbi +5 -0
  101. data/sig/amritk-galaxy-staging/client.rbs +62 -0
  102. data/sig/amritk-galaxy-staging/errors.rbs +117 -0
  103. data/sig/amritk-galaxy-staging/file_part.rbs +21 -0
  104. data/sig/amritk-galaxy-staging/internal/transport/base_client.rbs +133 -0
  105. data/sig/amritk-galaxy-staging/internal/transport/pooled_net_requester.rbs +48 -0
  106. data/sig/amritk-galaxy-staging/internal/type/array_of.rbs +48 -0
  107. data/sig/amritk-galaxy-staging/internal/type/base_model.rbs +102 -0
  108. data/sig/amritk-galaxy-staging/internal/type/base_page.rbs +24 -0
  109. data/sig/amritk-galaxy-staging/internal/type/boolean.rbs +26 -0
  110. data/sig/amritk-galaxy-staging/internal/type/converter.rbs +79 -0
  111. data/sig/amritk-galaxy-staging/internal/type/enum.rbs +32 -0
  112. data/sig/amritk-galaxy-staging/internal/type/file_input.rbs +25 -0
  113. data/sig/amritk-galaxy-staging/internal/type/hash_of.rbs +48 -0
  114. data/sig/amritk-galaxy-staging/internal/type/request_parameters.rbs +17 -0
  115. data/sig/amritk-galaxy-staging/internal/type/union.rbs +52 -0
  116. data/sig/amritk-galaxy-staging/internal/type/unknown.rbs +26 -0
  117. data/sig/amritk-galaxy-staging/internal/util.rbs +199 -0
  118. data/sig/amritk-galaxy-staging/internal.rbs +9 -0
  119. data/sig/amritk-galaxy-staging/models/authentication_create_token_params.rbs +28 -0
  120. data/sig/amritk-galaxy-staging/models/authentication_create_user_params.rbs +34 -0
  121. data/sig/amritk-galaxy-staging/models/authentication_list_me_params.rbs +17 -0
  122. data/sig/amritk-galaxy-staging/models/celestial_body.rbs +12 -0
  123. data/sig/amritk-galaxy-staging/models/celestial_body_create_params.rbs +24 -0
  124. data/sig/amritk-galaxy-staging/models/new_planet_webhook_event.rbs +11 -0
  125. data/sig/amritk-galaxy-staging/models/paginated_resource.rbs +53 -0
  126. data/sig/amritk-galaxy-staging/models/planet.rbs +210 -0
  127. data/sig/amritk-galaxy-staging/models/planet_create_params.rbs +17 -0
  128. data/sig/amritk-galaxy-staging/models/planet_delete_params.rbs +24 -0
  129. data/sig/amritk-galaxy-staging/models/planet_list_params.rbs +32 -0
  130. data/sig/amritk-galaxy-staging/models/planet_list_response.rbs +17 -0
  131. data/sig/amritk-galaxy-staging/models/planet_retrieve_params.rbs +24 -0
  132. data/sig/amritk-galaxy-staging/models/planet_update_params.rbs +26 -0
  133. data/sig/amritk-galaxy-staging/models/planet_upload_image_params.rbs +32 -0
  134. data/sig/amritk-galaxy-staging/models/planet_upload_image_response.rbs +50 -0
  135. data/sig/amritk-galaxy-staging/models/satellite.rbs +95 -0
  136. data/sig/amritk-galaxy-staging/models/token.rbs +15 -0
  137. data/sig/amritk-galaxy-staging/models/unwrap_webhook_event.rbs +11 -0
  138. data/sig/amritk-galaxy-staging/models/user.rbs +19 -0
  139. data/sig/amritk-galaxy-staging/models/webhook_unwrap_params.rbs +17 -0
  140. data/sig/amritk-galaxy-staging/models.rbs +39 -0
  141. data/sig/amritk-galaxy-staging/request_options.rbs +34 -0
  142. data/sig/amritk-galaxy-staging/resources/authentication.rbs +24 -0
  143. data/sig/amritk-galaxy-staging/resources/celestial_bodies.rbs +12 -0
  144. data/sig/amritk-galaxy-staging/resources/planets.rbs +68 -0
  145. data/sig/amritk-galaxy-staging/resources/webhooks.rbs +13 -0
  146. data/sig/amritk-galaxy-staging/version.rbs +3 -0
  147. data/sig/manifest.yaml +6 -0
  148. metadata +248 -0
@@ -0,0 +1,588 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DemoApiScalarGalaxy
4
+ module Internal
5
+ module Transport
6
+ # @api private
7
+ #
8
+ # @abstract
9
+ class BaseClient
10
+ extend DemoApiScalarGalaxy::Internal::Util::SorbetRuntimeSupport
11
+
12
+ # from whatwg fetch spec
13
+ MAX_REDIRECTS = 20
14
+
15
+ # rubocop:disable Style/MutableConstant
16
+ PLATFORM_HEADERS = {
17
+ "x-scalar-arch" => DemoApiScalarGalaxy::Internal::Util.arch,
18
+ "x-scalar-lang" => "ruby",
19
+ "x-scalar-os" => DemoApiScalarGalaxy::Internal::Util.os,
20
+ "x-scalar-package-version" => DemoApiScalarGalaxy::VERSION,
21
+ "x-scalar-runtime" => ::RUBY_ENGINE,
22
+ "x-scalar-runtime-version" => ::RUBY_ENGINE_VERSION
23
+ }
24
+ # rubocop:enable Style/MutableConstant
25
+
26
+ class << self
27
+ # @api private
28
+ #
29
+ # @param req [Hash{Symbol=>Object}]
30
+ #
31
+ # @raise [ArgumentError]
32
+ def validate!(req)
33
+ keys = %i[method path query headers body unwrap page stream model options]
34
+ case req
35
+ in Hash
36
+ req.each_key do |k|
37
+ unless keys.include?(k)
38
+ raise ArgumentError.new("Request `req` keys must be one of #{keys}, got #{k.inspect}")
39
+ end
40
+ end
41
+ else
42
+ raise ArgumentError.new("Request `req` must be a Hash or RequestOptions, got #{req.inspect}")
43
+ end
44
+ end
45
+
46
+ # @api private
47
+ #
48
+ # @param status [Integer]
49
+ # @param headers [Hash{String=>String}]
50
+ #
51
+ # @return [Boolean]
52
+ def should_retry?(status, headers:)
53
+ coerced = DemoApiScalarGalaxy::Internal::Util.coerce_boolean(headers["x-should-retry"])
54
+ case [coerced, status]
55
+ in [true | false, _]
56
+ coerced
57
+ in [_, 408 | 409 | 429 | 500..]
58
+ # retry on:
59
+ # 408: timeouts
60
+ # 409: locks
61
+ # 429: rate limits
62
+ # 500+: unknown errors
63
+ true
64
+ else
65
+ false
66
+ end
67
+ end
68
+
69
+ # @api private
70
+ #
71
+ # @param request [Hash{Symbol=>Object}] .
72
+ #
73
+ # @option request [Symbol] :method
74
+ #
75
+ # @option request [URI::Generic] :url
76
+ #
77
+ # @option request [Hash{String=>String}] :headers
78
+ #
79
+ # @option request [Object] :body
80
+ #
81
+ # @option request [Integer] :max_retries
82
+ #
83
+ # @option request [Float] :timeout
84
+ #
85
+ # @param status [Integer]
86
+ #
87
+ # @param response_headers [Hash{String=>String}]
88
+ #
89
+ # @return [Hash{Symbol=>Object}]
90
+ def follow_redirect(request, status:, response_headers:)
91
+ method, url, headers = request.fetch_values(:method, :url, :headers)
92
+ location =
93
+ Kernel.then do
94
+ URI.join(url, response_headers["location"])
95
+ rescue ArgumentError
96
+ message = "Server responded with status #{status} but no valid location header."
97
+ raise DemoApiScalarGalaxy::Errors::APIConnectionError.new(
98
+ url: url,
99
+ response: response_headers,
100
+ message: message
101
+ )
102
+ end
103
+
104
+ request = {**request, url: location}
105
+
106
+ case [url.scheme, location.scheme]
107
+ in ["https", "http"]
108
+ message = "Tried to redirect to a insecure URL"
109
+ raise DemoApiScalarGalaxy::Errors::APIConnectionError.new(
110
+ url: url,
111
+ response: response_headers,
112
+ message: message
113
+ )
114
+ else
115
+ nil
116
+ end
117
+
118
+ # from whatwg fetch spec
119
+ case [status, method]
120
+ in [301 | 302, :post] | [303, _]
121
+ drop = %w[content-encoding content-language content-length content-location content-type]
122
+ request = {
123
+ **request,
124
+ method: method == :head ? :head : :get,
125
+ headers: headers.except(*drop),
126
+ body: nil
127
+ }
128
+ else
129
+ end
130
+
131
+ # from undici
132
+ if DemoApiScalarGalaxy::Internal::Util.uri_origin(url) !=
133
+ DemoApiScalarGalaxy::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, DemoApiScalarGalaxy::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 DemoApiScalarGalaxy::Errors::APIConnectionError | 500.. then
150
+ DemoApiScalarGalaxy::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 [DemoApiScalarGalaxy::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 = DemoApiScalarGalaxy::Internal::Transport::PooledNetRequester.new
200
+ @headers =
201
+ DemoApiScalarGalaxy::Internal::Util.normalized_headers(
202
+ self.class::PLATFORM_HEADERS,
203
+ {
204
+ "accept" => "application/json",
205
+ "content-type" => "application/json",
206
+ "user-agent" => user_agent
207
+ },
208
+ headers
209
+ )
210
+ @base_url_components = DemoApiScalarGalaxy::Internal::Util.parse_uri(base_url)
211
+ @base_url = DemoApiScalarGalaxy::Internal::Util.unparse_uri(@base_url_components)
212
+ @idempotency_header = idempotency_header&.to_s&.downcase
213
+ @timeout = timeout
214
+ @max_retries = max_retries
215
+ @initial_retry_delay = initial_retry_delay
216
+ @max_retry_delay = max_retry_delay
217
+ end
218
+
219
+ # @api private
220
+ #
221
+ # @return [Hash{String=>String}]
222
+ private def auth_headers = {}
223
+
224
+ # @api private
225
+ #
226
+ # @return [String]
227
+ private def user_agent = "#{self.class.name}/Ruby #{DemoApiScalarGalaxy::VERSION}"
228
+
229
+ # @api private
230
+ #
231
+ # @return [String]
232
+ private def generate_idempotency_key = "scalar-ruby-retry-#{SecureRandom.uuid}"
233
+
234
+ # @api private
235
+ #
236
+ # @param req [Hash{Symbol=>Object}] .
237
+ #
238
+ # @option req [Symbol] :method
239
+ #
240
+ # @option req [String, Array<String>] :path
241
+ #
242
+ # @option req [Hash{String=>Array<String>, String, nil}, nil] :query
243
+ #
244
+ # @option req [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil] :headers
245
+ #
246
+ # @option req [Object, nil] :body
247
+ #
248
+ # @option req [Symbol, Integer, Array<Symbol, Integer>, Proc, nil] :unwrap
249
+ #
250
+ # @option req [Class<DemoApiScalarGalaxy::Internal::Type::BasePage>, nil] :page
251
+ #
252
+ # @option req [Class<DemoApiScalarGalaxy::Internal::Type::BaseStream>, nil] :stream
253
+ #
254
+ # @option req [DemoApiScalarGalaxy::Internal::Type::Converter, Class, nil] :model
255
+ #
256
+ # @param opts [Hash{Symbol=>Object}] .
257
+ #
258
+ # @option opts [String, nil] :idempotency_key
259
+ #
260
+ # @option opts [Hash{String=>Array<String>, String, nil}, nil] :extra_query
261
+ #
262
+ # @option opts [Hash{String=>String, nil}, nil] :extra_headers
263
+ #
264
+ # @option opts [Object, nil] :extra_body
265
+ #
266
+ # @option opts [Integer, nil] :max_retries
267
+ #
268
+ # @option opts [Float, nil] :timeout
269
+ #
270
+ # @return [Hash{Symbol=>Object}]
271
+ private def build_request(req, opts)
272
+ method, uninterpolated_path = req.fetch_values(:method, :path)
273
+
274
+ path = DemoApiScalarGalaxy::Internal::Util.interpolate_path(uninterpolated_path)
275
+
276
+ query = DemoApiScalarGalaxy::Internal::Util.deep_merge(req[:query].to_h, opts[:extra_query].to_h)
277
+
278
+ headers =
279
+ DemoApiScalarGalaxy::Internal::Util.normalized_headers(
280
+ @headers,
281
+ auth_headers,
282
+ req[:headers].to_h,
283
+ opts[:extra_headers].to_h
284
+ )
285
+
286
+ if @idempotency_header && !headers.key?(@idempotency_header) &&
287
+ (!Net::HTTP::IDEMPOTENT_METHODS_.include?(method.to_s.upcase) || opts.key?(:idempotency_key))
288
+ headers[@idempotency_header] = opts.fetch(:idempotency_key) { generate_idempotency_key }
289
+ end
290
+
291
+ headers["x-scalar-retry-count"] = "0" unless headers.key?("x-scalar-retry-count")
292
+
293
+ timeout = opts.fetch(:timeout, @timeout).to_f.clamp(0..)
294
+ headers["x-scalar-timeout"] = timeout.to_s unless headers.key?("x-scalar-timeout") || timeout.zero?
295
+
296
+ headers.reject! { |_, v| v.to_s.empty? }
297
+
298
+ body =
299
+ case method
300
+ in :get | :head | :options | :trace
301
+ nil
302
+ else
303
+ DemoApiScalarGalaxy::Internal::Util.deep_merge(*[req[:body], opts[:extra_body]].compact)
304
+ end
305
+
306
+ # Generated methods always pass `req[:body]` for operations that define a
307
+ # request body, so only elide the content-type header when the operation
308
+ # has no body at all, not when an optional body param was omitted.
309
+ headers.delete("content-type") if body.nil? && !req.key?(:body)
310
+
311
+ url =
312
+ DemoApiScalarGalaxy::Internal::Util.join_parsed_uri(
313
+ @base_url_components,
314
+ {**req, path: path, query: query}
315
+ )
316
+ headers, encoded = DemoApiScalarGalaxy::Internal::Util.encode_content(headers, body)
317
+ {
318
+ method: method,
319
+ url: url,
320
+ headers: headers,
321
+ body: encoded,
322
+ max_retries: opts.fetch(:max_retries, @max_retries),
323
+ timeout: timeout
324
+ }
325
+ end
326
+
327
+ # @api private
328
+ #
329
+ # @param headers [Hash{String=>String}]
330
+ # @param retry_count [Integer]
331
+ #
332
+ # @return [Float]
333
+ private def retry_delay(headers, retry_count:)
334
+ # Non-standard extension
335
+ span = Float(headers["retry-after-ms"], exception: false)&.then { _1 / 1000 }
336
+ return span if span
337
+
338
+ retry_header = headers["retry-after"]
339
+ return span if (span = Float(retry_header, exception: false))
340
+
341
+ span =
342
+ retry_header&.then do
343
+ Time.httpdate(_1) - Time.now
344
+ rescue ArgumentError
345
+ nil
346
+ end
347
+ return span if span
348
+
349
+ scale = retry_count**2
350
+ jitter = 1 - (0.25 * rand)
351
+ (@initial_retry_delay * scale * jitter).clamp(0, @max_retry_delay)
352
+ end
353
+
354
+ # @api private
355
+ #
356
+ # @param request [Hash{Symbol=>Object}] .
357
+ #
358
+ # @option request [Symbol] :method
359
+ #
360
+ # @option request [URI::Generic] :url
361
+ #
362
+ # @option request [Hash{String=>String}] :headers
363
+ #
364
+ # @option request [Object] :body
365
+ #
366
+ # @option request [Integer] :max_retries
367
+ #
368
+ # @option request [Float] :timeout
369
+ #
370
+ # @param redirect_count [Integer]
371
+ #
372
+ # @param retry_count [Integer]
373
+ #
374
+ # @param send_retry_header [Boolean]
375
+ #
376
+ # @raise [DemoApiScalarGalaxy::Errors::APIError]
377
+ # @return [Array(Integer, Net::HTTPResponse, Enumerable<String>)]
378
+ def send_request(request, redirect_count:, retry_count:, send_retry_header:)
379
+ url, headers, max_retries, timeout = request.fetch_values(:url, :headers, :max_retries, :timeout)
380
+ input = {
381
+ **request.except(:timeout),
382
+ deadline: DemoApiScalarGalaxy::Internal::Util.monotonic_secs + timeout
383
+ }
384
+
385
+ headers["x-scalar-retry-count"] = retry_count.to_s if send_retry_header
386
+
387
+ begin
388
+ status, response, stream = @requester.execute(input)
389
+ rescue DemoApiScalarGalaxy::Errors::APIConnectionError => e
390
+ status = e
391
+ end
392
+ headers = DemoApiScalarGalaxy::Internal::Util.normalized_headers(response&.each_header&.to_h)
393
+
394
+ case status
395
+ in ..299
396
+ [status, response, stream]
397
+ in 300..399 if redirect_count >= self.class::MAX_REDIRECTS
398
+ self.class.reap_connection!(status, stream: stream)
399
+
400
+ message = "Failed to complete the request within #{self.class::MAX_REDIRECTS} redirects."
401
+ raise DemoApiScalarGalaxy::Errors::APIConnectionError.new(
402
+ url: url,
403
+ response: response,
404
+ message: message
405
+ )
406
+ in 300..399
407
+ self.class.reap_connection!(status, stream: stream)
408
+
409
+ request = self.class.follow_redirect(request, status: status, response_headers: headers)
410
+ send_request(
411
+ request,
412
+ redirect_count: redirect_count + 1,
413
+ retry_count: retry_count,
414
+ send_retry_header: send_retry_header
415
+ )
416
+ in DemoApiScalarGalaxy::Errors::APIConnectionError if retry_count >= max_retries
417
+ raise status
418
+ in (400..) if retry_count >= max_retries || !self.class.should_retry?(status, headers: headers)
419
+ decoded =
420
+ Kernel.then do
421
+ DemoApiScalarGalaxy::Internal::Util.decode_content(
422
+ headers,
423
+ stream: stream,
424
+ suppress_error: true
425
+ )
426
+ ensure
427
+ self.class.reap_connection!(status, stream: stream)
428
+ end
429
+
430
+ raise DemoApiScalarGalaxy::Errors::APIStatusError.for(
431
+ url: url,
432
+ status: status,
433
+ headers: headers,
434
+ body: decoded,
435
+ request: nil,
436
+ response: response
437
+ )
438
+ in 400.. | DemoApiScalarGalaxy::Errors::APIConnectionError
439
+ self.class.reap_connection!(status, stream: stream)
440
+
441
+ delay = retry_delay(response || {}, retry_count: retry_count)
442
+ sleep(delay)
443
+
444
+ send_request(
445
+ request,
446
+ redirect_count: redirect_count,
447
+ retry_count: retry_count + 1,
448
+ send_retry_header: send_retry_header
449
+ )
450
+ end
451
+ end
452
+
453
+ # Execute the request specified by `req`. This is the method that all resource
454
+ # methods call into.
455
+ #
456
+ # @overload request(method, path, query: {}, headers: {}, body: nil, unwrap: nil, page: nil, stream: nil, model: DemoApiScalarGalaxy::Internal::Type::Unknown, options: {})
457
+ #
458
+ # @param method [Symbol]
459
+ #
460
+ # @param path [String, Array<String>]
461
+ #
462
+ # @param query [Hash{String=>Array<String>, String, nil}, nil]
463
+ #
464
+ # @param headers [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil]
465
+ #
466
+ # @param body [Object, nil]
467
+ #
468
+ # @param unwrap [Symbol, Integer, Array<Symbol, Integer>, Proc, nil]
469
+ #
470
+ # @param page [Class<DemoApiScalarGalaxy::Internal::Type::BasePage>, nil]
471
+ #
472
+ # @param stream [Class<DemoApiScalarGalaxy::Internal::Type::BaseStream>, nil]
473
+ #
474
+ # @param model [DemoApiScalarGalaxy::Internal::Type::Converter, Class, nil]
475
+ #
476
+ # @param options [DemoApiScalarGalaxy::RequestOptions, Hash{Symbol=>Object}, nil] .
477
+ #
478
+ # @option options [String, nil] :idempotency_key
479
+ #
480
+ # @option options [Hash{String=>Array<String>, String, nil}, nil] :extra_query
481
+ #
482
+ # @option options [Hash{String=>String, nil}, nil] :extra_headers
483
+ #
484
+ # @option options [Object, nil] :extra_body
485
+ #
486
+ # @option options [Integer, nil] :max_retries
487
+ #
488
+ # @option options [Float, nil] :timeout
489
+ #
490
+ # @raise [DemoApiScalarGalaxy::Errors::APIError]
491
+ # @return [Object]
492
+ def request(req)
493
+ self.class.validate!(req)
494
+ model = req.fetch(:model) { DemoApiScalarGalaxy::Internal::Type::Unknown }
495
+ opts = req[:options].to_h
496
+ unwrap = req[:unwrap]
497
+ DemoApiScalarGalaxy::RequestOptions.validate!(opts)
498
+ request = build_request(req.except(:options), opts)
499
+ url = request.fetch(:url)
500
+
501
+ # Don't send the current retry count in the headers if the caller modified the header defaults.
502
+ send_retry_header = request.fetch(:headers)["x-scalar-retry-count"] == "0"
503
+ status, response, stream =
504
+ send_request(request, redirect_count: 0, retry_count: 0, send_retry_header: send_retry_header)
505
+
506
+ headers = DemoApiScalarGalaxy::Internal::Util.normalized_headers(response.each_header.to_h)
507
+ decoded = DemoApiScalarGalaxy::Internal::Util.decode_content(headers, stream: stream)
508
+ case req
509
+ in stream: Class => st
510
+ st.new(
511
+ model: model,
512
+ url: url,
513
+ status: status,
514
+ headers: headers,
515
+ response: response,
516
+ unwrap: unwrap,
517
+ stream: decoded
518
+ )
519
+ in page: Class => page
520
+ page.new(client: self, req: req, headers: headers, page_data: decoded)
521
+ else
522
+ unwrapped = DemoApiScalarGalaxy::Internal::Util.dig(decoded, unwrap)
523
+ DemoApiScalarGalaxy::Internal::Type::Converter.coerce(model, unwrapped)
524
+ end
525
+ end
526
+
527
+ # @api private
528
+ #
529
+ # @return [String]
530
+ def inspect
531
+ # rubocop:disable Layout/LineLength
532
+ "#<#{self.class.name}:0x#{object_id.to_s(16)} base_url=#{@base_url} max_retries=#{@max_retries} timeout=#{@timeout}>"
533
+ # rubocop:enable Layout/LineLength
534
+ end
535
+
536
+ define_sorbet_constant!(:RequestComponents) do
537
+ T.type_alias do
538
+ {
539
+ method: Symbol,
540
+ path: T.any(String, T::Array[String]),
541
+ query: T.nilable(T::Hash[String, T.nilable(T.any(T::Array[String], String))]),
542
+ headers:
543
+ T.nilable(
544
+ T::Hash[
545
+ String,
546
+ T.nilable(T.any(String, Integer, T::Array[T.nilable(T.any(String, Integer))]))
547
+ ]
548
+ ),
549
+ body: T.nilable(T.anything),
550
+ unwrap:
551
+ T.nilable(
552
+ T.any(
553
+ Symbol,
554
+ Integer,
555
+ T::Array[T.any(Symbol, Integer)],
556
+ T.proc.params(arg0: T.anything).returns(T.anything)
557
+ )
558
+ ),
559
+ page:
560
+ T.nilable(
561
+ T::Class[
562
+ DemoApiScalarGalaxy::Internal::Type::BasePage[
563
+ DemoApiScalarGalaxy::Internal::Type::BaseModel
564
+ ]
565
+ ]
566
+ ),
567
+ stream: T.nilable(T::Class[T.anything]),
568
+ model: T.nilable(DemoApiScalarGalaxy::Internal::Type::Converter::Input),
569
+ options: T.nilable(DemoApiScalarGalaxy::RequestOptions::OrHash)
570
+ }
571
+ end
572
+ end
573
+ define_sorbet_constant!(:RequestInput) do
574
+ T.type_alias do
575
+ {
576
+ method: Symbol,
577
+ url: URI::Generic,
578
+ headers: T::Hash[String, String],
579
+ body: T.anything,
580
+ max_retries: Integer,
581
+ timeout: Float
582
+ }
583
+ end
584
+ end
585
+ end
586
+ end
587
+ end
588
+ end