faraday 2.13.1 → 2.14.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: 7ab8d55c7b360596e25721ecc32dcf99c65cea19d04598bb468fc2a422e26146
4
- data.tar.gz: 624ddc291aec0a438fded658897b2127d62b8fe10e00590e3ad0a609307711d6
3
+ metadata.gz: 2fc5e44f27171827ea964dee68999e7d9ae6cd4d952a0a8ad0600e155cdf3892
4
+ data.tar.gz: 88c85684e7d9acb7978b36d20f2041145441f99e7705e6cf8f41483ed620dadc
5
5
  SHA512:
6
- metadata.gz: e2394c6ccb0b3e12d16e2dcd739d8ecf053199b40ce5d8d4a1902cc141c12746190e21b928e4979405ffcb2660db80a4777d46ee839df10f75cb66b34039285e
7
- data.tar.gz: d91f5258bb49c40ff720aaba295ab4c86ea975cb7e0d4ad96647921e34924013501c1b4dddcf1c5497488fda0480ef39296f76b16188201f0162da004a490ba5
6
+ metadata.gz: 972d764f70f42d46e01ef8c8a33aab692619b8adc1793f07ed031cc7cd09ad6d749d5948d25f4e9e8c6b83377bfa2a25eda1e80eb79d086bae93d90959e321bd
7
+ data.tar.gz: b9b7b80f59d02b813ae99ba5afd3cbaf517953f208f2cfc91347bdbec1f4112d4b508c22721f65ac607b45a47507d7adc97cbe7cf08b3d0c56ad0c7dc1b0af5e
@@ -76,9 +76,9 @@ module Faraday
76
76
 
77
77
  empty_accumulator = {}
78
78
 
79
- split_query = (query.split('&').map do |pair|
79
+ split_query = query.split('&').filter_map do |pair|
80
80
  pair.split('=', 2) if pair && !pair.empty?
81
- end).compact
81
+ end
82
82
  split_query.each_with_object(empty_accumulator.dup) do |pair, accu|
83
83
  pair[0] = unescape(pair[0])
84
84
  pair[1] = true if pair[1].nil?
data/lib/faraday/error.rb CHANGED
@@ -79,12 +79,46 @@ module Faraday
79
79
 
80
80
  # Pulls out potential parent exception and response hash.
81
81
  def exc_msg_and_response(exc, response = nil)
82
- return [exc, exc.message, response] if exc.respond_to?(:backtrace)
82
+ case exc
83
+ when Exception
84
+ [exc, exc.message, response]
85
+ when Hash
86
+ [nil, build_error_message_from_hash(exc), exc]
87
+ when Faraday::Env
88
+ [nil, build_error_message_from_env(exc), exc]
89
+ else
90
+ [nil, exc.to_s, response]
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def build_error_message_from_hash(hash)
97
+ # Be defensive with external Hash objects - they might be missing keys
98
+ status = hash.fetch(:status, nil)
99
+ request = hash.fetch(:request, nil)
100
+
101
+ return fallback_error_message(status) if request.nil?
83
102
 
84
- return [nil, "the server responded with status #{exc[:status]}", exc] \
85
- if exc.respond_to?(:each_key)
103
+ method = request.fetch(:method, nil)
104
+ url = request.fetch(:url, nil)
105
+ build_status_error_message(status, method, url)
106
+ end
107
+
108
+ def build_error_message_from_env(env)
109
+ # Faraday::Env is internal - we can make reasonable assumptions about its structure
110
+ build_status_error_message(env.status, env.method, env.url)
111
+ end
86
112
 
87
- [nil, exc.to_s, response]
113
+ def build_status_error_message(status, method, url)
114
+ method_str = method ? method.to_s.upcase : ''
115
+ url_str = url ? url.to_s : ''
116
+ "the server responded with status #{status} for #{method_str} #{url_str}"
117
+ end
118
+
119
+ def fallback_error_message(status)
120
+ "the server responded with status #{status} - method and url are not available " \
121
+ 'due to include_request: false on Faraday::Response::RaiseError middleware'
88
122
  end
89
123
  end
90
124
 
@@ -121,9 +155,12 @@ module Faraday
121
155
  end
122
156
 
123
157
  # Raised by Faraday::Response::RaiseError in case of a 422 response.
124
- class UnprocessableEntityError < ClientError
158
+ class UnprocessableContentError < ClientError
125
159
  end
126
160
 
161
+ # Used to provide compatibility with legacy error name.
162
+ UnprocessableEntityError = UnprocessableContentError
163
+
127
164
  # Raised by Faraday::Response::RaiseError in case of a 429 response.
