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.44"
".": "0.1.0-alpha.45"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 169
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-1d44bb7fad99487af1161eb24dfd5369440eda7e80ed237cbc1acc6802a7d212.yml
openapi_spec_hash: 1b6b6215b60094b76b91c56b925a251a
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-fbbce0ea11d3e86e532f705804f3d79e8eecfb8a7796e3a6ad3c50cccee14bb1.yml
openapi_spec_hash: 3f44b97866ca74effe5e70fc9c64effb
config_hash: 768e8f0faa1a21e26b07e6cdc395cebf
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Changelog

## 0.1.0-alpha.45 (2025-09-26)

Full Changelog: [v0.1.0-alpha.44...v0.1.0-alpha.45](https://github.com/lithic-com/lithic-ruby/compare/v0.1.0-alpha.44...v0.1.0-alpha.45)

### Features

* expose response headers for both streams and errors ([a0a6aba](https://github.com/lithic-com/lithic-ruby/commit/a0a6aba5882df60a0019557a7e7d38aba3fb7176))


### Bug Fixes

* **internal:** use null byte as file separator in the fast formatting script ([7260d7a](https://github.com/lithic-com/lithic-ruby/commit/7260d7a16edee1733a39556e554ed6f1901595de))


### Performance Improvements

* faster code formatting ([0367f27](https://github.com/lithic-com/lithic-ruby/commit/0367f2706e7903d3552ee18b7a75f62ece9fc927))


### Chores

* do not install brew dependencies in ./scripts/bootstrap by default ([23ebf5c](https://github.com/lithic-com/lithic-ruby/commit/23ebf5c02a0c168955ed2ebf1e7ef38d2d4eeda4))
* **docs:** small updates to doc strings on a few endpoints ([b7c4832](https://github.com/lithic-com/lithic-ruby/commit/b7c4832186d3ecddb47728496bf966e00e3c15eb))
* improve example values ([5c934d6](https://github.com/lithic-com/lithic-ruby/commit/5c934d6d52eccc89a72dfe7d5a850280b44ded75))

## 0.1.0-alpha.44 (2025-09-11)

Full Changelog: [v0.1.0-alpha.43...v0.1.0-alpha.44](https://github.com/lithic-com/lithic-ruby/compare/v0.1.0-alpha.43...v0.1.0-alpha.44)
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ GIT
PATH
remote: .
specs:
lithic (0.1.0.pre.alpha.44)
lithic (0.1.0.pre.alpha.45)
connection_pool

GEM
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ To use this gem, install via Bundler by adding the following to your application
<!-- x-release-please-start-version -->

```ruby
gem "lithic", "~> 0.1.0.pre.alpha.44"
gem "lithic", "~> 0.1.0.pre.alpha.45"
```

<!-- x-release-please-end -->
Expand Down
18 changes: 10 additions & 8 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ tapioca = "sorbet/tapioca"
examples = "examples"
ignore_file = ".ignore"

FILES_ENV = "FORMAT_FILE"

CLEAN.push(*%w[.idea/ .ruby-lsp/ .yardoc/ doc/], *FileList["*.gem"], ignore_file)

CLOBBER.push(*%w[sorbet/rbi/annotations/ sorbet/rbi/gems/], tapioca)
Expand Down Expand Up @@ -55,21 +57,21 @@ end
desc("Format `*.rb`")
multitask(:"format:rb") do
# while `syntax_tree` is much faster than `rubocop`, `rubocop` is the only formatter with full syntax support
find = %w[find ./lib ./test ./examples -type f -and -name *.rb -print0]
files = ENV.key?(FILES_ENV) ? %w[sed -E -z -n -e /\.rb$/p --] << ENV.fetch(FILES_ENV) : %w[find ./lib ./test ./examples -type f -and -name *.rb -print0]
fmt = xargs + %w[rubocop --fail-level F --autocorrect --format simple --]
sh("#{find.shelljoin} | #{fmt.shelljoin}")
sh("#{files.shelljoin} | #{fmt.shelljoin}")
end

desc("Format `*.rbi`")
multitask(:"format:rbi") do
find = %w[find ./rbi -type f -and -name *.rbi -print0]
files = ENV.key?(FILES_ENV) ? %w[sed -E -z -n -e /\.rbi$/p --] << ENV.fetch(FILES_ENV) : %w[find ./rbi -type f -and -name *.rbi -print0]
fmt = xargs + %w[stree write --]
sh(ruby_opt, "#{find.shelljoin} | #{fmt.shelljoin}")
sh(ruby_opt, "#{files.shelljoin} | #{fmt.shelljoin}")
end

desc("Format `*.rbs`")
multitask(:"format:rbs") do
find = %w[find ./sig -type f -name *.rbs -print0]
files = ENV.key?(FILES_ENV) ? %w[sed -E -z -n -e /\.rbs$/p --] << ENV.fetch(FILES_ENV) : %w[find ./sig -type f -name *.rbs -print0]
inplace = /darwin|bsd/ =~ RUBY_PLATFORM ? ["-i", ""] : %w[-i]
uuid = SecureRandom.uuid

Expand Down Expand Up @@ -98,13 +100,13 @@ multitask(:"format:rbs") do
success = false

# transform class aliases to type aliases, which syntax tree has no trouble with
sh("#{find.shelljoin} | #{pre.shelljoin}")
sh("#{files.shelljoin} | #{pre.shelljoin}")
# run syntax tree to format `*.rbs` files
sh(ruby_opt, "#{find.shelljoin} | #{fmt.shelljoin}") do
sh(ruby_opt, "#{files.shelljoin} | #{fmt.shelljoin}") do
success = _1
end
# transform type aliases back to class aliases
sh("#{find.shelljoin} | #{pst.shelljoin}")
sh("#{files.shelljoin} | #{pst.shelljoin}")

# always run post-processing to remove comment marker
fail unless success
Expand Down
36 changes: 25 additions & 11 deletions lib/lithic/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,25 @@ class APIError < Lithic::Errors::Error
# @return [Integer, nil]
attr_accessor :status

# @return [Hash{String=>String}, nil]
attr_accessor :headers

# @return [Object, nil]
attr_accessor :body

# @api private
#
# @param url [URI::Generic]
# @param status [Integer, nil]
# @param headers [Hash{String=>String}, nil]
# @param body [Object, nil]
# @param request [nil]
# @param response [nil]
# @param message [String, nil]
def initialize(url:, status: nil, body: nil, request: nil, response: nil, message: nil)
def initialize(url:, status: nil, headers: nil, body: nil, request: nil, response: nil, message: nil)
@url = url
@status = status
@headers = headers
@body = body
@request = request
@response = response
Expand All @@ -74,13 +79,15 @@ class APIConnectionError < Lithic::Errors::APIError
#
# @param url [URI::Generic]
# @param status [nil]
# @param headers [Hash{String=>String}, nil]
# @param body [nil]
# @param request [nil]
# @param response [nil]
# @param message [String, nil]
def initialize(
url:,
status: nil,
headers: nil,
body: nil,
request: nil,
response: nil,
Expand All @@ -95,13 +102,15 @@ class APITimeoutError < Lithic::Errors::APIConnectionError
#
# @param url [URI::Generic]
# @param status [nil]
# @param headers [Hash{String=>String}, nil]
# @param body [nil]
# @param request [nil]
# @param response [nil]
# @param message [String, nil]
def initialize(
url:,
status: nil,
headers: nil,
body: nil,
request: nil,
response: nil,
Expand All @@ -116,21 +125,24 @@ class APIStatusError < Lithic::Errors::APIError
#
# @param url [URI::Generic]
# @param status [Integer]
# @param headers [Hash{String=>String}, nil]
# @param body [Object, nil]
# @param request [nil]
# @param response [nil]
# @param message [String, nil]
#
# @return [self]
def self.for(url:, status:, body:, request:, response:, message: nil)
kwargs = {
url: url,
status: status,
body: body,
request: request,
response: response,
message: message
}
def self.for(url:, status:, headers:, body:, request:, response:, message: nil)
kwargs =
{
url: url,
status: status,
headers: headers,
body: body,
request: request,
response: response,
message: message
}

case status
in 400
Expand Down Expand Up @@ -162,15 +174,17 @@ def self.for(url:, status:, body:, request:, response:, message: nil)
#
# @param url [URI::Generic]
# @param status [Integer]
# @param headers [Hash{String=>String}, nil]
# @param body [Object, nil]
# @param request [nil]
# @param response [nil]
# @param message [String, nil]
def initialize(url:, status:, body:, request:, response:, message: nil)
def initialize(url:, status:, headers:, body:, request:, response:, message: nil)
message ||= {url: url.to_s, status: status, body: body}
super(
url: url,
status: status,
headers: headers,
body: body,
request: request,
response: response,
Expand Down
2 changes: 1 addition & 1 deletion lib/lithic/internal/cursor_page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def auto_paging_each(&blk)
#
# @param client [Lithic::Internal::Transport::BaseClient]
# @param req [Hash{Symbol=>Object}]
# @param headers [Hash{String=>String}, Net::HTTPHeader]
# @param headers [Hash{String=>String}]
# @param page_data [Hash{Symbol=>Object}]
def initialize(client:, req:, headers:, page_data:)
super
Expand Down
2 changes: 1 addition & 1 deletion lib/lithic/internal/single_page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def auto_paging_each(&blk)
#
# @param client [Lithic::Internal::Transport::BaseClient]
# @param req [Hash{Symbol=>Object}]
# @param headers [Hash{String=>String}, Net::HTTPHeader]
# @param headers [Hash{String=>String}]
# @param page_data [Array<Object>]
def initialize(client:, req:, headers:, page_data:)
super
Expand Down
18 changes: 11 additions & 7 deletions lib/lithic/internal/transport/base_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def validate!(req)
# @api private
#
# @param status [Integer]
# @param headers [Hash{String=>String}, Net::HTTPHeader]
# @param headers [Hash{String=>String}]
#
# @return [Boolean]
def should_retry?(status, headers:)
Expand Down Expand Up @@ -85,7 +85,7 @@ def should_retry?(status, headers:)
#
# @param status [Integer]
#
# @param response_headers [Hash{String=>String}, Net::HTTPHeader]
# @param response_headers [Hash{String=>String}]
#
# @return [Hash{Symbol=>Object}]
def follow_redirect(request, status:, response_headers:)
Expand Down Expand Up @@ -378,6 +378,7 @@ def send_request(request, redirect_count:, retry_count:, send_retry_header:)
rescue Lithic::Errors::APIConnectionError => e
status = e
end
headers = Lithic::Internal::Util.normalized_headers(response&.each_header&.to_h)

case status
in ..299
Expand All @@ -390,7 +391,7 @@ def send_request(request, redirect_count:, retry_count:, send_retry_header:)
in 300..399
self.class.reap_connection!(status, stream: stream)

request = self.class.follow_redirect(request, status: status, response_headers: response)
request = self.class.follow_redirect(request, status: status, response_headers: headers)
send_request(
request,
redirect_count: redirect_count + 1,
Expand All @@ -399,16 +400,17 @@ def send_request(request, redirect_count:, retry_count:, send_retry_header:)
)
in Lithic::Errors::APIConnectionError if retry_count >= max_retries
raise status
in (400..) if retry_count >= max_retries || !self.class.should_retry?(status, headers: response)
in (400..) if retry_count >= max_retries || !self.class.should_retry?(status, headers: headers)
decoded = Kernel.then do
Lithic::Internal::Util.decode_content(response, stream: stream, suppress_error: true)
Lithic::Internal::Util.decode_content(headers, stream: stream, suppress_error: true)
ensure
self.class.reap_connection!(status, stream: stream)
end

raise Lithic::Errors::APIStatusError.for(
url: url,
status: status,
headers: headers,
body: decoded,
request: nil,
response: response
Expand Down Expand Up @@ -485,19 +487,21 @@ def request(req)
send_retry_header: send_retry_header
)

decoded = Lithic::Internal::Util.decode_content(response, stream: stream)
headers = Lithic::Internal::Util.normalized_headers(response.each_header.to_h)
decoded = Lithic::Internal::Util.decode_content(headers, stream: stream)
case req
in {stream: Class => st}
st.new(
model: model,
url: url,
status: status,
headers: headers,
response: response,
unwrap: unwrap,
stream: decoded
)
in {page: Class => page}
page.new(client: self, req: req, headers: response, page_data: decoded)
page.new(client: self, req: req, headers: headers, page_data: decoded)
else
unwrapped = Lithic::Internal::Util.dig(decoded, unwrap)
Lithic::Internal::Type::Converter.coerce(model, unwrapped)
Expand Down
2 changes: 1 addition & 1 deletion lib/lithic/internal/type/base_page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def to_enum = super(:auto_paging_each)
#
# @param client [Lithic::Internal::Transport::BaseClient]
# @param req [Hash{Symbol=>Object}]
# @param headers [Hash{String=>String}, Net::HTTPHeader]
# @param headers [Hash{String=>String}]
# @param page_data [Object]
def initialize(client:, req:, headers:, page_data:)
@client = client
Expand Down
16 changes: 9 additions & 7 deletions lib/lithic/internal/type/union.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ module Type
# @api private
#
# @example
# # `account_holder_update_response` is a `Lithic::Models::AccountHolderUpdateResponse`
# case account_holder_update_response
# when Lithic::Models::AccountHolderUpdateResponse::KYBKYCPatchResponse
# puts(account_holder_update_response.token)
# when Lithic::Models::AccountHolderUpdateResponse::PatchResponse
# puts(account_holder_update_response.address)
# # `account_activity_list_response` is a `Lithic::Models::AccountActivityListResponse`
# case account_activity_list_response
# when Lithic::Models::AccountActivityListResponse::FinancialTransaction
# puts(account_activity_list_response.token)
# when Lithic::Models::AccountActivityListResponse::BookTransferTransaction
# puts(account_activity_list_response.category)
# when Lithic::Models::AccountActivityListResponse::CardTransaction
# # ...
# else
# puts(account_holder_update_response)
# puts(account_activity_list_response)
# end
module Union
include Lithic::Internal::Type::Converter
Expand Down
2 changes: 1 addition & 1 deletion lib/lithic/internal/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ def force_charset!(content_type, text:)
#
# Assumes each chunk in stream has `Encoding::BINARY`.
#
# @param headers [Hash{String=>String}, Net::HTTPHeader]
# @param headers [Hash{String=>String}]
# @param stream [Enumerable<String>]
# @param suppress_error [Boolean]
#
Expand Down
2 changes: 2 additions & 0 deletions lib/lithic/models/account_activity_list_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ module Models
module AccountActivityListResponse
extend Lithic::Internal::Type::Union

discriminator :family

# Financial transaction with inheritance from unified base transaction
variant -> { Lithic::Models::AccountActivityListResponse::FinancialTransaction }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ module Models
module AccountActivityRetrieveTransactionResponse
extend Lithic::Internal::Type::Union

discriminator :family

# Financial transaction with inheritance from unified base transaction
variant -> { Lithic::Models::AccountActivityRetrieveTransactionResponse::FinancialTransaction }

Expand Down
3 changes: 2 additions & 1 deletion lib/lithic/models/transaction_simulate_void_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class TransactionSimulateVoidParams < Lithic::Internal::Type::BaseModel

# @!attribute amount
# Amount (in cents) to void. Typically this will match the amount in the original
# authorization, but can be less.
# authorization, but can be less. Applies to authorization reversals only. An
# authorization expiry will always apply to the full pending amount.
#
# @return [Integer, nil]
optional :amount, Integer
Expand Down
Loading