minato_ruby_api_client 0.2.8 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02fb9f5aacc6d902765edab876eaf8683524cf4e316c0904942cbc264e0752f4
4
- data.tar.gz: ef183cfd8211a90cbce9a0a0ad289f3ffb14bdc3d265ed3e3ce1db6e6232a5dd
3
+ metadata.gz: 430a3104ab0c69e3c62ba990f3fb91029a85f257525c56e6a35f58b88a9a94bd
4
+ data.tar.gz: eec830c93eab4c0f5b395adf4f1b65a93c8b92add998c7d095b9adb10f0c0249
5
5
  SHA512:
6
- metadata.gz: 4ac4f87be5a401c6cb4cb69eae9e77119883304eb9bea5cc6a66966bc3906d63d11d1b1f1338c37d5d879ee8b5b58ac63aa8c931bda6ed16d51408f9f8823165
7
- data.tar.gz: d45d5bdfc88e0ac4d53845f1c8afc2eba032a6255b10545253e4555cb53a153c617a0bdbcb5c2dc7b9dd9681b07f980639dd9af86a3e24bcdc97d519b164307d
6
+ metadata.gz: '0119f09dc1d07e5901b65979adfcc4025f269fbdc321861790fa78562dd18bf49ef093d76473ae160e92982c8e585eb0ef4972e77ea02eaa11935e668feadb19'
7
+ data.tar.gz: f5040c62fb32554a3fa83c14b9368a6d7e0647d629dca7aafd125442b0e3816c38eb580977f12479d501093cee7561d8a9383023d18d5719a3e9ddad48ae8d80
data/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.0]
9
+
10
+ ### Changed
11
+
12
+ - Switch the Faraday adapter from `net_http` to `async_http` (`async-http-faraday`) so requests no longer block the underlying thread inside Falcon's fiber scheduler.
13
+ - Require Faraday `>= 2.0` (needed by `async-http-faraday`).
14
+
15
+ ### Fixed
16
+
17
+ - `ApiError#caused_by` now falls back to the standard HTTP status text when the adapter does not expose a custom reason phrase (the `async_http` adapter does not preserve it, unlike `net_http`).
18
+
8
19
  ## [0.2.0]
9
20
 
10
21
  ### Added
data/Gemfile.lock CHANGED
@@ -1,10 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- minato_ruby_api_client (0.2.8)
5
- faraday (>= 1.0, < 3.0)
4
+ minato_ruby_api_client (0.3.0)
5
+ async-http-faraday (>= 0.19)
6
+ faraday (>= 2.0, < 3.0)
6
7
  minato-trace (>= 0.2.0)
7
8
  minato_error_handler (>= 0.1.13)
9
+ rack (>= 2.0)
8
10
 
9
11
  GEM
10
12
  remote: https://rubygems.org/
@@ -86,6 +88,28 @@ GEM
86
88
  addressable (2.8.7)
87
89
  public_suffix (>= 2.0.2, < 7.0)
88
90
  ast (2.4.3)
91
+ async (2.37.0)
92
+ console (~> 1.29)
93
+ fiber-annotation
94
+ io-event (~> 1.11)
95
+ metrics (~> 0.12)
96
+ traces (~> 0.18)
97
+ async-http (0.94.2)
98
+ async (>= 2.10.2)
99
+ async-pool (~> 0.11)
100
+ io-endpoint (~> 0.14)
101
+ io-stream (~> 0.6)
102
+ metrics (~> 0.12)
103
+ protocol-http (~> 0.58)
104
+ protocol-http1 (~> 0.36)
105
+ protocol-http2 (~> 0.22)
106
+ protocol-url (~> 0.2)
107
+ traces (~> 0.10)
108
+ async-http-faraday (0.22.1)
109
+ async-http (~> 0.42)
110
+ faraday
111
+ async-pool (0.11.2)
112
+ async (>= 2.0)
89
113
  base64 (0.3.0)
90
114
  bigdecimal (3.3.1)
91
115
  builder (3.3.0)
@@ -93,6 +117,10 @@ GEM
93
117
  coderay (1.1.3)
94
118
  concurrent-ruby (1.3.5)
95
119
  connection_pool (2.5.4)
120
+ console (1.34.3)
121
+ fiber-annotation
122
+ fiber-local (~> 1.1)
123
+ json
96
124
  crack (1.0.1)
97
125
  bigdecimal
98
126
  rexml
@@ -103,29 +131,18 @@ GEM
103
131
  drb (2.2.3)
104
132
  erb (6.0.0)
105
133
  erubi (1.13.1)