128
165
  class TooManyRequestsError < ClientError
129
166
  end
@@ -63,7 +63,7 @@ module Faraday
63
63
 
64
64
  def dump_body(body)
65
65
  if body.respond_to?(:to_str)
66
- body.to_str
66
+ body.to_str.encode(Encoding::UTF_8, undef: :replace, invalid: :replace)
67
67
  else
68
68
  pretty_inspect(body)
69
69
  end
@@ -221,7 +221,7 @@ module Faraday
221
221
  end
222
222
 
223
223
  def raise_if_adapter(klass)
224
- return unless is_adapter?(klass)
224
+ return unless klass <= Faraday::Adapter
225
225
 
226
226
  raise 'Adapter should be set using the `adapter` method, not `use`'
227
227
  end
@@ -234,10 +234,6 @@ module Faraday
234
234
  !@adapter.nil?
235
235
  end
236
236
 
237
- def is_adapter?(klass) # rubocop:disable Naming/PredicateName
238
- klass <= Faraday::Adapter
239
- end
240
-
241
237
  def use_symbol(mod, key, ...)
242
238
  use(mod.lookup_middleware(key), ...)
243
239
  end
@@ -15,7 +15,7 @@ module Faraday
15
15
  404 => Faraday::ResourceNotFound,
16
16
  408 => Faraday::RequestTimeoutError,
17
17
  409 => Faraday::ConflictError,
18
- 422 => Faraday::UnprocessableEntityError,
18
+ 422 => Faraday::UnprocessableContentError,
19
19
  429 => Faraday::TooManyRequestsError
20
20
  }.freeze
21
21
  # rubocop:enable Naming/ConstantName
@@ -33,6 +33,10 @@ module Faraday
33
33
  finished? ? env.body : nil
34
34
  end
35
35
 
36
+ def url
37
+ finished? ? env.url : nil
38
+ end
39
+
36
40
  def finished?
37
41
  !!env
38
42
  end
@@ -60,9 +64,9 @@ module Faraday
60
64
 
61
65
  def to_hash
62
66
  {
63
- status: env.status, body: env.body,
64
- response_headers: env.response_headers,
65
- url: env.url
67
+ status: status, body: body,
68
+ response_headers: headers,
69
+ url: url
66
70
  }
67
71
  end
68
72
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- VERSION = '2.13.1'
4
+ VERSION = '2.14.0'
5
5
  end
data/lib/faraday.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cgi'
3
+ require 'cgi/escape'
4
+ require 'cgi/util' if RUBY_VERSION < '3.5'
4
5
  require 'date'
5
6
  require 'set'
6
7
  require 'forwardable'
@@ -23,7 +23,7 @@ RSpec.describe Faraday::Error do
23
23
 
24
24
  it { expect(subject.wrapped_exception).to be_nil }
25
25
  it { expect(subject.response).to eq(exception) }
