minato_ruby_api_client 0.2.8 → 0.2.9

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: 45411b504804ac12f92faecfe179060bdd00d565625671c8f6635b1b26ec66b0
4
+ data.tar.gz: 76665d77f7d0d036152d0389c7625185a0b244160803b8f614866fc1c6aacc78
5
5
  SHA512:
6
- metadata.gz: 4ac4f87be5a401c6cb4cb69eae9e77119883304eb9bea5cc6a66966bc3906d63d11d1b1f1338c37d5d879ee8b5b58ac63aa8c931bda6ed16d51408f9f8823165
7
- data.tar.gz: d45d5bdfc88e0ac4d53845f1c8afc2eba032a6255b10545253e4555cb53a153c617a0bdbcb5c2dc7b9dd9681b07f980639dd9af86a3e24bcdc97d519b164307d
6
+ metadata.gz: c9b8ba15364e4b92dc7d485da1b6d8dae44b66f53058a42ab8be4073aff5d8d954180da03b4a0b6c59d82141ea1a8b6403c1c9b27bd246f9207538f230a5e0d4
7
+ data.tar.gz: a5d9bde60aeaccfbfee2dfe6e71c3bab5bf01d81bb3e3f069a019614dd45429fe6f776f46ca259ccf907278c20aab44a3ee84b96e4054f6783c144dcc39d73eb
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- minato_ruby_api_client (0.2.8)
4
+ minato_ruby_api_client (0.2.9)
5
5
  faraday (>= 1.0, < 3.0)
6
6
  minato-trace (>= 0.2.0)
7
7
  minato_error_handler (>= 0.1.13)
@@ -72,7 +72,7 @@ module MinatoRubyApiClient
72
72
  unless response.success?
73
73
  error = self.class.module_parent::ApiError.new(:status_code => response.status,
74
74
  :res => response, :req => request)
75
- error.caused_by = response.reason_phrase
75
+ error.caused_by = response.reason_phrase unless error.upstream_error?
76
76
 
77
77
  fail error
78
78
  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.2.9'
3
3
  end
@@ -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.2.9
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-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -99,6 +99,7 @@ files:
99
99
  - lib/minato_ruby_api_client/version.rb
100
100
  - minato_ruby_api_client.gemspec
101
101
  - spec/api_client_spec.rb
102
+ - spec/api_error_spec.rb
102
103
  - spec/configuration_spec.rb
103
104
  - spec/minato_ruby_api_client_spec.rb
104
105
  - spec/spec_helper.rb
@@ -127,6 +128,7 @@ specification_version: 4
127
128
  summary: A Minato ruby HTTP Client
128
129
  test_files:
129
130
  - spec/api_client_spec.rb
131
+ - spec/api_error_spec.rb
130
132
  - spec/configuration_spec.rb
131
133
  - spec/minato_ruby_api_client_spec.rb
132
134
  - spec/spec_helper.rb