106
- faraday (1.10.4)
107
- faraday-em_http (~> 1.0)
108
- faraday-em_synchrony (~> 1.0)
109
- faraday-excon (~> 1.1)
110
- faraday-httpclient (~> 1.0)
111
- faraday-multipart (~> 1.0)
112
- faraday-net_http (~> 1.0)
113
- faraday-net_http_persistent (~> 1.0)
114
- faraday-patron (~> 1.0)
115
- faraday-rack (~> 1.0)
116
- faraday-retry (~> 1.0)
117
- ruby2_keywords (>= 0.0.4)
118
- faraday-em_http (1.0.0)
119
- faraday-em_synchrony (1.0.1)
120
- faraday-excon (1.1.0)
121
- faraday-httpclient (1.0.1)
122
- faraday-multipart (1.1.1)
123
- multipart-post (~> 2.0)
124
- faraday-net_http (1.0.2)
125
- faraday-net_http_persistent (1.2.0)
126
- faraday-patron (1.0.0)
127
- faraday-rack (1.0.0)
128
- faraday-retry (1.0.3)
134
+ faraday (2.14.3)
135
+ faraday-net_http (>= 2.0, < 3.5)
136
+ json
137
+ logger
138
+ faraday-net_http (3.4.4)
139
+ net-http (~> 0.5)
140
+ faraday-retry (2.4.0)
141
+ faraday (~> 2.0)
142
+ fiber-annotation (0.2.0)
143
+ fiber-local (1.1.0)
144
+ fiber-storage
145
+ fiber-storage (1.0.1)
129
146
  gapic-common (1.2.0)
130
147
  faraday (>= 1.9, < 3.a)
131
148
  faraday-retry (>= 1.0, < 3.a)
@@ -218,11 +235,14 @@ GEM
218
235
  i18n (1.14.7)
219
236
  concurrent-ruby (~> 1.0)
220
237
  io-console (0.8.1)
238
+ io-endpoint (0.17.2)
239
+ io-event (1.11.2)
240
+ io-stream (0.11.1)
221
241
  irb (1.15.3)
222
242
  pp (>= 0.6.0)
223
243
  rdoc (>= 4.0.0)
224
244
  reline (>= 0.4.2)
225
- json (2.16.0)
245
+ json (2.21.2)
226
246
  jwt (3.1.2)
227
247
  base64
228
248
  language_server-protocol (3.17.0.5)
@@ -238,6 +258,7 @@ GEM
238
258
  net-smtp
239
259
  marcel (1.1.0)
240
260
  method_source (1.1.0)
261
+ metrics (0.15.0)
241
262
  minato-trace (0.2.0)
242
263
  google-cloud-trace (~> 0.42.2)
243
264
  rails (>= 7.0.0)
@@ -246,7 +267,8 @@ GEM
246
267
  mini_mime (1.1.5)
247
268
  minitest (5.26.2)
248
269
  multi_json (1.17.0)
249
- multipart-post (2.4.1)
270
+ net-http (0.9.1)
271
+ uri (>= 0.11.1)
250
272
  net-imap (0.5.12)
251
273
  date
252
274
  net-protocol
@@ -282,6 +304,14 @@ GEM
282
304
  prettyprint
283
305
  prettyprint (0.2.0)
284
306
  prism (1.6.0)
307
+ protocol-hpack (1.5.1)
308
+ protocol-http (0.59.0)
309
+ protocol-http1 (0.37.0)
310
+ protocol-http (~> 0.58)
311
+ protocol-http2 (0.24.0)
312
+ protocol-hpack (~> 1.4)
313
+ protocol-http (~> 0.47)
314
+ protocol-url (0.10.0)
285
315
  pry (0.15.2)
286
316
  coderay (~> 1.1)
287
317
  method_source (~> 1.0)
@@ -371,7 +401,6 @@ GEM
371
401
  rubocop-rspec (3.0.5)
372
402
  rubocop (~> 1.61)
373
403
  ruby-progressbar (1.13.0)
374
- ruby2_keywords (0.0.5)
375
404
  securerandom (0.4.1)
376
405
  signet (0.21.0)
377
406
  addressable (~> 2.8)
@@ -389,6 +418,7 @@ GEM
389
418
  stringio (3.1.8)
390
419
  thor (1.4.0)
391
420
  timeout (0.4.4)
421
+ traces (0.18.2)
392
422
  tsort (0.2.0)
393
423
  tzinfo (2.0.6)
394
424
  concurrent-ruby (~> 1.0)
@@ -426,4 +456,4 @@ DEPENDENCIES
426
456
  webmock