26
- it { expect(subject.message).to eq('the server responded with status 400') }
26
+ it { expect(subject.message).to eq('the server responded with status 400 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
27
27
  if RUBY_VERSION >= '3.4'
28
28
  it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
29
29
  else
@@ -89,5 +89,87 @@ RSpec.describe Faraday::Error do
89
89
  it { expect(subject.response_headers).to eq(headers) }
90
90
  it { expect(subject.response_body).to eq(body) }
91
91
  end
92
+
93
+ context 'with hash missing status key' do
94
+ let(:exception) { { body: 'error body' } }
95
+
96
+ it { expect(subject.wrapped_exception).to be_nil }
97
+ it { expect(subject.response).to eq(exception) }
98
+ it { expect(subject.message).to eq('the server responded with status - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
99
+ end
100
+
101
+ context 'with hash with status but missing request data' do
102
+ let(:exception) { { status: 404, body: 'not found' } } # missing request key
103
+
104
+ it { expect(subject.wrapped_exception).to be_nil }
105
+ it { expect(subject.response).to eq(exception) }
106
+ it { expect(subject.message).to eq('the server responded with status 404 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
107
+ end
108
+
109
+ context 'with hash with status and request but missing method in request' do
110
+ let(:exception) { { status: 404, body: 'not found', request: { url: 'http://example.com/test' } } } # missing method
111
+
112
+ it { expect(subject.wrapped_exception).to be_nil }
113
+ it { expect(subject.response).to eq(exception) }
114
+ it { expect(subject.message).to eq('the server responded with status 404 for http://example.com/test') }
115
+ end
116
+
117
+ context 'with hash with status and request but missing url in request' do
118
+ let(:exception) { { status: 404, body: 'not found', request: { method: :get } } } # missing url
119
+
120
+ it { expect(subject.wrapped_exception).to be_nil }
121
+ it { expect(subject.response).to eq(exception) }
122
+ it { expect(subject.message).to eq('the server responded with status 404 for GET ') }
123
+ end
124
+
125
+ context 'with properly formed Faraday::Env' do
126
+ # This represents the normal case - a well-formed Faraday::Env object
127
+ # with all the standard properties populated as they would be during
128
+ # a typical HTTP request/response cycle
129
+ let(:exception) { Faraday::Env.new }
130
+
131
+ before do
132
+ exception.status = 500
133
+ exception.method = :post
134
+ exception.url = URI('https://api.example.com/users')
135
+ exception.request = Faraday::RequestOptions.new
136
+ exception.response_headers = { 'content-type' => 'application/json' }
137
+ exception.response_body = '{"error": "Internal server error"}'
138
+ exception.request_headers = { 'authorization' => 'Bearer token123' }
139
+ exception.request_body = '{"name": "John"}'
140
+ end
141
+
142
+ it { expect(subject.wrapped_exception).to be_nil }
143
+ it { expect(subject.response).to eq(exception) }
144
+ it { expect(subject.message).to eq('the server responded with status 500 for POST https://api.example.com/users') }
145
+ end
146
+
147
+ context 'with Faraday::Env missing status key' do
148
+ let(:exception) { Faraday::Env.new }
149
+
150
+ before do
151
+ exception[:body] = 'error body'
152
+ # Intentionally not setting status
153
+ end
154
+
155
+ it { expect(subject.wrapped_exception).to be_nil }
156
+ it { expect(subject.response).to eq(exception) }
157
+ it { expect(subject.message).to eq('the server responded with status for ') }
158
+ end
159
+
160
+ context 'with Faraday::Env with direct method and url properties' do
161
+ let(:exception) { Faraday::Env.new }
162
+
163
+ before do
164
+ exception.status = 404
165
+ exception.method = :get
166
+ exception.url = URI('http://example.com/test')
167
+ exception[:body] = 'not found'
168
+ end
169
+
170
+ it { expect(subject.wrapped_exception).to be_nil }
171
+ it { expect(subject.response).to eq(exception) }
172
+ it { expect(subject.message).to eq('the server responded with status 404 for GET http://example.com/test') }
173
+ end
92
174
  end
93
175
  end
@@ -21,6 +21,7 @@ RSpec.describe Faraday::Response::Logger do
21
21
  stubs.post('/ohai') { [200, { 'Content-Type' => 'text/html' }, 'fred'] }
22
22
  stubs.post('/ohyes') { [200, { 'Content-Type' => 'text/html' }, 'pebbles'] }
23
23
  stubs.get('/rubbles') { [200, { 'Content-Type' => 'application/json' }, rubbles] }
24
+ stubs.get('/8bit') { [200, { 'Content-Type' => 'text/html' }, (+'café!').force_encoding(Encoding::ASCII_8BIT)] }
24
25
  stubs.get('/filtered_body') { [200, { 'Content-Type' => 'text/html' }, 'soylent green is people'] }
25
26
  stubs.get('/filtered_headers') { [200, { 'Content-Type' => 'text/html' }, 'headers response'] }
26
27
  stubs.get('/filtered_params') { [200, { 'Content-Type' => 'text/html' }, 'params response'] }
@@ -238,6 +239,11 @@ RSpec.describe Faraday::Response::Logger do
238
239
  expect(string_io.string).to match(%(fred))
239
240
  end
240
241
 
242
+ it 'converts to UTF-8' do
243
+ conn.get '/8bit'
244
+ expect(string_io.string).to match(%(caf��!))
245
+ end
246
+
241
247
  after do
242
248
  described_class.default_options = { bodies: false }
243
249
  end
@@ -13,7 +13,7 @@ RSpec.describe Faraday::Response::RaiseError do
13
13
  stub.get('proxy-error') { [407, { 'X-Reason' => 'because' }, 'keep looking'] }
14
14
  stub.get('request-timeout') { [408, { 'X-Reason' => 'because' }, 'keep looking'] }
15
15
  stub.get('conflict') { [409, { 'X-Reason' => 'because' }, 'keep looking'] }
16
- stub.get('unprocessable-entity') { [422, { 'X-Reason' => 'because' }, 'keep looking'] }
16
+ stub.get('unprocessable-content') { [422, { 'X-Reason' => 'because' }, 'keep looking'] }
17
17
  stub.get('too-many-requests') { [429, { 'X-Reason' => 'because' }, 'keep looking'] }
18
18
  stub.get('4xx') { [499, { 'X-Reason' => 'because' }, 'keep looking'] }
19
19
  stub.get('nil-status') { [nil, { 'X-Reason' => 'nil' }, 'fail'] }
@@ -28,7 +28,7 @@ RSpec.describe Faraday::Response::RaiseError do
28
28
 
29
29
  it 'raises Faraday::BadRequestError for 400 responses' do
30
30
  expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex|
31
- expect(ex.message).to eq('the server responded with status 400')
31
+ expect(ex.message).to eq('the server responded with status 400 for GET http:/bad-request')
32
32
  expect(ex.response[:headers]['X-Reason']).to eq('because')
33
33
  expect(ex.response[:status]).to eq(400)
34
34
  expect(ex.response_status).to eq(400)
@@ -39,7 +39,7 @@ RSpec.describe Faraday::Response::RaiseError do
39
39
 
40
40
  it 'raises Faraday::UnauthorizedError for 401 responses' do
41
41
  expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex|
42
- expect(ex.message).to eq('the server responded with status 401')
42
+ expect(ex.message).to eq('the server responded with status 401 for GET http:/unauthorized')
43
43
  expect(ex.response[:headers]['X-Reason']).to eq('because')
44
44
  expect(ex.response[:status]).to eq(401)
45
45
  expect(ex.response_status).to eq(401)
@@ -50,7 +50,7 @@ RSpec.describe Faraday::Response::RaiseError do
50
50
 
51
51
  it 'raises Faraday::ForbiddenError for 403 responses' do
52
52
  expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex|
53
- expect(ex.message).to eq('the server responded with status 403')
53
+ expect(ex.message).to eq('the server responded with status 403 for GET http:/forbidden')
54
54
  expect(ex.response[:headers]['X-Reason']).to eq('because')
55
55
  expect(ex.response[:status]).to eq(403)
56
56
  expect(ex.response_status).to eq(403)
@@ -61,7 +61,7 @@ RSpec.describe Faraday::Response::RaiseError do
61
61
 
62
62
  it 'raises Faraday::ResourceNotFound for 404 responses' do
63
63
  expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex|
64
- expect(ex.message).to eq('the server responded with status 404')
64
+ expect(ex.message).to eq('the server responded with status 404 for GET http:/not-found')
65
65
  expect(ex.response[:headers]['X-Reason']).to eq('because')
66
66
  expect(ex.response[:status]).to eq(404)
67
67
  expect(ex.response_status).to eq(404)
@@ -83,7 +83,7 @@ RSpec.describe Faraday::Response::RaiseError do
83
83
 
84
84
  it 'raises Faraday::RequestTimeoutError for 408 responses' do
85
85
  expect { conn.get('request-timeout') }.to raise_error(Faraday::RequestTimeoutError) do |ex|
86
- expect(ex.message).to eq('the server responded with status 408')
86
+ expect(ex.message).to eq('the server responded with status 408 for GET http:/request-timeout')
87
87
  expect(ex.response[:headers]['X-Reason']).to eq('because')
88
88
  expect(ex.response[:status]).to eq(408)
89
89
  expect(ex.response_status).to eq(408)
@@ -94,7 +94,7 @@ RSpec.describe Faraday::Response::RaiseError do
94
94
 
95
95
  it 'raises Faraday::ConflictError for 409 responses' do
96
96
  expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex|
97
- expect(ex.message).to eq('the server responded with status 409')
97
+ expect(ex.message).to eq('the server responded with status 409 for GET http:/conflict')
98
98
  expect(ex.response[:headers]['X-Reason']).to eq('because')
99
99
  expect(ex.response[:status]).to eq(409)
100
100
  expect(ex.response_status).to eq(409)
@@ -103,9 +103,20 @@ RSpec.describe Faraday::Response::RaiseError do
103
103
  end
104
104
  end
105
105
 
106
- it 'raises Faraday::UnprocessableEntityError for 422 responses' do
107
- expect { conn.get('unprocessable-entity') }.to raise_error(Faraday::UnprocessableEntityError) do |ex|
108
- expect(ex.message).to eq('the server responded with status 422')
106
+ it 'raises legacy Faraday::UnprocessableEntityError for 422 responses' do
107
+ expect { conn.get('unprocessable-content') }.to raise_error(Faraday::UnprocessableEntityError) do |ex|
108
+ expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-content')
109
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
110
+ expect(ex.response[:status]).to eq(422)
111
+ expect(ex.response_status).to eq(422)
112
+ expect(ex.response_body).to eq('keep looking')
113
+ expect(ex.response_headers['X-Reason']).to eq('because')
114
+ end
115
+ end
116
+
117
+ it 'raises Faraday::UnprocessableContentError for 422 responses' do
118
+ expect { conn.get('unprocessable-content') }.to raise_error(Faraday::UnprocessableContentError) do |ex|
119
+ expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-content')
109
120
  expect(ex.response[:headers]['X-Reason']).to eq('because')
110
121
  expect(ex.response[:status]).to eq(422)
111
122
  expect(ex.response_status).to eq(422)
@@ -116,7 +127,7 @@ RSpec.describe Faraday::Response::RaiseError do
116
127
 
117
128
  it 'raises Faraday::TooManyRequestsError for 429 responses' do
118
129
  expect { conn.get('too-many-requests') }.to raise_error(Faraday::TooManyRequestsError) do |ex|
119
- expect(ex.message).to eq('the server responded with status 429')
130
+ expect(ex.message).to eq('the server responded with status 429 for GET http:/too-many-requests')
120
131
  expect(ex.response[:headers]['X-Reason']).to eq('because')
121
132
  expect(ex.response[:status]).to eq(429)
122
133
  expect(ex.response_status).to eq(429)
@@ -138,7 +149,7 @@ RSpec.describe Faraday::Response::RaiseError do
138
149
 
139
150
  it 'raises Faraday::ClientError for other 4xx responses' do
140
151
  expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex|
141
- expect(ex.message).to eq('the server responded with status 499')
152
+ expect(ex.message).to eq('the server responded with status 499 for GET http:/4xx')
142
153
  expect(ex.response[:headers]['X-Reason']).to eq('because')
143
154
  expect(ex.response[:status]).to eq(499)
144
155
  expect(ex.response_status).to eq(499)
@@ -149,7 +160,7 @@ RSpec.describe Faraday::Response::RaiseError do
149
160
 
150
161
  it 'raises Faraday::ServerError for 500 responses' do
151
162
  expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex|
152
- expect(ex.message).to eq('the server responded with status 500')
163
+ expect(ex.message).to eq('the server responded with status 500 for GET http:/server-error')
153
164
  expect(ex.response[:headers]['X-Error']).to eq('bailout')
154
165
  expect(ex.response[:status]).to eq(500)
155
166
  expect(ex.response_status).to eq(500)
@@ -13,6 +13,7 @@ RSpec.describe Faraday::Response do
13
13
  it { expect(subject.success?).to be_falsey }
14
14
  it { expect(subject.status).to eq(404) }
15
15
  it { expect(subject.body).to eq('yikes') }
16
+ it { expect(subject.url).to eq(URI('https://lostisland.github.io/faraday')) }
16
17
  it { expect(subject.headers['Content-Type']).to eq('text/plain') }
17
18
  it { expect(subject['content-type']).to eq('text/plain') }
18
19
 
@@ -31,6 +32,12 @@ RSpec.describe Faraday::Response do
31
32
  it { expect(hash[:response_headers]).to eq(subject.headers) }
32
33
  it { expect(hash[:body]).to eq(subject.body) }
33
34
  it { expect(hash[:url]).to eq(subject.env.url) }
35
+
36
+ context 'when response is not finished' do
37
+ subject { Faraday::Response.new.to_hash }
38
+
39
+ it { is_expected.to eq({ status: nil, body: nil, response_headers: {}, url: nil }) }
40
+ end
34
41
  end
35
42
 
36
43
  describe 'marshal serialization support' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faraday
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.13.1
4
+ version: 2.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - "@technoweenie"
@@ -144,7 +144,7 @@ licenses:
144
144
  - MIT
145
145
  metadata:
146
146
  homepage_uri: https://lostisland.github.io/faraday
147
- changelog_uri: https://github.com/lostisland/faraday/releases/tag/v2.13.1
147
+ changelog_uri: https://github.com/lostisland/faraday/releases/tag/v2.14.0
148
148
  source_code_uri: https://github.com/lostisland/faraday
149
149
  bug_tracker_uri: https://github.com/lostisland/faraday/issues
150
150
  rubygems_mfa_required: 'true'
@@ -163,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
163
  - !ruby/object:Gem::Version
164
164
  version: '0'
165
165
  requirements: []
166
- rubygems_version: 3.6.7
166
+ rubygems_version: 3.6.9
167
167
  specification_version: 4
168
168
  summary: HTTP/REST API client library.
169
169
  test_files: []