orb-billing 0.1.2 → 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.
- checksums.yaml +4 -4
- data/.ignore +2 -0
- data/CHANGELOG.md +972 -0
- data/README.md +7 -15
- data/SECURITY.md +27 -0
- data/lib/orb/client.rb +3 -2
- data/lib/orb/internal/page.rb +33 -31
- data/lib/orb/internal/transport/base_client.rb +14 -4
- data/lib/orb/internal/transport/pooled_net_requester.rb +1 -1
- data/lib/orb/internal/type/array_of.rb +17 -2
- data/lib/orb/internal/type/base_model.rb +52 -8
- data/lib/orb/internal/type/base_page.rb +1 -0
- data/lib/orb/internal/type/boolean.rb +2 -0
- data/lib/orb/internal/type/converter.rb +24 -0
- data/lib/orb/internal/type/enum.rb +19 -3
- data/lib/orb/internal/type/hash_of.rb +17 -2
- data/lib/orb/internal/type/io_like.rb +2 -0
- data/lib/orb/internal/type/union.rb +17 -3
- data/lib/orb/internal/type/unknown.rb +2 -0
- data/lib/orb/internal/util.rb +36 -9
- data/lib/orb/internal.rb +5 -1
- data/lib/orb/version.rb +1 -1
- data/rbi/lib/orb/client.rbi +3 -2
- data/rbi/lib/orb/internal/page.rbi +1 -0
- data/rbi/lib/orb/internal/transport/base_client.rbi +1 -0
- data/rbi/lib/orb/internal/type/array_of.rbi +12 -9
- data/rbi/lib/orb/internal/type/base_model.rbi +16 -0
- data/rbi/lib/orb/internal/type/boolean.rbi +4 -5
- data/rbi/lib/orb/internal/type/converter.rbi +8 -0
- data/rbi/lib/orb/internal/type/enum.rbi +4 -0
- data/rbi/lib/orb/internal/type/hash_of.rbi +12 -9
- data/rbi/lib/orb/internal/type/io_like.rbi +4 -5
- data/rbi/lib/orb/internal/type/union.rbi +4 -0
- data/rbi/lib/orb/internal/type/unknown.rbi +4 -5
- data/rbi/lib/orb/internal/util.rbi +15 -0
- data/rbi/lib/orb/internal.rbi +1 -1
- data/sig/orb/internal/type/array_of.rbs +2 -0
- data/sig/orb/internal/type/base_model.rbs +8 -0
- data/sig/orb/internal/type/converter.rbs +4 -0
- data/sig/orb/internal/type/enum.rbs +2 -0
- data/sig/orb/internal/type/hash_of.rbs +2 -0
- data/sig/orb/internal/type/union.rbs +2 -0
- data/sig/orb/internal/util.rbs +2 -0
- data/sig/orb/internal.rbs +1 -1
- metadata +5 -2
data/README.md
CHANGED
@@ -4,9 +4,9 @@ The Orb Ruby library provides convenient access to the Orb REST API from any Rub
|
|
4
4
|
|
5
5
|
## Documentation
|
6
6
|
|
7
|
-
Documentation for
|
7
|
+
Documentation for releases of this gem can be found [on RubyDoc](https://gemdocs.org/gems/orb-billing).
|
8
8
|
|
9
|
-
The
|
9
|
+
The REST API documentation can be found on [docs.withorb.com](https://docs.withorb.com/reference/api-reference).
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -15,22 +15,16 @@ To use this gem, install via Bundler by adding the following to your application
|
|
15
15
|
<!-- x-release-please-start-version -->
|
16
16
|
|
17
17
|
```ruby
|
18
|
-
gem "orb-billing", "~> 0.
|
18
|
+
gem "orb-billing", "~> 0.2.0"
|
19
19
|
```
|
20
20
|
|
21
21
|
<!-- x-release-please-end -->
|
22
22
|
|
23
|
-
To fetch an initial copy of the gem:
|
24
|
-
|
25
|
-
```sh
|
26
|
-
bundle install
|
27
|
-
```
|
28
|
-
|
29
23
|
## Usage
|
30
24
|
|
31
25
|
```ruby
|
32
26
|
require "bundler/setup"
|
33
|
-
require "orb
|
27
|
+
require "orb"
|
34
28
|
|
35
29
|
orb = Orb::Client.new(
|
36
30
|
api_key: "My API Key" # defaults to ENV["ORB_API_KEY"]
|
@@ -62,7 +56,7 @@ end
|
|
62
56
|
|
63
57
|
### Errors
|
64
58
|
|
65
|
-
When the library is unable to connect to the API, or if the API returns a non-success status code (i.e., 4xx or 5xx response), a subclass of `Orb::
|
59
|
+
When the library is unable to connect to the API, or if the API returns a non-success status code (i.e., 4xx or 5xx response), a subclass of `Orb::Errors::APIError` will be thrown:
|
66
60
|
|
67
61
|
```ruby
|
68
62
|
begin
|
@@ -168,8 +162,7 @@ Due to limitations with the Sorbet type system, where a method otherwise can tak
|
|
168
162
|
Please follow Sorbet's [setup guides](https://sorbet.org/docs/adopting) for best experience.
|
169
163
|
|
170
164
|
```ruby
|
171
|
-
params =
|
172
|
-
Orb::Models::CustomerCreateParams.new(email: "example-customer@withorb.com", name: "My Customer")
|
165
|
+
params = Orb::Models::CustomerCreateParams.new(email: "example-customer@withorb.com", name: "My Customer")
|
173
166
|
|
174
167
|
orb.customers.create(**params)
|
175
168
|
```
|
@@ -197,8 +190,7 @@ If you want to explicitly send an extra param, you can do so with the `extra_que
|
|
197
190
|
To make requests to undocumented endpoints, you can make requests using `client.request`. Options on the client will be respected (such as retries) when making this request.
|
198
191
|
|
199
192
|
```ruby
|
200
|
-
response =
|
201
|
-
client.request(
|
193
|
+
response = client.request(
|
202
194
|
method: :post,
|
203
195
|
path: '/undocumented/endpoint',
|
204
196
|
query: {"dog": "woof"},
|
data/SECURITY.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Security Policy
|
2
|
+
|
3
|
+
## Reporting Security Issues
|
4
|
+
|
5
|
+
This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.
|
6
|
+
|
7
|
+
To report a security issue, please contact the Stainless team at security@stainless.com.
|
8
|
+
|
9
|
+
## Responsible Disclosure
|
10
|
+
|
11
|
+
We appreciate the efforts of security researchers and individuals who help us maintain the security of
|
12
|
+
SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible
|
13
|
+
disclosure practices by allowing us a reasonable amount of time to investigate and address the issue
|
14
|
+
before making any information public.
|
15
|
+
|
16
|
+
## Reporting Non-SDK Related Security Issues
|
17
|
+
|
18
|
+
If you encounter security issues that are not directly related to SDKs but pertain to the services
|
19
|
+
or products provided by Orb please follow the respective company's security reporting guidelines.
|
20
|
+
|
21
|
+
### Orb Terms and Policies
|
22
|
+
|
23
|
+
Please contact team@withorb.com for any questions or concerns regarding security of our services.
|
24
|
+
|
25
|
+
---
|
26
|
+
|
27
|
+
Thank you for helping us keep the SDKs and systems they interact with secure.
|
data/lib/orb/client.rb
CHANGED
@@ -76,7 +76,8 @@ module Orb
|
|
76
76
|
#
|
77
77
|
# @param api_key [String, nil] Defaults to `ENV["ORB_API_KEY"]`
|
78
78
|
#
|
79
|
-
# @param base_url [String, nil] Override the default base URL for the API, e.g.,
|
79
|
+
# @param base_url [String, nil] Override the default base URL for the API, e.g.,
|
80
|
+
# `"https://api.example.com/v2/"`. Defaults to `ENV["ORB_BASE_URL"]`
|
80
81
|
#
|
81
82
|
# @param max_retries [Integer] Max number of retries to attempt after a failed retryable request.
|
82
83
|
#
|
@@ -89,7 +90,7 @@ module Orb
|
|
89
90
|
# @param idempotency_header [String]
|
90
91
|
def initialize(
|
91
92
|
api_key: ENV["ORB_API_KEY"],
|
92
|
-
base_url:
|
93
|
+
base_url: ENV["ORB_BASE_URL"],
|
93
94
|
max_retries: DEFAULT_MAX_RETRIES,
|
94
95
|
timeout: DEFAULT_TIMEOUT_IN_SECONDS,
|
95
96
|
initial_retry_delay: DEFAULT_INITIAL_RETRY_DELAY,
|
data/lib/orb/internal/page.rb
CHANGED
@@ -22,33 +22,6 @@ module Orb
|
|
22
22
|
# @return [PaginationMetadata]
|
23
23
|
attr_accessor :pagination_metadata
|
24
24
|
|
25
|
-
# @api private
|
26
|
-
#
|
27
|
-
# @param client [Orb::Internal::Transport::BaseClient]
|
28
|
-
# @param req [Hash{Symbol=>Object}]
|
29
|
-
# @param headers [Hash{String=>String}, Net::HTTPHeader]
|
30
|
-
# @param page_data [Hash{Symbol=>Object}]
|
31
|
-
def initialize(client:, req:, headers:, page_data:)
|
32
|
-
super
|
33
|
-
model = req.fetch(:model)
|
34
|
-
|
35
|
-
case page_data
|
36
|
-
in {data: Array | nil => data}
|
37
|
-
@data = data&.map { Orb::Internal::Type::Converter.coerce(model, _1) }
|
38
|
-
else
|
39
|
-
end
|
40
|
-
|
41
|
-
case page_data
|
42
|
-
in {pagination_metadata: Hash | nil => pagination_metadata}
|
43
|
-
@pagination_metadata =
|
44
|
-
Orb::Internal::Type::Converter.coerce(
|
45
|
-
Orb::Internal::Page::PaginationMetadata,
|
46
|
-
pagination_metadata
|
47
|
-
)
|
48
|
-
else
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
25
|
# @return [Boolean]
|
53
26
|
def next_page?
|
54
27
|
!pagination_metadata&.next_cursor.nil?
|
@@ -73,19 +46,48 @@ module Orb
|
|
73
46
|
unless block_given?
|
74
47
|
raise ArgumentError.new("A block must be given to ##{__method__}")
|
75
48
|
end
|
49
|
+
|
76
50
|
page = self
|
77
51
|
loop do
|
78
|
-
page.data&.each
|
52
|
+
page.data&.each(&blk)
|
53
|
+
|
79
54
|
break unless page.next_page?
|
80
55
|
page = page.next_page
|
81
56
|
end
|
82
57
|
end
|
83
58
|
|
59
|
+
# @api private
|
60
|
+
#
|
61
|
+
# @param client [Orb::Internal::Transport::BaseClient]
|
62
|
+
# @param req [Hash{Symbol=>Object}]
|
63
|
+
# @param headers [Hash{String=>String}, Net::HTTPHeader]
|
64
|
+
# @param page_data [Hash{Symbol=>Object}]
|
65
|
+
def initialize(client:, req:, headers:, page_data:)
|
66
|
+
super
|
67
|
+
|
68
|
+
case page_data
|
69
|
+
in {data: Array | nil => data}
|
70
|
+
@data = data&.map { Orb::Internal::Type::Converter.coerce(@model, _1) }
|
71
|
+
else
|
72
|
+
end
|
73
|
+
case page_data
|
74
|
+
in {pagination_metadata: Hash | nil => pagination_metadata}
|
75
|
+
@pagination_metadata =
|
76
|
+
Orb::Internal::Type::Converter.coerce(
|
77
|
+
Orb::Internal::Page::PaginationMetadata,
|
78
|
+
pagination_metadata
|
79
|
+
)
|
80
|
+
else
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# @api private
|
85
|
+
#
|
84
86
|
# @return [String]
|
85
87
|
def inspect
|
86
|
-
|
87
|
-
|
88
|
-
#
|
88
|
+
model = Orb::Internal::Type::Converter.inspect(@model, depth: 1)
|
89
|
+
|
90
|
+
"#<#{self.class}[#{model}]:0x#{object_id.to_s(16)}>"
|
89
91
|
end
|
90
92
|
|
91
93
|
class PaginationMetadata < Orb::Internal::Type::BaseModel
|
@@ -93,7 +93,11 @@ module Orb
|
|
93
93
|
URI.join(url, response_headers["location"])
|
94
94
|
rescue ArgumentError
|
95
95
|
message = "Server responded with status #{status} but no valid location header."
|
96
|
-
raise Orb::Errors::APIConnectionError.new(
|
96
|
+
raise Orb::Errors::APIConnectionError.new(
|
97
|
+
url: url,
|
98
|
+
response: response_headers,
|
99
|
+
message: message
|
100
|
+
)
|
97
101
|
end
|
98
102
|
|
99
103
|
request = {**request, url: location}
|
@@ -101,7 +105,11 @@ module Orb
|
|
101
105
|
case [url.scheme, location.scheme]
|
102
106
|
in ["https", "http"]
|
103
107
|
message = "Tried to redirect to a insecure URL"
|
104
|
-
raise Orb::Errors::APIConnectionError.new(
|
108
|
+
raise Orb::Errors::APIConnectionError.new(
|
109
|
+
url: url,
|
110
|
+
response: response_headers,
|
111
|
+
message: message
|
112
|
+
)
|
105
113
|
else
|
106
114
|
nil
|
107
115
|
end
|
@@ -245,7 +253,7 @@ module Orb
|
|
245
253
|
|
246
254
|
if @idempotency_header &&
|
247
255
|
!headers.key?(@idempotency_header) &&
|
248
|
-
!Net::HTTP::IDEMPOTENT_METHODS_.include?(method.to_s.upcase)
|
256
|
+
(!Net::HTTP::IDEMPOTENT_METHODS_.include?(method.to_s.upcase) || opts.key?(:idempotency_key))
|
249
257
|
headers[@idempotency_header] = opts.fetch(:idempotency_key) { generate_idempotency_key }
|
250
258
|
end
|
251
259
|
|
@@ -350,7 +358,7 @@ module Orb
|
|
350
358
|
self.class.reap_connection!(status, stream: stream)
|
351
359
|
|
352
360
|
message = "Failed to complete the request within #{self.class::MAX_REDIRECTS} redirects."
|
353
|
-
raise Orb::Errors::APIConnectionError.new(url: url, message: message)
|
361
|
+
raise Orb::Errors::APIConnectionError.new(url: url, response: response, message: message)
|
354
362
|
in 300..399
|
355
363
|
self.class.reap_connection!(status, stream: stream)
|
356
364
|
|
@@ -460,6 +468,8 @@ module Orb
|
|
460
468
|
end
|
461
469
|
end
|
462
470
|
|
471
|
+
# @api private
|
472
|
+
#
|
463
473
|
# @return [String]
|
464
474
|
def inspect
|
465
475
|
# rubocop:disable Layout/LineLength
|
@@ -13,6 +13,10 @@ module Orb
|
|
13
13
|
class ArrayOf
|
14
14
|
include Orb::Internal::Type::Converter
|
15
15
|
|
16
|
+
private_class_method :new
|
17
|
+
|
18
|
+
# @overload [](type_info, spec = {})
|
19
|
+
#
|
16
20
|
# @param type_info [Hash{Symbol=>Object}, Proc, Orb::Internal::Type::Converter, Class]
|
17
21
|
#
|
18
22
|
# @param spec [Hash{Symbol=>Object}] .
|
@@ -24,7 +28,7 @@ module Orb
|
|
24
28
|
# @option spec [Proc] :union
|
25
29
|
#
|
26
30
|
# @option spec [Boolean] :"nil?"
|
27
|
-
def self.[](
|
31
|
+
def self.[](...) = new(...)
|
28
32
|
|
29
33
|
# @param other [Object]
|
30
34
|
#
|
@@ -120,7 +124,18 @@ module Orb
|
|
120
124
|
# @option spec [Boolean] :"nil?"
|
121
125
|
def initialize(type_info, spec = {})
|
122
126
|
@item_type_fn = Orb::Internal::Type::Converter.type_info(type_info || spec)
|
123
|
-
@nilable = spec
|
127
|
+
@nilable = spec.fetch(:nil?, false)
|
128
|
+
end
|
129
|
+
|
130
|
+
# @api private
|
131
|
+
#
|
132
|
+
# @param depth [Integer]
|
133
|
+
#
|
134
|
+
# @return [String]
|
135
|
+
def inspect(depth: 0)
|
136
|
+
items = Orb::Internal::Type::Converter.inspect(item_type, depth: depth.succ)
|
137
|
+
|
138
|
+
"#{self.class}[#{[items, nilable? ? 'nil' : nil].compact.join(' | ')}]"
|
124
139
|
end
|
125
140
|
end
|
126
141
|
end
|
@@ -63,7 +63,7 @@ module Orb
|
|
63
63
|
|
64
64
|
setter = "#{name_sym}="
|
65
65
|
api_name = info.fetch(:api_name, name_sym)
|
66
|
-
nilable = info
|
66
|
+
nilable = info.fetch(:nil?, false)
|
67
67
|
const = required && !nilable ? info.fetch(:const, Orb::Internal::OMIT) : Orb::Internal::OMIT
|
68
68
|
|
69
69
|
[name_sym, setter].each { undef_method(_1) } if known_fields.key?(name_sym)
|
@@ -338,6 +338,27 @@ module Orb
|
|
338
338
|
.to_h
|
339
339
|
end
|
340
340
|
|
341
|
+
class << self
|
342
|
+
# @param model [Orb::Internal::Type::BaseModel]
|
343
|
+
#
|
344
|
+
# @return [Hash{Symbol=>Object}]
|
345
|
+
def walk(model)
|
346
|
+
walk = ->(x) do
|
347
|
+
case x
|
348
|
+
in Orb::Internal::Type::BaseModel
|
349
|
+
walk.call(x.to_h)
|
350
|
+
in Hash
|
351
|
+
x.transform_values(&walk)
|
352
|
+
in Array
|
353
|
+
x.map(&walk)
|
354
|
+
else
|
355
|
+
x
|
356
|
+
end
|
357
|
+
end
|
358
|
+
walk.call(model)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
341
362
|
# @param a [Object]
|
342
363
|
#
|
343
364
|
# @return [String]
|
@@ -361,15 +382,38 @@ module Orb
|
|
361
382
|
end
|
362
383
|
end
|
363
384
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
385
|
+
class << self
|
386
|
+
# @api private
|
387
|
+
#
|
388
|
+
# @param depth [Integer]
|
389
|
+
#
|
390
|
+
# @return [String]
|
391
|
+
def inspect(depth: 0)
|
392
|
+
return super() if depth.positive?
|
393
|
+
|
394
|
+
depth = depth.succ
|
395
|
+
deferred = fields.transform_values do |field|
|
396
|
+
type, required, nilable = field.fetch_values(:type, :required, :nilable)
|
397
|
+
inspected = [
|
398
|
+
Orb::Internal::Type::Converter.inspect(type, depth: depth),
|
399
|
+
!required || nilable ? "nil" : nil
|
400
|
+
].compact.join(" | ")
|
401
|
+
-> { inspected }.tap { _1.define_singleton_method(:inspect) { call } }
|
402
|
+
end
|
403
|
+
|
404
|
+
"#{name}[#{deferred.inspect}]"
|
370
405
|
end
|
371
|
-
"#<#{self.class.name}:0x#{object_id.to_s(16)} #{rows.join(' ')}>"
|
372
406
|
end
|
407
|
+
|
408
|
+
# @api private
|
409
|
+
#
|
410
|
+
# @return [String]
|
411
|
+
def to_s = self.class.walk(@data).to_s
|
412
|
+
|
413
|
+
# @api private
|
414
|
+
#
|
415
|
+
# @return [String]
|
416
|
+
def inspect = "#<#{self.class}:0x#{object_id.to_s(16)} #{self}>"
|
373
417
|
end
|
374
418
|
end
|
375
419
|
end
|
@@ -49,6 +49,15 @@ module Orb
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
# @api private
|
53
|
+
#
|
54
|
+
# @param depth [Integer]
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
def inspect(depth: 0)
|
58
|
+
super()
|
59
|
+
end
|
60
|
+
|
52
61
|
# rubocop:enable Lint/UnusedMethodArgument
|
53
62
|
|
54
63
|
class << self
|
@@ -240,6 +249,21 @@ module Orb
|
|
240
249
|
Orb::Internal::Type::Unknown.dump(value, state: state)
|
241
250
|
end
|
242
251
|
end
|
252
|
+
|
253
|
+
# @api private
|
254
|
+
#
|
255
|
+
# @param target [Object]
|
256
|
+
# @param depth [Integer]
|
257
|
+
#
|
258
|
+
# @return [String]
|
259
|
+
def inspect(target, depth:)
|
260
|
+
case target
|
261
|
+
in Orb::Internal::Type::Converter
|
262
|
+
target.inspect(depth: depth.succ)
|
263
|
+
else
|
264
|
+
target.inspect
|
265
|
+
end
|
266
|
+
end
|
243
267
|
end
|
244
268
|
end
|
245
269
|
end
|
@@ -58,9 +58,9 @@ module Orb
|
|
58
58
|
#
|
59
59
|
# @return [Boolean]
|
60
60
|
def ==(other)
|
61
|
-
# rubocop:disable
|
62
|
-
|
63
|
-
# rubocop:enable
|
61
|
+
# rubocop:disable Style/CaseEquality
|
62
|
+
Orb::Internal::Type::Enum === other && other.values.to_set == values.to_set
|
63
|
+
# rubocop:enable Style/CaseEquality
|
64
64
|
end
|
65
65
|
|
66
66
|
# @api private
|
@@ -103,6 +103,22 @@ module Orb
|
|
103
103
|
# #
|
104
104
|
# # @return [Symbol, Object]
|
105
105
|
# def dump(value, state:) = super
|
106
|
+
|
107
|
+
# @api private
|
108
|
+
#
|
109
|
+
# @param depth [Integer]
|
110
|
+
#
|
111
|
+
# @return [String]
|
112
|
+
def inspect(depth: 0)
|
113
|
+
if depth.positive?
|
114
|
+
return is_a?(Module) ? super() : self.class.name
|
115
|
+
end
|
116
|
+
|
117
|
+
members = values.map { Orb::Internal::Type::Converter.inspect(_1, depth: depth.succ) }
|
118
|
+
prefix = is_a?(Module) ? name : self.class.name
|
119
|
+
|
120
|
+
"#{prefix}[#{members.join(' | ')}]"
|
121
|
+
end
|
106
122
|
end
|
107
123
|
end
|
108
124
|
end
|
@@ -13,6 +13,10 @@ module Orb
|
|
13
13
|
class HashOf
|
14
14
|
include Orb::Internal::Type::Converter
|
15
15
|
|
16
|
+
private_class_method :new
|
17
|
+
|
18
|
+
# @overload [](type_info, spec = {})
|
19
|
+
#
|
16
20
|
# @param type_info [Hash{Symbol=>Object}, Proc, Orb::Internal::Type::Converter, Class]
|
17
21
|
#
|
18
22
|
# @param spec [Hash{Symbol=>Object}] .
|
@@ -24,7 +28,7 @@ module Orb
|
|
24
28
|
# @option spec [Proc] :union
|
25
29
|
#
|
26
30
|
# @option spec [Boolean] :"nil?"
|
27
|
-
def self.[](
|
31
|
+
def self.[](...) = new(...)
|
28
32
|
|
29
33
|
# @param other [Object]
|
30
34
|
#
|
@@ -140,7 +144,18 @@ module Orb
|
|
140
144
|
# @option spec [Boolean] :"nil?"
|
141
145
|
def initialize(type_info, spec = {})
|
142
146
|
@item_type_fn = Orb::Internal::Type::Converter.type_info(type_info || spec)
|
143
|
-
@nilable = spec
|
147
|
+
@nilable = spec.fetch(:nil?, false)
|
148
|
+
end
|
149
|
+
|
150
|
+
# @api private
|
151
|
+
#
|
152
|
+
# @param depth [Integer]
|
153
|
+
#
|
154
|
+
# @return [String]
|
155
|
+
def inspect(depth: 0)
|
156
|
+
items = Orb::Internal::Type::Converter.inspect(item_type, depth: depth.succ)
|
157
|
+
|
158
|
+
"#{self.class}[#{[items, nilable? ? 'nil' : nil].compact.join(' | ')}]"
|
144
159
|
end
|
145
160
|
end
|
146
161
|
end
|
@@ -140,9 +140,7 @@ module Orb
|
|
140
140
|
#
|
141
141
|
# @return [Boolean]
|
142
142
|
def ==(other)
|
143
|
-
|
144
|
-
other.is_a?(Module) && other.singleton_class <= Orb::Internal::Type::Union && other.derefed_variants == derefed_variants
|
145
|
-
# rubocop:enable Layout/LineLength
|
143
|
+
Orb::Internal::Type::Union === other && other.derefed_variants == derefed_variants
|
146
144
|
end
|
147
145
|
|
148
146
|
# @api private
|
@@ -225,6 +223,22 @@ module Orb
|
|
225
223
|
|
226
224
|
# rubocop:enable Style/CaseEquality
|
227
225
|
# rubocop:enable Style/HashEachMethods
|
226
|
+
|
227
|
+
# @api private
|
228
|
+
#
|
229
|
+
# @param depth [Integer]
|
230
|
+
#
|
231
|
+
# @return [String]
|
232
|
+
def inspect(depth: 0)
|
233
|
+
if depth.positive?
|
234
|
+
return is_a?(Module) ? super() : self.class.name
|
235
|
+
end
|
236
|
+
|
237
|
+
members = variants.map { Orb::Internal::Type::Converter.inspect(_1, depth: depth.succ) }
|
238
|
+
prefix = is_a?(Module) ? name : self.class.name
|
239
|
+
|
240
|
+
"#{prefix}[#{members.join(' | ')}]"
|
241
|
+
end
|
228
242
|
end
|
229
243
|
end
|
230
244
|
end
|
data/lib/orb/internal/util.rb
CHANGED
@@ -448,7 +448,7 @@ module Orb
|
|
448
448
|
else
|
449
449
|
src
|
450
450
|
end
|
451
|
-
@buf = String.new
|
451
|
+
@buf = String.new
|
452
452
|
@blk = blk
|
453
453
|
end
|
454
454
|
end
|
@@ -460,7 +460,7 @@ module Orb
|
|
460
460
|
# @return [Enumerable<String>]
|
461
461
|
def writable_enum(&blk)
|
462
462
|
Enumerator.new do |y|
|
463
|
-
buf = String.new
|
463
|
+
buf = String.new
|
464
464
|
y.define_singleton_method(:write) do
|
465
465
|
self << buf.replace(_1)
|
466
466
|
buf.bytesize
|
@@ -582,6 +582,27 @@ module Orb
|
|
582
582
|
|
583
583
|
# @api private
|
584
584
|
#
|
585
|
+
# https://www.iana.org/assignments/character-sets/character-sets.xhtml
|
586
|
+
#
|
587
|
+
# @param content_type [String]
|
588
|
+
# @param text [String]
|
589
|
+
def force_charset!(content_type, text:)
|
590
|
+
charset = /charset=([^;\s]+)/.match(content_type)&.captures&.first
|
591
|
+
|
592
|
+
return unless charset
|
593
|
+
|
594
|
+
begin
|
595
|
+
encoding = Encoding.find(charset)
|
596
|
+
text.force_encoding(encoding)
|
597
|
+
rescue ArgumentError
|
598
|
+
nil
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
# @api private
|
603
|
+
#
|
604
|
+
# Assumes each chunk in stream has `Encoding::BINARY`.
|
605
|
+
#
|
585
606
|
# @param headers [Hash{String=>String}, Net::HTTPHeader]
|
586
607
|
# @param stream [Enumerable<String>]
|
587
608
|
# @param suppress_error [Boolean]
|
@@ -589,7 +610,7 @@ module Orb
|
|
589
610
|
# @raise [JSON::ParserError]
|
590
611
|
# @return [Object]
|
591
612
|
def decode_content(headers, stream:, suppress_error: false)
|
592
|
-
case headers["content-type"]
|
613
|
+
case (content_type = headers["content-type"])
|
593
614
|
in %r{^application/(?:vnd\.api\+)?json}
|
594
615
|
json = stream.to_a.join
|
595
616
|
begin
|
@@ -606,11 +627,10 @@ module Orb
|
|
606
627
|
in %r{^text/event-stream}
|
607
628
|
lines = decode_lines(stream)
|
608
629
|
decode_sse(lines)
|
609
|
-
in %r{^text/}
|
610
|
-
stream.to_a.join
|
611
630
|
else
|
612
|
-
|
613
|
-
|
631
|
+
text = stream.to_a.join
|
632
|
+
force_charset!(content_type, text: text)
|
633
|
+
StringIO.new(text)
|
614
634
|
end
|
615
635
|
end
|
616
636
|
end
|
@@ -675,12 +695,17 @@ module Orb
|
|
675
695
|
class << self
|
676
696
|
# @api private
|
677
697
|
#
|
698
|
+
# Assumes Strings have been forced into having `Encoding::BINARY`.
|
699
|
+
#
|
700
|
+
# This decoder is responsible for reassembling lines split across multiple
|
701
|
+
# fragments.
|
702
|
+
#
|
678
703
|
# @param enum [Enumerable<String>]
|
679
704
|
#
|
680
705
|
# @return [Enumerable<String>]
|
681
706
|
def decode_lines(enum)
|
682
707
|
re = /(\r\n|\r|\n)/
|
683
|
-
buffer = String.new
|
708
|
+
buffer = String.new
|
684
709
|
cr_seen = nil
|
685
710
|
|
686
711
|
chain_fused(enum) do |y|
|
@@ -711,6 +736,8 @@ module Orb
|
|
711
736
|
#
|
712
737
|
# https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream
|
713
738
|
#
|
739
|
+
# Assumes that `lines` has been decoded with `#decode_lines`.
|
740
|
+
#
|
714
741
|
# @param lines [Enumerable<String>]
|
715
742
|
#
|
716
743
|
# @return [Enumerable<Hash{Symbol=>Object}>]
|
@@ -734,7 +761,7 @@ module Orb
|
|
734
761
|
in "event"
|
735
762
|
current.merge!(event: value)
|
736
763
|
in "data"
|
737
|
-
(current[:data] ||= String.new
|
764
|
+
(current[:data] ||= String.new) << (value << "\n")
|
738
765
|
in "id" unless value.include?("\0")
|
739
766
|
current.merge!(id: value)
|
740
767
|
in "retry" if /^\d+$/ =~ value
|