brand.dev 0.0.1.pre.alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +229 -0
- data/SECURITY.md +23 -0
- data/lib/brand_dev/client.rb +74 -0
- data/lib/brand_dev/errors.rb +192 -0
- data/lib/brand_dev/file_part.rb +55 -0
- data/lib/brand_dev/internal/transport/base_client.rb +555 -0
- data/lib/brand_dev/internal/transport/pooled_net_requester.rb +209 -0
- data/lib/brand_dev/internal/type/array_of.rb +162 -0
- data/lib/brand_dev/internal/type/base_model.rb +484 -0
- data/lib/brand_dev/internal/type/base_page.rb +55 -0
- data/lib/brand_dev/internal/type/boolean.rb +71 -0
- data/lib/brand_dev/internal/type/converter.rb +292 -0
- data/lib/brand_dev/internal/type/enum.rb +120 -0
- data/lib/brand_dev/internal/type/file_input.rb +103 -0
- data/lib/brand_dev/internal/type/hash_of.rb +182 -0
- data/lib/brand_dev/internal/type/request_parameters.rb +42 -0
- data/lib/brand_dev/internal/type/union.rb +227 -0
- data/lib/brand_dev/internal/type/unknown.rb +75 -0
- data/lib/brand_dev/internal/util.rb +915 -0
- data/lib/brand_dev/internal.rb +20 -0
- data/lib/brand_dev/models/brand_identify_from_transaction_params.rb +22 -0
- data/lib/brand_dev/models/brand_identify_from_transaction_response.rb +435 -0
- data/lib/brand_dev/models/brand_retrieve_by_ticker_params.rb +22 -0
- data/lib/brand_dev/models/brand_retrieve_by_ticker_response.rb +432 -0
- data/lib/brand_dev/models/brand_retrieve_naics_params.rb +27 -0
- data/lib/brand_dev/models/brand_retrieve_naics_response.rb +61 -0
- data/lib/brand_dev/models/brand_retrieve_params.rb +91 -0
- data/lib/brand_dev/models/brand_retrieve_response.rb +432 -0
- data/lib/brand_dev/models/brand_search_params.rb +22 -0
- data/lib/brand_dev/models/brand_search_response.rb +35 -0
- data/lib/brand_dev/models.rb +51 -0
- data/lib/brand_dev/request_options.rb +77 -0
- data/lib/brand_dev/resources/brand.rb +130 -0
- data/lib/brand_dev/version.rb +5 -0
- data/lib/brand_dev.rb +64 -0
- data/manifest.yaml +15 -0
- data/rbi/brand_dev/client.rbi +49 -0
- data/rbi/brand_dev/errors.rbi +162 -0
- data/rbi/brand_dev/file_part.rbi +37 -0
- data/rbi/brand_dev/internal/transport/base_client.rbi +293 -0
- data/rbi/brand_dev/internal/transport/pooled_net_requester.rbi +79 -0
- data/rbi/brand_dev/internal/type/array_of.rbi +104 -0
- data/rbi/brand_dev/internal/type/base_model.rbi +302 -0
- data/rbi/brand_dev/internal/type/base_page.rbi +42 -0
- data/rbi/brand_dev/internal/type/boolean.rbi +56 -0
- data/rbi/brand_dev/internal/type/converter.rbi +162 -0
- data/rbi/brand_dev/internal/type/enum.rbi +82 -0
- data/rbi/brand_dev/internal/type/file_input.rbi +59 -0
- data/rbi/brand_dev/internal/type/hash_of.rbi +104 -0
- data/rbi/brand_dev/internal/type/request_parameters.rbi +29 -0
- data/rbi/brand_dev/internal/type/union.rbi +116 -0
- data/rbi/brand_dev/internal/type/unknown.rbi +56 -0
- data/rbi/brand_dev/internal/util.rbi +485 -0
- data/rbi/brand_dev/internal.rbi +16 -0
- data/rbi/brand_dev/models/brand_identify_from_transaction_params.rbi +46 -0
- data/rbi/brand_dev/models/brand_identify_from_transaction_response.rbi +981 -0
- data/rbi/brand_dev/models/brand_retrieve_by_ticker_params.rbi +43 -0
- data/rbi/brand_dev/models/brand_retrieve_by_ticker_response.rbi +976 -0
- data/rbi/brand_dev/models/brand_retrieve_naics_params.rbi +44 -0
- data/rbi/brand_dev/models/brand_retrieve_naics_response.rbi +127 -0
- data/rbi/brand_dev/models/brand_retrieve_params.rbi +344 -0
- data/rbi/brand_dev/models/brand_retrieve_response.rbi +949 -0
- data/rbi/brand_dev/models/brand_search_params.rbi +40 -0
- data/rbi/brand_dev/models/brand_search_response.rbi +63 -0
- data/rbi/brand_dev/models.rbi +14 -0
- data/rbi/brand_dev/request_options.rbi +59 -0
- data/rbi/brand_dev/resources/brand.rbi +89 -0
- data/rbi/brand_dev/version.rbi +5 -0
- data/sig/brand_dev/client.rbs +26 -0
- data/sig/brand_dev/errors.rbs +101 -0
- data/sig/brand_dev/file_part.rbs +21 -0
- data/sig/brand_dev/internal/transport/base_client.rbs +131 -0
- data/sig/brand_dev/internal/transport/pooled_net_requester.rbs +45 -0
- data/sig/brand_dev/internal/type/array_of.rbs +48 -0
- data/sig/brand_dev/internal/type/base_model.rbs +102 -0
- data/sig/brand_dev/internal/type/base_page.rbs +24 -0
- data/sig/brand_dev/internal/type/boolean.rbs +26 -0
- data/sig/brand_dev/internal/type/converter.rbs +56 -0
- data/sig/brand_dev/internal/type/enum.rbs +32 -0
- data/sig/brand_dev/internal/type/file_input.rbs +25 -0
- data/sig/brand_dev/internal/type/hash_of.rbs +48 -0
- data/sig/brand_dev/internal/type/request_parameters.rbs +17 -0
- data/sig/brand_dev/internal/type/union.rbs +52 -0
- data/sig/brand_dev/internal/type/unknown.rbs +26 -0
- data/sig/brand_dev/internal/util.rbs +185 -0
- data/sig/brand_dev/internal.rbs +9 -0
- data/sig/brand_dev/models/brand_identify_from_transaction_params.rbs +24 -0
- data/sig/brand_dev/models/brand_identify_from_transaction_response.rbs +418 -0
- data/sig/brand_dev/models/brand_retrieve_by_ticker_params.rbs +23 -0
- data/sig/brand_dev/models/brand_retrieve_by_ticker_response.rbs +418 -0
- data/sig/brand_dev/models/brand_retrieve_naics_params.rbs +23 -0
- data/sig/brand_dev/models/brand_retrieve_naics_response.rbs +61 -0
- data/sig/brand_dev/models/brand_retrieve_params.rbs +148 -0
- data/sig/brand_dev/models/brand_retrieve_response.rbs +418 -0
- data/sig/brand_dev/models/brand_search_params.rbs +23 -0
- data/sig/brand_dev/models/brand_search_response.rbs +29 -0
- data/sig/brand_dev/models.rbs +11 -0
- data/sig/brand_dev/request_options.rbs +34 -0
- data/sig/brand_dev/resources/brand.rbs +33 -0
- data/sig/brand_dev/version.rbs +3 -0
- metadata +160 -0
@@ -0,0 +1,555 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BrandDev
|
4
|
+
module Internal
|
5
|
+
module Transport
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# @abstract
|
9
|
+
class BaseClient
|
10
|
+
extend BrandDev::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" => BrandDev::Internal::Util.arch,
|
19
|
+
"x-stainless-lang" => "ruby",
|
20
|
+
"x-stainless-os" => BrandDev::Internal::Util.os,
|
21
|
+
"x-stainless-package-version" => BrandDev::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}, Net::HTTPHeader]
|
51
|
+
#
|
52
|
+
# @return [Boolean]
|
53
|
+
def should_retry?(status, headers:)
|
54
|
+
coerced = BrandDev::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}, Net::HTTPHeader]
|
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 BrandDev::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 BrandDev::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 BrandDev::Internal::Util.uri_origin(url) != BrandDev::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, BrandDev::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 BrandDev::Errors::APIConnectionError | (500..)
|
150
|
+
BrandDev::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 [BrandDev::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 = BrandDev::Internal::Transport::PooledNetRequester.new
|
200
|
+
@headers = BrandDev::Internal::Util.normalized_headers(
|
201
|
+
self.class::PLATFORM_HEADERS,
|
202
|
+
{
|
203
|
+
"accept" => "application/json",
|
204
|
+
"content-type" => "application/json"
|
205
|
+
},
|
206
|
+
headers
|
207
|
+
)
|
208
|
+
@base_url_components = BrandDev::Internal::Util.parse_uri(base_url)
|
209
|
+
@base_url = BrandDev::Internal::Util.unparse_uri(@base_url_components)
|
210
|
+
@idempotency_header = idempotency_header&.to_s&.downcase
|
211
|
+
@timeout = timeout
|
212
|
+
@max_retries = max_retries
|
213
|
+
@initial_retry_delay = initial_retry_delay
|
214
|
+
@max_retry_delay = max_retry_delay
|
215
|
+
end
|
216
|
+
|
217
|
+
# @api private
|
218
|
+
#
|
219
|
+
# @return [Hash{String=>String}]
|
220
|
+
private def auth_headers = {}
|
221
|
+
|
222
|
+
# @api private
|
223
|
+
#
|
224
|
+
# @return [String]
|
225
|
+
private def generate_idempotency_key = "stainless-ruby-retry-#{SecureRandom.uuid}"
|
226
|
+
|
227
|
+
# @api private
|
228
|
+
#
|
229
|
+
# @param req [Hash{Symbol=>Object}] .
|
230
|
+
#
|
231
|
+
# @option req [Symbol] :method
|
232
|
+
#
|
233
|
+
# @option req [String, Array<String>] :path
|
234
|
+
#
|
235
|
+
# @option req [Hash{String=>Array<String>, String, nil}, nil] :query
|
236
|
+
#
|
237
|
+
# @option req [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil] :headers
|
238
|
+
#
|
239
|
+
# @option req [Object, nil] :body
|
240
|
+
#
|
241
|
+
# @option req [Symbol, Integer, Array<Symbol, Integer>, Proc, nil] :unwrap
|
242
|
+
#
|
243
|
+
# @option req [Class<BrandDev::Internal::Type::BasePage>, nil] :page
|
244
|
+
#
|
245
|
+
# @option req [Class<BrandDev::Internal::Type::BaseStream>, nil] :stream
|
246
|
+
#
|
247
|
+
# @option req [BrandDev::Internal::Type::Converter, Class, nil] :model
|
248
|
+
#
|
249
|
+
# @param opts [Hash{Symbol=>Object}] .
|
250
|
+
#
|
251
|
+
# @option opts [String, nil] :idempotency_key
|
252
|
+
#
|
253
|
+
# @option opts [Hash{String=>Array<String>, String, nil}, nil] :extra_query
|
254
|
+
#
|
255
|
+
# @option opts [Hash{String=>String, nil}, nil] :extra_headers
|
256
|
+
#
|
257
|
+
# @option opts [Object, nil] :extra_body
|
258
|
+
#
|
259
|
+
# @option opts [Integer, nil] :max_retries
|
260
|
+
#
|
261
|
+
# @option opts [Float, nil] :timeout
|
262
|
+
#
|
263
|
+
# @return [Hash{Symbol=>Object}]
|
264
|
+
private def build_request(req, opts)
|
265
|
+
method, uninterpolated_path = req.fetch_values(:method, :path)
|
266
|
+
|
267
|
+
path = BrandDev::Internal::Util.interpolate_path(uninterpolated_path)
|
268
|
+
|
269
|
+
query = BrandDev::Internal::Util.deep_merge(req[:query].to_h, opts[:extra_query].to_h)
|
270
|
+
|
271
|
+
headers = BrandDev::Internal::Util.normalized_headers(
|
272
|
+
@headers,
|
273
|
+
auth_headers,
|
274
|
+
req[:headers].to_h,
|
275
|
+
opts[:extra_headers].to_h
|
276
|
+
)
|
277
|
+
|
278
|
+
if @idempotency_header &&
|
279
|
+
!headers.key?(@idempotency_header) &&
|
280
|
+
(!Net::HTTP::IDEMPOTENT_METHODS_.include?(method.to_s.upcase) || opts.key?(:idempotency_key))
|
281
|
+
headers[@idempotency_header] = opts.fetch(:idempotency_key) { generate_idempotency_key }
|
282
|
+
end
|
283
|
+
|
284
|
+
unless headers.key?("x-stainless-retry-count")
|
285
|
+
headers["x-stainless-retry-count"] = "0"
|
286
|
+
end
|
287
|
+
|
288
|
+
timeout = opts.fetch(:timeout, @timeout).to_f.clamp(0..)
|
289
|
+
unless headers.key?("x-stainless-timeout") || timeout.zero?
|
290
|
+
headers["x-stainless-timeout"] = timeout.to_s
|
291
|
+
end
|
292
|
+
|
293
|
+
headers.reject! { |_, v| v.to_s.empty? }
|
294
|
+
|
295
|
+
body =
|
296
|
+
case method
|
297
|
+
in :get | :head | :options | :trace
|
298
|
+
nil
|
299
|
+
else
|
300
|
+
BrandDev::Internal::Util.deep_merge(*[req[:body], opts[:extra_body]].compact)
|
301
|
+
end
|
302
|
+
|
303
|
+
url = BrandDev::Internal::Util.join_parsed_uri(
|
304
|
+
@base_url_components,
|
305
|
+
{**req, path: path, query: query}
|
306
|
+
)
|
307
|
+
headers, encoded = BrandDev::Internal::Util.encode_content(headers, body)
|
308
|
+
{
|
309
|
+
method: method,
|
310
|
+
url: url,
|
311
|
+
headers: headers,
|
312
|
+
body: encoded,
|
313
|
+
max_retries: opts.fetch(:max_retries, @max_retries),
|
314
|
+
timeout: timeout
|
315
|
+
}
|
316
|
+
end
|
317
|
+
|
318
|
+
# @api private
|
319
|
+
#
|
320
|
+
# @param headers [Hash{String=>String}]
|
321
|
+
# @param retry_count [Integer]
|
322
|
+
#
|
323
|
+
# @return [Float]
|
324
|
+
private def retry_delay(headers, retry_count:)
|
325
|
+
# Non-standard extension
|
326
|
+
span = Float(headers["retry-after-ms"], exception: false)&.then { _1 / 1000 }
|
327
|
+
return span if span
|
328
|
+
|
329
|
+
retry_header = headers["retry-after"]
|
330
|
+
return span if (span = Float(retry_header, exception: false))
|
331
|
+
|
332
|
+
span = retry_header&.then do
|
333
|
+
Time.httpdate(_1) - Time.now
|
334
|
+
rescue ArgumentError
|
335
|
+
nil
|
336
|
+
end
|
337
|
+
return span if span
|
338
|
+
|
339
|
+
scale = retry_count**2
|
340
|
+
jitter = 1 - (0.25 * rand)
|
341
|
+
(@initial_retry_delay * scale * jitter).clamp(0, @max_retry_delay)
|
342
|
+
end
|
343
|
+
|
344
|
+
# @api private
|
345
|
+
#
|
346
|
+
# @param request [Hash{Symbol=>Object}] .
|
347
|
+
#
|
348
|
+
# @option request [Symbol] :method
|
349
|
+
#
|
350
|
+
# @option request [URI::Generic] :url
|
351
|
+
#
|
352
|
+
# @option request [Hash{String=>String}] :headers
|
353
|
+
#
|
354
|
+
# @option request [Object] :body
|
355
|
+
#
|
356
|
+
# @option request [Integer] :max_retries
|
357
|
+
#
|
358
|
+
# @option request [Float] :timeout
|
359
|
+
#
|
360
|
+
# @param redirect_count [Integer]
|
361
|
+
#
|
362
|
+
# @param retry_count [Integer]
|
363
|
+
#
|
364
|
+
# @param send_retry_header [Boolean]
|
365
|
+
#
|
366
|
+
# @raise [BrandDev::Errors::APIError]
|
367
|
+
# @return [Array(Integer, Net::HTTPResponse, Enumerable<String>)]
|
368
|
+
private def send_request(request, redirect_count:, retry_count:, send_retry_header:)
|
369
|
+
url, headers, max_retries, timeout = request.fetch_values(:url, :headers, :max_retries, :timeout)
|
370
|
+
input = {**request.except(:timeout), deadline: BrandDev::Internal::Util.monotonic_secs + timeout}
|
371
|
+
|
372
|
+
if send_retry_header
|
373
|
+
headers["x-stainless-retry-count"] = retry_count.to_s
|
374
|
+
end
|
375
|
+
|
376
|
+
begin
|
377
|
+
status, response, stream = @requester.execute(input)
|
378
|
+
rescue BrandDev::Errors::APIConnectionError => e
|
379
|
+
status = e
|
380
|
+
end
|
381
|
+
|
382
|
+
case status
|
383
|
+
in ..299
|
384
|
+
[status, response, stream]
|
385
|
+
in 300..399 if redirect_count >= self.class::MAX_REDIRECTS
|
386
|
+
self.class.reap_connection!(status, stream: stream)
|
387
|
+
|
388
|
+
message = "Failed to complete the request within #{self.class::MAX_REDIRECTS} redirects."
|
389
|
+
raise BrandDev::Errors::APIConnectionError.new(url: url, response: response, message: message)
|
390
|
+
in 300..399
|
391
|
+
self.class.reap_connection!(status, stream: stream)
|
392
|
+
|
393
|
+
request = self.class.follow_redirect(request, status: status, response_headers: response)
|
394
|
+
send_request(
|
395
|
+
request,
|
396
|
+
redirect_count: redirect_count + 1,
|
397
|
+
retry_count: retry_count,
|
398
|
+
send_retry_header: send_retry_header
|
399
|
+
)
|
400
|
+
in BrandDev::Errors::APIConnectionError if retry_count >= max_retries
|
401
|
+
raise status
|
402
|
+
in (400..) if retry_count >= max_retries || !self.class.should_retry?(status, headers: response)
|
403
|
+
decoded = Kernel.then do
|
404
|
+
BrandDev::Internal::Util.decode_content(response, stream: stream, suppress_error: true)
|
405
|
+
ensure
|
406
|
+
self.class.reap_connection!(status, stream: stream)
|
407
|
+
end
|
408
|
+
|
409
|
+
raise BrandDev::Errors::APIStatusError.for(
|
410
|
+
url: url,
|
411
|
+
status: status,
|
412
|
+
body: decoded,
|
413
|
+
request: nil,
|
414
|
+
response: response
|
415
|
+
)
|
416
|
+
in (400..) | BrandDev::Errors::APIConnectionError
|
417
|
+
self.class.reap_connection!(status, stream: stream)
|
418
|
+
|
419
|
+
delay = retry_delay(response || {}, retry_count: retry_count)
|
420
|
+
sleep(delay)
|
421
|
+
|
422
|
+
send_request(
|
423
|
+
request,
|
424
|
+
redirect_count: redirect_count,
|
425
|
+
retry_count: retry_count + 1,
|
426
|
+
send_retry_header: send_retry_header
|
427
|
+
)
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# Execute the request specified by `req`. This is the method that all resource
|
432
|
+
# methods call into.
|
433
|
+
#
|
434
|
+
# @overload request(method, path, query: {}, headers: {}, body: nil, unwrap: nil, page: nil, stream: nil, model: BrandDev::Internal::Type::Unknown, options: {})
|
435
|
+
#
|
436
|
+
# @param method [Symbol]
|
437
|
+
#
|
438
|
+
# @param path [String, Array<String>]
|
439
|
+
#
|
440
|
+
# @param query [Hash{String=>Array<String>, String, nil}, nil]
|
441
|
+
#
|
442
|
+
# @param headers [Hash{String=>String, Integer, Array<String, Integer, nil>, nil}, nil]
|
443
|
+
#
|
444
|
+
# @param body [Object, nil]
|
445
|
+
#
|
446
|
+
# @param unwrap [Symbol, Integer, Array<Symbol, Integer>, Proc, nil]
|
447
|
+
#
|
448
|
+
# @param page [Class<BrandDev::Internal::Type::BasePage>, nil]
|
449
|
+
#
|
450
|
+
# @param stream [Class<BrandDev::Internal::Type::BaseStream>, nil]
|
451
|
+
#
|
452
|
+
# @param model [BrandDev::Internal::Type::Converter, Class, nil]
|
453
|
+
#
|
454
|
+
# @param options [BrandDev::RequestOptions, Hash{Symbol=>Object}, nil] .
|
455
|
+
#
|
456
|
+
# @option options [String, nil] :idempotency_key
|
457
|
+
#
|
458
|
+
# @option options [Hash{String=>Array<String>, String, nil}, nil] :extra_query
|
459
|
+
#
|
460
|
+
# @option options [Hash{String=>String, nil}, nil] :extra_headers
|
461
|
+
#
|
462
|
+
# @option options [Object, nil] :extra_body
|
463
|
+
#
|
464
|
+
# @option options [Integer, nil] :max_retries
|
465
|
+
#
|
466
|
+
# @option options [Float, nil] :timeout
|
467
|
+
#
|
468
|
+
# @raise [BrandDev::Errors::APIError]
|
469
|
+
# @return [Object]
|
470
|
+
def request(req)
|
471
|
+
self.class.validate!(req)
|
472
|
+
model = req.fetch(:model) { BrandDev::Internal::Type::Unknown }
|
473
|
+
opts = req[:options].to_h
|
474
|
+
BrandDev::RequestOptions.validate!(opts)
|
475
|
+
request = build_request(req.except(:options), opts)
|
476
|
+
url = request.fetch(:url)
|
477
|
+
|
478
|
+
# Don't send the current retry count in the headers if the caller modified the header defaults.
|
479
|
+
send_retry_header = request.fetch(:headers)["x-stainless-retry-count"] == "0"
|
480
|
+
status, response, stream = send_request(
|
481
|
+
request,
|
482
|
+
redirect_count: 0,
|
483
|
+
retry_count: 0,
|
484
|
+
send_retry_header: send_retry_header
|
485
|
+
)
|
486
|
+
|
487
|
+
decoded = BrandDev::Internal::Util.decode_content(response, stream: stream)
|
488
|
+
case req
|
489
|
+
in {stream: Class => st}
|
490
|
+
st.new(model: model, url: url, status: status, response: response, stream: decoded)
|
491
|
+
in {page: Class => page}
|
492
|
+
page.new(client: self, req: req, headers: response, page_data: decoded)
|
493
|
+
else
|
494
|
+
unwrapped = BrandDev::Internal::Util.dig(decoded, req[:unwrap])
|
495
|
+
BrandDev::Internal::Type::Converter.coerce(model, unwrapped)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
# @api private
|
500
|
+
#
|
501
|
+
# @return [String]
|
502
|
+
def inspect
|
503
|
+
# rubocop:disable Layout/LineLength
|
504
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} base_url=#{@base_url} max_retries=#{@max_retries} timeout=#{@timeout}>"
|
505
|
+
# rubocop:enable Layout/LineLength
|
506
|
+
end
|
507
|
+
|
508
|
+
define_sorbet_constant!(:RequestComponents) do
|
509
|
+
T.type_alias do
|
510
|
+
{
|
511
|
+
method: Symbol,
|
512
|
+
path: T.any(String, T::Array[String]),
|
513
|
+
query: T.nilable(T::Hash[String, T.nilable(T.any(T::Array[String], String))]),
|
514
|
+
headers: T.nilable(
|
515
|
+
T::Hash[String,
|
516
|
+
T.nilable(
|
517
|
+
T.any(
|
518
|
+
String,
|
519
|
+
Integer,
|
520
|
+
T::Array[T.nilable(T.any(String, Integer))]
|
521
|
+
)
|
522
|
+
)]
|
523
|
+
),
|
524
|
+
body: T.nilable(T.anything),
|
525
|
+
unwrap: T.nilable(
|
526
|
+
T.any(
|
527
|
+
Symbol,
|
528
|
+
Integer,
|
529
|
+
T::Array[T.any(Symbol, Integer)],
|
530
|
+
T.proc.params(arg0: T.anything).returns(T.anything)
|
531
|
+
)
|
532
|
+
),
|
533
|
+
page: T.nilable(T::Class[BrandDev::Internal::Type::BasePage[BrandDev::Internal::Type::BaseModel]]),
|
534
|
+
stream: T.nilable(T::Class[T.anything]),
|
535
|
+
model: T.nilable(BrandDev::Internal::Type::Converter::Input),
|
536
|
+
options: T.nilable(BrandDev::RequestOptions::OrHash)
|
537
|
+
}
|
538
|
+
end
|
539
|
+
end
|
540
|
+
define_sorbet_constant!(:RequestInput) do
|
541
|
+
T.type_alias do
|
542
|
+
{
|
543
|
+
method: Symbol,
|
544
|
+
url: URI::Generic,
|
545
|
+
headers: T::Hash[String, String],
|
546
|
+
body: T.anything,
|
547
|
+
max_retries: Integer,
|
548
|
+
timeout: Float
|
549
|
+
}
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
end
|
555
|
+
end
|