427
457
 
428
458
  BUNDLED WITH
429
- 2.7.2
459
+ 2.5.5
@@ -4,6 +4,8 @@ require 'logger'
4
4
  require 'tempfile'
5
5
  require 'time'
6
6
  require 'faraday'
7
+ require 'async/http/faraday'
8
+ require 'rack/utils'
7
9
  require 'minato/trace'
8
10
  require 'ostruct'
9
11
 
@@ -56,7 +58,7 @@ module MinatoRubyApiClient
56
58
  conn.request :multipart
57
59
  conn.request :url_encoded
58
60
  end
59
- conn.adapter(Faraday.default_adapter)
61
+ conn.adapter(:async_http)
60
62
  end
61
63
 
62
64
  request = nil
@@ -72,7 +74,8 @@ module MinatoRubyApiClient
72
74
  unless response.success?
73
75
  error = self.class.module_parent::ApiError.new(:status_code => response.status,
74
76
  :res => response, :req => request)
75
- error.caused_by = response.reason_phrase
77
+ reason = response.reason_phrase.to_s.empty? ? Rack::Utils::HTTP_STATUS_CODES[response.status] : response.reason_phrase
78
+ error.caused_by = reason unless error.upstream_error?
76
79
 
77
80
  fail error
78
81
  end
@@ -1,4 +1,5 @@
1
1
  require 'minato_error_handler'
2
+ require 'json'
2
3
 
3
4
  module MinatoRubyApiClient
4
5
  class ApiError < MinatoErrorHandler::Errors::ExternalError
@@ -6,11 +7,54 @@ module MinatoRubyApiClient
6
7
 
7
8
  def initialize(res: nil, req: nil, status_code: 500)
8
9
  super(req: req, res: res)
10
+ @upstream_error = nil
9
11
  @status_code = status_code
12
+ extracted_error, extracted_status = extract_upstream_error(res)
13
+ @upstream_error = extracted_error
14
+ @status_code = extracted_status || status_code
10
15
  end
11
16
 
12
17
  def message
13
- "An error occurred while communicating with the API."
18
+ upstream_field("message") || "An error occurred while communicating with the API."
14
19
  end
20
+
21
+ def code
22
+ upstream_field("code") || super
23
+ end
24
+
25
+ def details
26
+ upstream_field("details") || super
27
+ end
28
+
29
+ def caused_by
30
+ upstream_field("caused_by") || super
31
+ end
32
+
33
+ def upstream_error?
34
+ !@upstream_error.nil? && !@upstream_error.empty?
35
+ end
36
+
37
+ private
38
+
39
+ def extract_upstream_error(res)
40
+ return [nil, nil] unless res.respond_to?(:body)
41
+
42
+ body = res.body
43
+ return [nil, nil] if body.nil? || body.empty?
44
+
45
+ parsed = JSON.parse(body)
46
+ return [nil, nil] unless parsed.is_a?(Hash)
47
+
48
+ upstream = parsed["error"] || parsed
49
+ return [nil, nil] unless upstream.is_a?(Hash) && upstream["code"]
50
+
51
+ [upstream, upstream["status_code"]&.to_i]
52
+ rescue JSON::ParserError
53
+ [nil, nil]
54
+ end
55
+
56
+ def upstream_field(key)
57
+ @upstream_error&.[](key)
58
+ end
15
59
  end
16
60
  end
@@ -1,3 +1,3 @@
1
1
  module MinatoRubyApiClient
2
- VERSION = '0.2.8'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -22,7 +22,9 @@ Gem::Specification.new do |s|
22
22
  s.license = "unlicense"
23
23
  s.required_ruby_version = ">= 3.2"
24
24
 
25
- s.add_runtime_dependency 'faraday', '>= 1.0', '< 3.0'
25
+ s.add_runtime_dependency 'faraday', '>= 2.0', '< 3.0'
26
+ s.add_runtime_dependency 'async-http-faraday', '>= 0.19'
27
+ s.add_runtime_dependency 'rack', '>= 2.0'
26
28
  s.add_runtime_dependency 'minato_error_handler', '>= 0.1.13'
27
29
  s.add_runtime_dependency 'minato-trace', '>= 0.2.0'
