faraday 2.7.10 → 2.14.1
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 +4 -4
- data/CHANGELOG.md +1 -1
- data/README.md +22 -11
- data/Rakefile +3 -0
- data/lib/faraday/adapter/test.rb +1 -1
- data/lib/faraday/adapter.rb +1 -1
- data/lib/faraday/connection.rb +17 -7
- data/lib/faraday/encoders/flat_params_encoder.rb +2 -2
- data/lib/faraday/encoders/nested_params_encoder.rb +1 -1
- data/lib/faraday/error.rb +50 -5
- data/lib/faraday/logging/formatter.rb +8 -8
- data/lib/faraday/middleware.rb +40 -1
- data/lib/faraday/options/env.rb +2 -2
- data/lib/faraday/options/proxy_options.rb +4 -2
- data/lib/faraday/options/ssl_options.rb +8 -2
- data/lib/faraday/options.rb +3 -3
- data/lib/faraday/rack_builder.rb +21 -25
- data/lib/faraday/request/json.rb +8 -2
- data/lib/faraday/response/json.rb +21 -1
- data/lib/faraday/response/logger.rb +5 -3
- data/lib/faraday/response/raise_error.rb +36 -19
- data/lib/faraday/response.rb +7 -3
- data/lib/faraday/utils/headers.rb +8 -2
- data/lib/faraday/utils.rb +3 -4
- data/lib/faraday/version.rb +1 -1
- data/lib/faraday.rb +2 -1
- data/spec/faraday/connection_spec.rb +35 -2
- data/spec/faraday/error_spec.rb +93 -3
- data/spec/faraday/middleware_spec.rb +143 -0
- data/spec/faraday/options/proxy_options_spec.rb +27 -0
- data/spec/faraday/params_encoders/nested_spec.rb +2 -1
- data/spec/faraday/request/json_spec.rb +64 -0
- data/spec/faraday/response/json_spec.rb +89 -0
- data/spec/faraday/response/logger_spec.rb +45 -4
- data/spec/faraday/response/raise_error_spec.rb +115 -13
- data/spec/faraday/response_spec.rb +7 -0
- data/spec/faraday/utils/headers_spec.rb +9 -0
- data/spec/faraday/utils_spec.rb +3 -1
- data/spec/faraday_spec.rb +10 -4
- data/spec/spec_helper.rb +6 -5
- data/spec/support/faraday_middleware_subclasses.rb +18 -0
- metadata +26 -14
|
@@ -10,11 +10,13 @@ module Faraday
|
|
|
10
10
|
# lifecycle to a given Logger object. By default, this logs to STDOUT. See
|
|
11
11
|
# Faraday::Logging::Formatter to see specifically what is logged.
|
|
12
12
|
class Logger < Middleware
|
|
13
|
+
DEFAULT_OPTIONS = { formatter: Logging::Formatter }.merge(Logging::Formatter::DEFAULT_OPTIONS).freeze
|
|
14
|
+
|
|
13
15
|
def initialize(app, logger = nil, options = {})
|
|
14
|
-
super(app)
|
|
16
|
+
super(app, options)
|
|
15
17
|
logger ||= ::Logger.new($stdout)
|
|
16
|
-
formatter_class = options.delete(:formatter)
|
|
17
|
-
@formatter = formatter_class.new(logger: logger, options: options)
|
|
18
|
+
formatter_class = @options.delete(:formatter)
|
|
19
|
+
@formatter = formatter_class.new(logger: logger, options: @options)
|
|
18
20
|
yield @formatter if block_given?
|
|
19
21
|
end
|
|
20
22
|
|
|
@@ -6,30 +6,32 @@ module Faraday
|
|
|
6
6
|
# client or server error responses.
|
|
7
7
|
class RaiseError < Middleware
|
|
8
8
|
# rubocop:disable Naming/ConstantName
|
|
9
|
-
ClientErrorStatuses = (400...500)
|
|
10
|
-
ServerErrorStatuses = (500...600)
|
|
9
|
+
ClientErrorStatuses = (400...500)
|
|
10
|
+
ServerErrorStatuses = (500...600)
|
|
11
|
+
ClientErrorStatusesWithCustomExceptions = {
|
|
12
|
+
400 => Faraday::BadRequestError,
|
|
13
|
+
401 => Faraday::UnauthorizedError,
|
|
14
|
+
403 => Faraday::ForbiddenError,
|
|
15
|
+
404 => Faraday::ResourceNotFound,
|
|
16
|
+
408 => Faraday::RequestTimeoutError,
|
|
17
|
+
409 => Faraday::ConflictError,
|
|
18
|
+
422 => Faraday::UnprocessableContentError,
|
|
19
|
+
429 => Faraday::TooManyRequestsError
|
|
20
|
+
}.freeze
|
|
11
21
|
# rubocop:enable Naming/ConstantName
|
|
12
22
|
|
|
23
|
+
DEFAULT_OPTIONS = { include_request: true, allowed_statuses: [] }.freeze
|
|
24
|
+
|
|
13
25
|
def on_complete(env)
|
|
26
|
+
return if Array(options[:allowed_statuses]).include?(env[:status])
|
|
27
|
+
|
|
14
28
|
case env[:status]
|
|
15
|
-
when
|
|
16
|
-
raise
|
|
17
|
-
when 401
|
|
18
|
-
raise Faraday::UnauthorizedError, response_values(env)
|
|
19
|
-
when 403
|
|
20
|
-
raise Faraday::ForbiddenError, response_values(env)
|
|
21
|
-
when 404
|
|
22
|
-
raise Faraday::ResourceNotFound, response_values(env)
|
|
29
|
+
when *ClientErrorStatusesWithCustomExceptions.keys
|
|
30
|
+
raise ClientErrorStatusesWithCustomExceptions[env[:status]], response_values(env)
|
|
23
31
|
when 407
|
|
24
32
|
# mimic the behavior that we get with proxy requests with HTTPS
|
|
25
33
|
msg = %(407 "Proxy Authentication Required")
|
|
26
34
|
raise Faraday::ProxyAuthError.new(msg, response_values(env))
|
|
27
|
-
when 408
|
|
28
|
-
raise Faraday::RequestTimeoutError, response_values(env)
|
|
29
|
-
when 409
|
|
30
|
-
raise Faraday::ConflictError, response_values(env)
|
|
31
|
-
when 422
|
|
32
|
-
raise Faraday::UnprocessableEntityError, response_values(env)
|
|
33
35
|
when ClientErrorStatuses
|
|
34
36
|
raise Faraday::ClientError, response_values(env)
|
|
35
37
|
when ServerErrorStatuses
|
|
@@ -39,11 +41,26 @@ module Faraday
|
|
|
39
41
|
end
|
|
40
42
|
end
|
|
41
43
|
|
|
44
|
+
# Returns a hash of response data with the following keys:
|
|
45
|
+
# - status
|
|
46
|
+
# - headers
|
|
47
|
+
# - body
|
|
48
|
+
# - request
|
|
49
|
+
#
|
|
50
|
+
# The `request` key is omitted when the middleware is explicitly
|
|
51
|
+
# configured with the option `include_request: false`.
|
|
42
52
|
def response_values(env)
|
|
43
|
-
{
|
|
53
|
+
response = {
|
|
44
54
|
status: env.status,
|
|
45
55
|
headers: env.response_headers,
|
|
46
|
-
body: env.body
|
|
56
|
+
body: env.body
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Include the request data by default. If the middleware was explicitly
|
|
60
|
+
# configured to _not_ include request data, then omit it.
|
|
61
|
+
return response unless options[:include_request]
|
|
62
|
+
|
|
63
|
+
response.merge(
|
|
47
64
|
request: {
|
|
48
65
|
method: env.method,
|
|
49
66
|
url: env.url,
|
|
@@ -52,7 +69,7 @@ module Faraday
|
|
|
52
69
|
headers: env.request_headers,
|
|
53
70
|
body: env.request_body
|
|
54
71
|
}
|
|
55
|
-
|
|
72
|
+
)
|
|
56
73
|
end
|
|
57
74
|
|
|
58
75
|
def query_params(env)
|
data/lib/faraday/response.rb
CHANGED
|
@@ -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:
|
|
64
|
-
response_headers:
|
|
65
|
-
url:
|
|
67
|
+
status: status, body: body,
|
|
68
|
+
response_headers: headers,
|
|
69
|
+
url: url
|
|
66
70
|
}
|
|
67
71
|
end
|
|
68
72
|
|
|
@@ -62,10 +62,10 @@ module Faraday
|
|
|
62
62
|
super(key, val)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
def fetch(key,
|
|
65
|
+
def fetch(key, ...)
|
|
66
66
|
key = KeyMap[key]
|
|
67
67
|
key = @names.fetch(key.downcase, key)
|
|
68
|
-
super(key,
|
|
68
|
+
super(key, ...)
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def delete(key)
|
|
@@ -77,6 +77,12 @@ module Faraday
|
|
|
77
77
|
super(key)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
def dig(key, *rest)
|
|
81
|
+
key = KeyMap[key]
|
|
82
|
+
key = @names.fetch(key.downcase, key)
|
|
83
|
+
super(key, *rest)
|
|
84
|
+
end
|
|
85
|
+
|
|
80
86
|
def include?(key)
|
|
81
87
|
@names.include? key.downcase
|
|
82
88
|
end
|
data/lib/faraday/utils.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'base64'
|
|
4
3
|
require 'uri'
|
|
5
4
|
require 'faraday/utils/headers'
|
|
6
5
|
require 'faraday/utils/params_hash'
|
|
@@ -26,7 +25,7 @@ module Faraday
|
|
|
26
25
|
attr_writer :default_space_encoding
|
|
27
26
|
end
|
|
28
27
|
|
|
29
|
-
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]
|
|
28
|
+
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
|
|
30
29
|
|
|
31
30
|
def escape(str)
|
|
32
31
|
str.to_s.gsub(ESCAPE_RE) do |match|
|
|
@@ -38,7 +37,7 @@ module Faraday
|
|
|
38
37
|
CGI.unescape str.to_s
|
|
39
38
|
end
|
|
40
39
|
|
|
41
|
-
DEFAULT_SEP = /[&;] */n
|
|
40
|
+
DEFAULT_SEP = /[&;] */n
|
|
42
41
|
|
|
43
42
|
# Adapted from Rack
|
|
44
43
|
def parse_query(query)
|
|
@@ -54,7 +53,7 @@ module Faraday
|
|
|
54
53
|
end
|
|
55
54
|
|
|
56
55
|
def basic_header_from(login, pass)
|
|
57
|
-
value =
|
|
56
|
+
value = ["#{login}:#{pass}"].pack('m') # Base64 encoding
|
|
58
57
|
value.delete!("\n")
|
|
59
58
|
"Basic #{value}"
|
|
60
59
|
end
|
data/lib/faraday/version.rb
CHANGED
data/lib/faraday.rb
CHANGED
|
@@ -300,14 +300,47 @@ RSpec.describe Faraday::Connection do
|
|
|
300
300
|
it 'joins url to base when used relative path' do
|
|
301
301
|
conn = Faraday.new(url: url)
|
|
302
302
|
uri = conn.build_exclusive_url('service:search?limit=400')
|
|
303
|
-
expect(uri.to_s).to eq('http://service.com/service
|
|
303
|
+
expect(uri.to_s).to eq('http://service.com/service:search?limit=400')
|
|
304
304
|
end
|
|
305
305
|
|
|
306
306
|
it 'joins url to base when used with path prefix' do
|
|
307
307
|
conn = Faraday.new(url: url)
|
|
308
308
|
conn.path_prefix = '/api'
|
|
309
309
|
uri = conn.build_exclusive_url('service:search?limit=400')
|
|
310
|
-
expect(uri.to_s).to eq('http://service.com/api/service
|
|
310
|
+
expect(uri.to_s).to eq('http://service.com/api/service:search?limit=400')
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
context 'with protocol-relative URL (GHSA-33mh-2634-fwr2)' do
|
|
315
|
+
it 'does not allow host override with //evil.com/path' do
|
|
316
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
317
|
+
uri = conn.build_exclusive_url('//evil.com/path')
|
|
318
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it 'does not allow host override with //evil.com:8080/path' do
|
|
322
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
323
|
+
uri = conn.build_exclusive_url('//evil.com:8080/path')
|
|
324
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
it 'does not allow host override with //user:pass@evil.com/path' do
|
|
328
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
329
|
+
uri = conn.build_exclusive_url('//user:pass@evil.com/path')
|
|
330
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it 'does not allow host override with ///evil.com' do
|
|
334
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
335
|
+
uri = conn.build_exclusive_url('///evil.com')
|
|
336
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
it 'still allows single-slash absolute paths' do
|
|
340
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
341
|
+
uri = conn.build_exclusive_url('/safe/path')
|
|
342
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
343
|
+
expect(uri.path).to eq('/safe/path')
|
|
311
344
|
end
|
|
312
345
|
end
|
|
313
346
|
|
data/spec/faraday/error_spec.rb
CHANGED
|
@@ -23,8 +23,12 @@ 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') }
|
|
27
|
-
|
|
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
|
+
if RUBY_VERSION >= '3.4'
|
|
28
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
|
|
29
|
+
else
|
|
30
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
|
31
|
+
end
|
|
28
32
|
it { expect(subject.response_status).to eq(400) }
|
|
29
33
|
it { expect(subject.response_headers).to be_nil }
|
|
30
34
|
it { expect(subject.response_body).to be_nil }
|
|
@@ -61,7 +65,11 @@ RSpec.describe Faraday::Error do
|
|
|
61
65
|
it { expect(subject.wrapped_exception).to be_nil }
|
|
62
66
|
it { expect(subject.response).to eq(response) }
|
|
63
67
|
it { expect(subject.message).to eq('custom message') }
|
|
64
|
-
|
|
68
|
+
if RUBY_VERSION >= '3.4'
|
|
69
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
|
|
70
|
+
else
|
|
71
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
|
72
|
+
end
|
|
65
73
|
it { expect(subject.response_status).to eq(400) }
|
|
66
74
|
it { expect(subject.response_headers).to be_nil }
|
|
67
75
|
it { expect(subject.response_body).to be_nil }
|
|
@@ -81,5 +89,87 @@ RSpec.describe Faraday::Error do
|
|
|
81
89
|
it { expect(subject.response_headers).to eq(headers) }
|
|
82
90
|
it { expect(subject.response_body).to eq(body) }
|
|
83
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
|
|
84
174
|
end
|
|
85
175
|
end
|
|
@@ -67,4 +67,147 @@ RSpec.describe Faraday::Middleware do
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
|
+
|
|
71
|
+
describe '::default_options' do
|
|
72
|
+
let(:subclass_no_options) { FaradayMiddlewareSubclasses::SubclassNoOptions }
|
|
73
|
+
let(:subclass_one_option) { FaradayMiddlewareSubclasses::SubclassOneOption }
|
|
74
|
+
let(:subclass_two_options) { FaradayMiddlewareSubclasses::SubclassTwoOptions }
|
|
75
|
+
|
|
76
|
+
def build_conn(resp_middleware)
|
|
77
|
+
Faraday.new do |c|
|
|
78
|
+
c.adapter :test do |stub|
|
|
79
|
+
stub.get('/success') { [200, {}, 'ok'] }
|
|
80
|
+
end
|
|
81
|
+
c.response resp_middleware
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
RSpec.shared_context 'reset @default_options' do
|
|
86
|
+
before(:each) do
|
|
87
|
+
FaradayMiddlewareSubclasses::SubclassNoOptions.instance_variable_set(:@default_options, nil)
|
|
88
|
+
FaradayMiddlewareSubclasses::SubclassOneOption.instance_variable_set(:@default_options, nil)
|
|
89
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.instance_variable_set(:@default_options, nil)
|
|
90
|
+
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
after(:all) do
|
|
95
|
+
FaradayMiddlewareSubclasses::SubclassNoOptions.instance_variable_set(:@default_options, nil)
|
|
96
|
+
FaradayMiddlewareSubclasses::SubclassOneOption.instance_variable_set(:@default_options, nil)
|
|
97
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.instance_variable_set(:@default_options, nil)
|
|
98
|
+
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context 'with subclass DEFAULT_OPTIONS defined' do
|
|
102
|
+
include_context 'reset @default_options'
|
|
103
|
+
|
|
104
|
+
context 'and without application options configured' do
|
|
105
|
+
let(:resp1) { build_conn(:one_option).get('/success') }
|
|
106
|
+
|
|
107
|
+
it 'has only subclass defaults' do
|
|
108
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
109
|
+
expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS)
|
|
110
|
+
expect(subclass_one_option.default_options).to eq(subclass_one_option::DEFAULT_OPTIONS)
|
|
111
|
+
expect(subclass_two_options.default_options).to eq(subclass_two_options::DEFAULT_OPTIONS)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it { expect(resp1.body).to eq('ok') }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context "and with one application's options changed" do
|
|
118
|
+
let(:resp2) { build_conn(:two_options).get('/success') }
|
|
119
|
+
|
|
120
|
+
before(:each) do
|
|
121
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it 'only updates default options of target subclass' do
|
|
125
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
126
|
+
expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS)
|
|
127
|
+
expect(subclass_one_option.default_options).to eq(subclass_one_option::DEFAULT_OPTIONS)
|
|
128
|
+
expect(subclass_two_options.default_options).to eq({ some_option: false, some_other_option: false })
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it { expect(resp2.body).to eq('ok') }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context "and with two applications' options changed" do
|
|
135
|
+
let(:resp1) { build_conn(:one_option).get('/success') }
|
|
136
|
+
let(:resp2) { build_conn(:two_options).get('/success') }
|
|
137
|
+
|
|
138
|
+
before(:each) do
|
|
139
|
+
FaradayMiddlewareSubclasses::SubclassOneOption.default_options = { some_other_option: true }
|
|
140
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'updates subclasses and parent independent of each other' do
|
|
144
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
145
|
+
expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS)
|
|
146
|
+
expect(subclass_one_option.default_options).to eq({ some_other_option: true })
|
|
147
|
+
expect(subclass_two_options.default_options).to eq({ some_option: false, some_other_option: false })
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it { expect(resp1.body).to eq('ok') }
|
|
151
|
+
it { expect(resp2.body).to eq('ok') }
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
context 'with FARADAY::MIDDLEWARE DEFAULT_OPTIONS and with Subclass DEFAULT_OPTIONS' do
|
|
156
|
+
before(:each) do
|
|
157
|
+
stub_const('Faraday::Middleware::DEFAULT_OPTIONS', { its_magic: false })
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Must stub Faraday::Middleware::DEFAULT_OPTIONS before resetting default options
|
|
161
|
+
include_context 'reset @default_options'
|
|
162
|
+
|
|
163
|
+
context 'and without application options configured' do
|
|
164
|
+
let(:resp1) { build_conn(:one_option).get('/success') }
|
|
165
|
+
|
|
166
|
+
it 'has only subclass defaults' do
|
|
167
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
168
|
+
expect(FaradayMiddlewareSubclasses::SubclassNoOptions.default_options).to eq({ its_magic: false })
|
|
169
|
+
expect(FaradayMiddlewareSubclasses::SubclassOneOption.default_options).to eq({ its_magic: false, some_other_option: false })
|
|
170
|
+
expect(FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options).to eq({ its_magic: false, some_option: true, some_other_option: false })
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it { expect(resp1.body).to eq('ok') }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
context "and with two applications' options changed" do
|
|
177
|
+
let(:resp1) { build_conn(:one_option).get('/success') }
|
|
178
|
+
let(:resp2) { build_conn(:two_options).get('/success') }
|
|
179
|
+
|
|
180
|
+
before(:each) do
|
|
181
|
+
FaradayMiddlewareSubclasses::SubclassOneOption.default_options = { some_other_option: true }
|
|
182
|
+
FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it 'updates subclasses and parent independent of each other' do
|
|
186
|
+
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
|
187
|
+
expect(FaradayMiddlewareSubclasses::SubclassNoOptions.default_options).to eq({ its_magic: false })
|
|
188
|
+
expect(FaradayMiddlewareSubclasses::SubclassOneOption.default_options).to eq({ its_magic: false, some_other_option: true })
|
|
189
|
+
expect(FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options).to eq({ its_magic: false, some_option: false, some_other_option: false })
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it { expect(resp1.body).to eq('ok') }
|
|
193
|
+
it { expect(resp2.body).to eq('ok') }
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
describe 'default_options input validation' do
|
|
198
|
+
include_context 'reset @default_options'
|
|
199
|
+
|
|
200
|
+
it 'raises error if Faraday::Middleware option does not exist' do
|
|
201
|
+
expect { Faraday::Middleware.default_options = { something_special: true } }.to raise_error(Faraday::InitializationError) do |e|
|
|
202
|
+
expect(e.message).to eq('Invalid options provided. Keys not found in Faraday::Middleware::DEFAULT_OPTIONS: something_special')
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it 'raises error if subclass option does not exist' do
|
|
207
|
+
expect { subclass_one_option.default_options = { this_is_a_typo: true } }.to raise_error(Faraday::InitializationError) do |e|
|
|
208
|
+
expect(e.message).to eq('Invalid options provided. Keys not found in FaradayMiddlewareSubclasses::SubclassOneOption::DEFAULT_OPTIONS: this_is_a_typo')
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
70
213
|
end
|
|
@@ -27,6 +27,33 @@ RSpec.describe Faraday::ProxyOptions do
|
|
|
27
27
|
expect(options.inspect).to eq('#<Faraday::ProxyOptions (empty)>')
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
it 'works with hash' do
|
|
31
|
+
hash = { user: 'user', password: 'pass', uri: 'http://@example.org' }
|
|
32
|
+
options = Faraday::ProxyOptions.from(hash)
|
|
33
|
+
expect(options.user).to eq('user')
|
|
34
|
+
expect(options.password).to eq('pass')
|
|
35
|
+
expect(options.uri).to be_a_kind_of(URI)
|
|
36
|
+
expect(options.path).to eq('')
|
|
37
|
+
expect(options.port).to eq(80)
|
|
38
|
+
expect(options.host).to eq('example.org')
|
|
39
|
+
expect(options.scheme).to eq('http')
|
|
40
|
+
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'works with option' do
|
|
44
|
+
opt_arg = { user: 'user', password: 'pass', uri: 'http://@example.org' }
|
|
45
|
+
option = Faraday::ConnectionOptions.from(proxy: opt_arg)
|
|
46
|
+
options = Faraday::ProxyOptions.from(option.proxy)
|
|
47
|
+
expect(options.user).to eq('user')
|
|
48
|
+
expect(options.password).to eq('pass')
|
|
49
|
+
expect(options.uri).to be_a_kind_of(URI)
|
|
50
|
+
expect(options.path).to eq('')
|
|
51
|
+
expect(options.port).to eq(80)
|
|
52
|
+
expect(options.host).to eq('example.org')
|
|
53
|
+
expect(options.scheme).to eq('http')
|
|
54
|
+
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
|
|
55
|
+
end
|
|
56
|
+
|
|
30
57
|
it 'works with no auth' do
|
|
31
58
|
proxy = Faraday::ProxyOptions.from 'http://example.org'
|
|
32
59
|
expect(proxy.user).to be_nil
|
|
@@ -62,7 +62,8 @@ RSpec.describe Faraday::NestedParamsEncoder do
|
|
|
62
62
|
it 'encodes rack compat' do
|
|
63
63
|
params = { a: [{ one: '1', two: '2' }, '3', ''] }
|
|
64
64
|
result = Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split('&')
|
|
65
|
-
|
|
65
|
+
escaped = Rack::Utils.build_nested_query(params)
|
|
66
|
+
expected = Rack::Utils.unescape(escaped).split('&')
|
|
66
67
|
expect(result).to match_array(expected)
|
|
67
68
|
end
|
|
68
69
|
|
|
@@ -132,4 +132,68 @@ RSpec.describe Faraday::Request::Json do
|
|
|
132
132
|
expect(result_type).to eq('application/xml; charset=utf-8')
|
|
133
133
|
end
|
|
134
134
|
end
|
|
135
|
+
|
|
136
|
+
context 'with encoder' do
|
|
137
|
+
let(:encoder) do
|
|
138
|
+
double('Encoder').tap do |e|
|
|
139
|
+
allow(e).to receive(:dump) { |s, opts| JSON.generate(s, opts) }
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
let(:result) { process(a: 1) }
|
|
144
|
+
|
|
145
|
+
context 'when encoder is passed as object' do
|
|
146
|
+
let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }, { encoder: encoder }) }
|
|
147
|
+
|
|
148
|
+
it 'calls specified JSON encoder\'s dump method' do
|
|
149
|
+
expect(encoder).to receive(:dump).with({ a: 1 })
|
|
150
|
+
|
|
151
|
+
result
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it 'encodes body' do
|
|
155
|
+
expect(result_body).to eq('{"a":1}')
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it 'adds content type' do
|
|
159
|
+
expect(result_type).to eq('application/json')
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
context 'when encoder is passed as an object-method pair' do
|
|
164
|
+
let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }, { encoder: [encoder, :dump] }) }
|
|
165
|
+
|
|
166
|
+
it 'calls specified JSON encoder' do
|
|
167
|
+
expect(encoder).to receive(:dump).with({ a: 1 })
|
|
168
|
+
|
|
169
|
+
result
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'encodes body' do
|
|
173
|
+
expect(result_body).to eq('{"a":1}')
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'adds content type' do
|
|
177
|
+
expect(result_type).to eq('application/json')
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
context 'when encoder is not passed' do
|
|
182
|
+
let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }) }
|
|
183
|
+
|
|
184
|
+
it 'calls JSON.generate' do
|
|
185
|
+
expect(JSON).to receive(:generate).with({ a: 1 })
|
|
186
|
+
|
|
187
|
+
result
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it 'encodes body' do
|
|
191
|
+
expect(result_body).to eq('{"a":1}')
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it 'adds content type' do
|
|
195
|
+
expect(result_type).to eq('application/json')
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
135
199
|
end
|