Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.1.0-alpha.8"
".": "0.1.0-alpha.9"
}
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

## 0.1.0-alpha.9 (2025-02-18)

Full Changelog: [v0.1.0-alpha.8...v0.1.0-alpha.9](https://github.com/orbcorp/orb-ruby/compare/v0.1.0-alpha.8...v0.1.0-alpha.9)

### Features

* support overlapping HTTP requests in same Fiber ([#43](https://github.com/orbcorp/orb-ruby/issues/43)) ([35549e0](https://github.com/orbcorp/orb-ruby/commit/35549e0240188802f66cab3d0c45573631f8baa7))


### Bug Fixes

* ssl timeout not required when TCP socket open timeout specified ([#44](https://github.com/orbcorp/orb-ruby/issues/44)) ([2734175](https://github.com/orbcorp/orb-ruby/commit/2734175c6c31340d8f6d2d32b41be08dd30249a4))


### Chores

* enable full pagination tests ([#41](https://github.com/orbcorp/orb-ruby/issues/41)) ([e70e98b](https://github.com/orbcorp/orb-ruby/commit/e70e98b41b346edcee0076741f11f8b8a186c33e))
* **internal:** codegen related update ([#42](https://github.com/orbcorp/orb-ruby/issues/42)) ([f34e03e](https://github.com/orbcorp/orb-ruby/commit/f34e03eb511117f21282716401ba8c129d0b15e4))
* **internal:** version bump ([#39](https://github.com/orbcorp/orb-ruby/issues/39)) ([21d40f8](https://github.com/orbcorp/orb-ruby/commit/21d40f8d2177b1ab3e5df755911e39576e4ef11d))

## 0.1.0-alpha.8 (2025-02-15)

Full Changelog: [v0.1.0-alpha.7...v0.1.0-alpha.8](https://github.com/orbcorp/orb-ruby/compare/v0.1.0-alpha.7...v0.1.0-alpha.8)
Expand Down
16 changes: 8 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ GIT
PATH
remote: .
specs:
orb (0.1.0.pre.alpha.8)
orb (0.1.0.pre.alpha.9)
connection_pool

GEM
Expand Down Expand Up @@ -87,13 +87,13 @@ GEM
parser (>= 3.3.1.0)
ruby-progressbar (1.13.0)
securerandom (0.4.1)
sorbet (0.5.11829)
sorbet-static (= 0.5.11829)
sorbet-runtime (0.5.11829)
sorbet-static (0.5.11829-x86_64-linux)
sorbet-static-and-runtime (0.5.11829)
sorbet (= 0.5.11829)
sorbet-runtime (= 0.5.11829)
sorbet (0.5.11834)
sorbet-static (= 0.5.11834)
sorbet-runtime (0.5.11834)
sorbet-static (0.5.11834-x86_64-linux)
sorbet-static-and-runtime (0.5.11834)
sorbet (= 0.5.11834)
sorbet-runtime (= 0.5.11834)
spoom (1.1.16)
sorbet (>= 0.5.10187)
sorbet-runtime (>= 0.5.9204)
Expand Down
18 changes: 11 additions & 7 deletions lib/orb/base_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,16 @@

path = Orb::Util.interpolate_path(uninterpolated_path)

query = Orb::Util.deep_merge(
req[:query].to_h,
opts[:extra_query].to_h
)

headers = Orb::Util.normalized_headers(
@headers,
auth_headers,
*[req[:headers], opts[:extra_headers]].compact
req[:headers].to_h,
opts[:extra_headers].to_h
)

if @idempotency_header &&
Expand Down Expand Up @@ -157,7 +163,7 @@
Orb::Util.deep_merge(*[req[:body], opts[:extra_body]].compact)
end

url = Orb::Util.join_parsed_uri(@base_url, {**req, path: path})
url = Orb::Util.join_parsed_uri(@base_url, {**req, path: path, query: query})
headers, encoded = Orb::Util.encode_content(headers, body)
max_retries = opts.fetch(:max_retries, @max_retries)
{method: method, url: url, headers: headers, body: encoded, max_retries: max_retries, timeout: timeout}
Expand Down Expand Up @@ -387,12 +393,10 @@
parsed = Orb::Util.decode_content(response)
unwrapped = Orb::Util.dig(parsed, req[:unwrap])

page = req[:page]
model = req.fetch(:model, Orb::Unknown)
case [page, model]
in [Class, Class | Orb::Converter | nil]
case [req[:page], req.fetch(:model, Orb::Unknown)]
in [Class => page, _]
page.new(client: self, req: req, headers: response, unwrapped: unwrapped)
in [nil, Class | Orb::Converter]
in [nil, Class | Orb::Converter => model]
Orb::Converter.coerce(model, unwrapped)
in [nil, nil]
unwrapped
Expand Down Expand Up @@ -446,7 +450,7 @@
#
def inspect
base_url = Orb::Util.unparse_uri(@base_url)
"#<#{self.class.name}:0x#{object_id.to_s(16)} base_url=#{base_url} max_retries=#{@max_retries} timeout=#{@timeout}>"

Check warning on line 453 in lib/orb/base_client.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/LineLength: Line is too long. [122/110]
end
end
end
2 changes: 2 additions & 0 deletions lib/orb/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class Client < Orb::BaseClient
# @return [Hash{String=>String}]
#
private def auth_headers
return {} if @api_key.nil?

{"Authorization" => "Bearer #{@api_key}"}
end

Expand Down
107 changes: 69 additions & 38 deletions lib/orb/pooled_net_requester.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,58 @@ def initialize
# @private
#
# @param url [URL::Generic]
# @param timeout [Float]
# @param blk [Proc]
#
# @return [ConnectionPool]
#
private def get_pool(url)
private def with_pool(url, &blk)
origin = Orb::Util.uri_origin(url)
@mutex.synchronize do
@pools[origin] ||= ConnectionPool.new(size: Etc.nprocessors) do
port =
case [url.port, url.scheme]
in [Integer, _]
url.port
in [nil, "http" | "ws"]
Net::HTTP.http_default_port
in [nil, "https" | "wss"]
Net::HTTP.https_default_port
end
key = :"#{self.class.name}-connection_in_use_for_#{origin}"

return blk.call(make_conn(url)) if Thread.current[key]

pool =
@mutex.synchronize do
@pools[origin] ||= ConnectionPool.new(size: Etc.nprocessors) do
make_conn(url)
end
end

pool.with do |conn|
Thread.current[key] = true

blk.call(conn)
# rubocop:disable Lint/RescueException
rescue Exception => e
# rubocop:enable Lint/RescueException
# should close connection on all errors to ensure no invalid state persists
conn.finish if conn.started?
raise e
ensure
Thread.current[key] = nil
end
end

session = Net::HTTP.new(url.host, port)
session.use_ssl = %w[https wss].include?(url.scheme)
session.max_retries = 0
session
# @private
#
# @param url [URI::Generic]
#
# @return [Net::HTTP]
#
private def make_conn(url)
port =
case [url.port, url.scheme]
in [Integer, _]
url.port
in [nil, "http" | "ws"]
Net::HTTP.http_default_port
in [nil, "https" | "wss"]
Net::HTTP.https_default_port
end

Net::HTTP.new(url.host, port).tap do
_1.use_ssl = %w[https wss].include?(url.scheme)
_1.max_retries = 0
end
end

Expand All @@ -44,7 +73,7 @@ def initialize
# @option req [Symbol] :method
# @option req [URI::Generic] :url
# @option req [Hash{String => String}] :headers
# @option req [String, Hash] :body
# @option req [String, Hash, IO, StringIO] :body
# @option req [Float] :timeout
#
# @return [Net::HTTPResponse]
Expand All @@ -55,7 +84,7 @@ def execute(req)
request = Net::HTTPGenericRequest.new(
method.to_s.upcase,
!body.nil?,
![:head, :options].include?(method),
method != :head,
url.to_s
)

Expand All @@ -67,28 +96,30 @@ def execute(req)
request.body_stream = body
end

# This timeout is for acquiring a connection from the pool
# The default 5 seconds seems too short, lets just have a nearly unbounded queue for now
#
# TODO: revisit this around granular timeout / concurrency control
get_pool(url).with(timeout: 600) do |conn|
with_pool(url) do |conn|
make_request(conn, request, timeout)
end
end

# @private
#
# @param conn [Net::HTTP]
# @param request [Net::HTTPGenericRequest]
# @param timeout [Float]
#
# @return [Net::HTTPResponse]
#
private def make_request(conn, request, timeout)
unless conn.started?
conn.open_timeout = timeout
conn.read_timeout = timeout
conn.write_timeout = timeout
conn.continue_timeout = timeout
conn.start
end

conn.start unless conn.started?
conn.read_timeout = timeout
conn.write_timeout = timeout
conn.continue_timeout = timeout

conn.request(request)
# rubocop:disable Lint/RescueException
rescue Exception => e
# rubocop:enable Lint/RescueException
# should close connection on all errors to ensure no invalid state persists
conn.finish if conn.started?
raise e
end
rescue ConnectionPool::TimeoutError
raise Orb::APITimeoutError.new(url: url)
conn.request(request)
end
end
end
8 changes: 3 additions & 5 deletions lib/orb/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,6 @@ def self.unparse_uri(parsed)
#
# @option rhs [Hash{String=>Array<String>}] :query
#
# @option rhs [Hash{String=>Array<String>}] :extra_query
#
# @return [URI::Generic]
#
def self.join_parsed_uri(lhs, rhs)
Expand All @@ -307,7 +305,7 @@ def self.join_parsed_uri(lhs, rhs)
query = deep_merge(
joined.path == base_path ? base_query : {},
parsed_query,
*rhs.values_at(:query, :extra_query).compact,
rhs[:query].to_h,
concat: true
)

Expand All @@ -327,12 +325,12 @@ def self.decode_query(query)

# @private
#
# @param query [Hash{String=>Array<String>, String, nil}]
# @param query [Hash{String=>Array<String>, String, nil}, nil]
#
# @return [String, nil]
#
def self.encode_query(query)
query.empty? ? nil : URI.encode_www_form(query)
query.to_h.empty? ? nil : URI.encode_www_form(query)
end

# @private
Expand Down
2 changes: 1 addition & 1 deletion lib/orb/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Orb
VERSION = "0.1.0-alpha.8"
VERSION = "0.1.0-alpha.9"
end
2 changes: 1 addition & 1 deletion rbi/lib/orb/client.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ module Orb
def dimensional_price_groups
end

sig { returns(T::Hash[String, String]) }
sig { override.returns(T::Hash[String, String]) }
private def auth_headers
end

Expand Down
7 changes: 6 additions & 1 deletion rbi/lib/orb/util.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,12 @@ module Orb
end

sig do
params(query: T::Hash[String, T.nilable(T.any(T::Array[String], String))]).returns(T.nilable(String))
params(
query: T.nilable(
T::Hash[String,
T.nilable(T.any(T::Array[String], String))]
)
).returns(T.nilable(String))
end
def self.encode_query(query)
end
Expand Down
2 changes: 1 addition & 1 deletion rbi/lib/orb/version.rbi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# typed: strong

module Orb
VERSION = "0.1.0-alpha.8"
VERSION = "0.1.0-alpha.9"
end
2 changes: 1 addition & 1 deletion sig/orb/util.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ module Orb
def self?.decode_query: (String? query) -> ::Hash[String, ::Array[String]]

def self?.encode_query: (
::Hash[String, (::Array[String] | String)?] query
::Hash[String, (::Array[String] | String)?]? query
) -> String?

def self?.normalized_headers: (
Expand Down
2 changes: 1 addition & 1 deletion sig/orb/version.rbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Orb
VERSION: "0.1.0-alpha.7"
VERSION: "0.1.0-alpha.8"
end
7 changes: 2 additions & 5 deletions test/orb/util_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,12 @@ def test_joining_uris
Orb::Util.parse_uri("h://a.b/c?d=e")
],
[
"h://a.b/c?d=e&f=g",
"h://a.b/c?d=e",
"h://nope",
{
host: "a.b",
path: "/c",
query: {"d" => ["e"]},
extra_query: {
"f" => ["g"]
}
query: {"d" => ["e"]}
}
]
]
Expand Down
Loading