28
30
 
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe MinatoRubyApiClient::ApiError do
4
+ def fake_response(body:, status: 422)
5
+ double("Faraday::Response", body: body, status: status)
6
+ end
7
+
8
+ let(:upstream_error_body) do
9
+ JSON.generate({
10
+ "error" => {
11
+ "code" => "AccountSvc::Errors::AccountNotFound",
12
+ "message" => "Account not found",
13
+ "status_code" => 404,
14
+ "details" => { "account_id" => "abc-123" },
15
+ "caused_by" => "Couldn't find Account with id=abc-123"
16
+ }
17
+ })
18
+ end
19
+
20
+ context "when the upstream response contains a Minato error payload" do
21
+ subject(:error) do
22
+ described_class.new(res: fake_response(body: upstream_error_body, status: 404))
23
+ end
24
+
25
+ it "propagates the upstream code" do
26
+ expect(error.code).to eq("AccountSvc::Errors::AccountNotFound")
27
+ end
28
+
29
+ it "propagates the upstream message" do
30
+ expect(error.message).to eq("Account not found")
31
+ end
32
+
33
+ it "propagates the upstream status_code" do
34
+ expect(error.status_code).to eq(404)
35
+ end
36
+
37
+ it "propagates the upstream details" do
38
+ expect(error.details).to eq({ "account_id" => "abc-123" })
39
+ end
40
+
41
+ it "propagates the upstream caused_by" do
42
+ expect(error.caused_by).to eq("Couldn't find Account with id=abc-123")
43
+ end
44
+ end
45
+
46
+ context "when the upstream response has no Minato error payload" do
47
+ subject(:error) do
48
+ described_class.new(res: fake_response(body: "Service Unavailable", status: 503), status_code: 503)
49
+ end
50
+
51
+ it "falls back to the default code (class name)" do
52
+ expect(error.code).to eq("MinatoRubyApiClient::ApiError")
53
+ end
54
+
55
+ it "falls back to the default message" do
56
+ expect(error.message).to eq("An error occurred while communicating with the API.")
57
+ end
58
+
59
+ it "uses the provided status_code" do
60
+ expect(error.status_code).to eq(503)
61
+ end
62
+ end
63
+
64
+ context "when the upstream response body is empty" do
65
+ subject(:error) do
66
+ described_class.new(res: fake_response(body: "", status: 500), status_code: 500)
67
+ end
68
+
69
+ it "falls back to default behavior" do
70
+ expect(error.code).to eq("MinatoRubyApiClient::ApiError")
71
+ expect(error.message).to eq("An error occurred while communicating with the API.")
72
+ end
73
+ end
74
+
75
+ context "when the upstream response body is invalid JSON" do
76
+ subject(:error) do
77
+ described_class.new(res: fake_response(body: "<html>error</html>", status: 500), status_code: 500)
78
+ end
79
+
80
+ it "falls back to default behavior without raising" do
81
+ expect(error.code).to eq("MinatoRubyApiClient::ApiError")
82
+ expect(error.message).to eq("An error occurred while communicating with the API.")
83
+ end
84
+ end
85
+
86
+ describe "#upstream_error?" do
87
+ it "returns true when the response contains a Minato error payload" do
88
+ error = described_class.new(res: fake_response(body: upstream_error_body, status: 404))
89
+ expect(error.upstream_error?).to be true
90
+ end
91
+
92
+ it "returns false when the response has no Minato error payload" do
93
+ error = described_class.new(res: fake_response(body: "", status: 503), status_code: 503)
94
+ expect(error.upstream_error?).to be false
95
+ end
96
+ end
97
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minato_ruby_api_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ferreri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-18 00:00:00.000000000 Z
11
+ date: 2026-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
19
+ version: '2.0'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '3.0'
@@ -26,10 +26,38 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '1.0'
29
+ version: '2.0'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '3.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: async-http-faraday
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0.19'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0.19'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rack
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '2.0'
33
61
  - !ruby/object:Gem::Dependency
34
62
  name: minato_error_handler
35
63
  requirement: !ruby/object:Gem::Requirement
@@ -99,6 +127,7 @@ files:
99
127
  - lib/minato_ruby_api_client/version.rb
100
128
  - minato_ruby_api_client.gemspec
101
129
  - spec/api_client_spec.rb
130
+ - spec/api_error_spec.rb
102
131
  - spec/configuration_spec.rb
103
132
  - spec/minato_ruby_api_client_spec.rb
104
133
  - spec/spec_helper.rb
@@ -127,6 +156,7 @@ specification_version: 4
127
156
  summary: A Minato ruby HTTP Client
128
157
  test_files:
129
158
  - spec/api_client_spec.rb
159
+ - spec/api_error_spec.rb
130
160
  - spec/configuration_spec.rb
131
161
  - spec/minato_ruby_api_client_spec.rb
132
162
  - spec/spec_helper.rb