faraday 1.4.1 → 2.14.2
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 +198 -4
- data/LICENSE.md +1 -1
- data/README.md +34 -20
- data/Rakefile +6 -1
- data/examples/client_spec.rb +67 -13
- data/examples/client_test.rb +81 -16
- data/lib/faraday/adapter/test.rb +117 -52
- data/lib/faraday/adapter.rb +12 -20
- data/lib/faraday/connection.rb +86 -134
- data/lib/faraday/encoders/flat_params_encoder.rb +3 -2
- data/lib/faraday/encoders/nested_params_encoder.rb +15 -7
- data/lib/faraday/error.rb +65 -15
- data/lib/faraday/logging/formatter.rb +30 -17
- data/lib/faraday/middleware.rb +44 -3
- data/lib/faraday/middleware_registry.rb +17 -63
- data/lib/faraday/options/connection_options.rb +7 -6
- data/lib/faraday/options/env.rb +88 -65
- data/lib/faraday/options/proxy_options.rb +17 -6
- data/lib/faraday/options/request_options.rb +8 -7
- data/lib/faraday/options/ssl_options.rb +62 -45
- data/lib/faraday/options.rb +8 -7
- data/lib/faraday/rack_builder.rb +46 -47
- data/lib/faraday/request/authorization.rb +37 -38
- data/lib/faraday/request/instrumentation.rb +5 -1
- data/lib/faraday/request/json.rb +70 -0
- data/lib/faraday/request/url_encoded.rb +5 -1
- data/lib/faraday/request.rb +20 -37
- data/lib/faraday/response/json.rb +74 -0
- data/lib/faraday/response/logger.rb +13 -7
- data/lib/faraday/response/raise_error.rb +45 -18
- data/lib/faraday/response.rb +15 -21
- data/lib/faraday/utils/headers.rb +18 -7
- data/lib/faraday/utils.rb +11 -7
- data/lib/faraday/version.rb +1 -1
- data/lib/faraday.rb +12 -32
- data/spec/faraday/adapter/test_spec.rb +182 -0
- data/spec/faraday/connection_spec.rb +219 -92
- data/spec/faraday/error_spec.rb +122 -7
- data/spec/faraday/middleware_registry_spec.rb +31 -0
- data/spec/faraday/middleware_spec.rb +163 -2
- data/spec/faraday/options/env_spec.rb +8 -2
- data/spec/faraday/options/options_spec.rb +1 -1
- data/spec/faraday/options/proxy_options_spec.rb +42 -0
- data/spec/faraday/params_encoders/nested_spec.rb +10 -1
- data/spec/faraday/rack_builder_spec.rb +26 -54
- data/spec/faraday/request/authorization_spec.rb +54 -24
- data/spec/faraday/request/instrumentation_spec.rb +5 -7
- data/spec/faraday/request/json_spec.rb +199 -0
- data/spec/faraday/request/url_encoded_spec.rb +12 -2
- data/spec/faraday/request_spec.rb +14 -15
- data/spec/faraday/response/json_spec.rb +206 -0
- data/spec/faraday/response/logger_spec.rb +84 -5
- data/spec/faraday/response/raise_error_spec.rb +133 -16
- data/spec/faraday/response_spec.rb +10 -1
- data/spec/faraday/utils/headers_spec.rb +31 -4
- data/spec/faraday/utils_spec.rb +66 -2
- data/spec/faraday_spec.rb +10 -4
- data/spec/spec_helper.rb +6 -5
- data/spec/support/fake_safe_buffer.rb +1 -1
- data/spec/support/faraday_middleware_subclasses.rb +18 -0
- data/spec/support/helper_methods.rb +0 -37
- data/spec/support/shared_examples/adapter.rb +2 -2
- data/spec/support/shared_examples/request_method.rb +22 -21
- metadata +27 -81
- data/lib/faraday/adapter/em_http.rb +0 -289
- data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -62
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -69
- data/lib/faraday/adapter/em_synchrony.rb +0 -153
- data/lib/faraday/adapter/httpclient.rb +0 -152
- data/lib/faraday/adapter/patron.rb +0 -132
- data/lib/faraday/adapter/rack.rb +0 -75
- data/lib/faraday/adapter/typhoeus.rb +0 -15
- data/lib/faraday/autoload.rb +0 -92
- data/lib/faraday/dependency_loader.rb +0 -37
- data/lib/faraday/file_part.rb +0 -128
- data/lib/faraday/param_part.rb +0 -53
- data/lib/faraday/request/basic_authentication.rb +0 -20
- data/lib/faraday/request/multipart.rb +0 -106
- data/lib/faraday/request/retry.rb +0 -239
- data/lib/faraday/request/token_authentication.rb +0 -20
- data/spec/faraday/adapter/em_http_spec.rb +0 -47
- data/spec/faraday/adapter/em_synchrony_spec.rb +0 -16
- data/spec/faraday/adapter/excon_spec.rb +0 -49
- data/spec/faraday/adapter/httpclient_spec.rb +0 -73
- data/spec/faraday/adapter/net_http_spec.rb +0 -64
- data/spec/faraday/adapter/patron_spec.rb +0 -18
- data/spec/faraday/adapter/rack_spec.rb +0 -8
- data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
- data/spec/faraday/composite_read_io_spec.rb +0 -80
- data/spec/faraday/request/multipart_spec.rb +0 -302
- data/spec/faraday/request/retry_spec.rb +0 -242
- data/spec/faraday/response/middleware_spec.rb +0 -68
- data/spec/support/webmock_rack_app.rb +0 -68
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
RSpec.describe Faraday::Request::Authorization do
|
|
4
4
|
let(:conn) do
|
|
5
5
|
Faraday.new do |b|
|
|
6
|
-
b.request auth_type, *auth_config
|
|
6
|
+
b.request :authorization, auth_type, *auth_config
|
|
7
7
|
b.adapter :test do |stub|
|
|
8
8
|
stub.get('/auth-echo') do |env|
|
|
9
9
|
[200, {}, env[:request_headers]['Authorization']]
|
|
@@ -14,10 +14,10 @@ RSpec.describe Faraday::Request::Authorization do
|
|
|
14
14
|
|
|
15
15
|
shared_examples 'does not interfere with existing authentication' do
|
|
16
16
|
context 'and request already has an authentication header' do
|
|
17
|
-
let(:response) { conn.get('/auth-echo', nil, authorization: '
|
|
17
|
+
let(:response) { conn.get('/auth-echo', nil, authorization: 'OAuth oauth_token') }
|
|
18
18
|
|
|
19
19
|
it 'does not interfere with existing authorization' do
|
|
20
|
-
expect(response.body).to eq('
|
|
20
|
+
expect(response.body).to eq('OAuth oauth_token')
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -25,7 +25,7 @@ RSpec.describe Faraday::Request::Authorization do
|
|
|
25
25
|
let(:response) { conn.get('/auth-echo') }
|
|
26
26
|
|
|
27
27
|
describe 'basic_auth' do
|
|
28
|
-
let(:auth_type) { :
|
|
28
|
+
let(:auth_type) { :basic }
|
|
29
29
|
|
|
30
30
|
context 'when passed correct params' do
|
|
31
31
|
let(:auth_config) { %w[aladdin opensesame] }
|
|
@@ -44,43 +44,73 @@ RSpec.describe Faraday::Request::Authorization do
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
describe '
|
|
48
|
-
let(:auth_type) { :
|
|
47
|
+
describe 'authorization' do
|
|
48
|
+
let(:auth_type) { :Bearer }
|
|
49
49
|
|
|
50
|
-
context 'when passed
|
|
51
|
-
let(:auth_config) { '
|
|
50
|
+
context 'when passed a string' do
|
|
51
|
+
let(:auth_config) { ['custom'] }
|
|
52
52
|
|
|
53
|
-
it { expect(response.body).to eq('
|
|
53
|
+
it { expect(response.body).to eq('Bearer custom') }
|
|
54
54
|
|
|
55
55
|
include_examples 'does not interfere with existing authentication'
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
context 'when
|
|
59
|
-
let(:auth_config) { [
|
|
58
|
+
context 'when passed a proc' do
|
|
59
|
+
let(:auth_config) { [-> { 'custom_from_proc' }] }
|
|
60
60
|
|
|
61
|
-
it { expect(response.body).to
|
|
62
|
-
it { expect(response.body).to match(/token="baz"/) }
|
|
63
|
-
it { expect(response.body).to match(/foo="42"/) }
|
|
61
|
+
it { expect(response.body).to eq('Bearer custom_from_proc') }
|
|
64
62
|
|
|
65
63
|
include_examples 'does not interfere with existing authentication'
|
|
66
64
|
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
describe 'authorization' do
|
|
70
|
-
let(:auth_type) { :authorization }
|
|
71
65
|
|
|
72
|
-
context 'when passed
|
|
73
|
-
let(:
|
|
66
|
+
context 'when passed a callable' do
|
|
67
|
+
let(:callable) { double('Callable Authorizer', call: 'custom_from_callable') }
|
|
68
|
+
let(:auth_config) { [callable] }
|
|
74
69
|
|
|
75
|
-
it { expect(response.body).to eq('
|
|
70
|
+
it { expect(response.body).to eq('Bearer custom_from_callable') }
|
|
76
71
|
|
|
77
72
|
include_examples 'does not interfere with existing authentication'
|
|
78
73
|
end
|
|
79
74
|
|
|
80
|
-
context '
|
|
81
|
-
let(:
|
|
75
|
+
context 'with an argument' do
|
|
76
|
+
let(:response) { conn.get('/auth-echo', nil, 'middle' => 'crunchy surprise') }
|
|
77
|
+
|
|
78
|
+
context 'when passed a proc' do
|
|
79
|
+
let(:auth_config) { [proc { |env| "proc #{env.request_headers['middle']}" }] }
|
|
80
|
+
|
|
81
|
+
it { expect(response.body).to eq('Bearer proc crunchy surprise') }
|
|
82
|
+
|
|
83
|
+
include_examples 'does not interfere with existing authentication'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context 'when passed a lambda' do
|
|
87
|
+
let(:auth_config) { [->(env) { "lambda #{env.request_headers['middle']}" }] }
|
|
88
|
+
|
|
89
|
+
it { expect(response.body).to eq('Bearer lambda crunchy surprise') }
|
|
90
|
+
|
|
91
|
+
include_examples 'does not interfere with existing authentication'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
context 'when passed a callable with an argument' do
|
|
95
|
+
let(:callable) do
|
|
96
|
+
Class.new do
|
|
97
|
+
def call(env)
|
|
98
|
+
"callable #{env.request_headers['middle']}"
|
|
99
|
+
end
|
|
100
|
+
end.new
|
|
101
|
+
end
|
|
102
|
+
let(:auth_config) { [callable] }
|
|
103
|
+
|
|
104
|
+
it { expect(response.body).to eq('Bearer callable crunchy surprise') }
|
|
105
|
+
|
|
106
|
+
include_examples 'does not interfere with existing authentication'
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context 'when passed too many arguments' do
|
|
111
|
+
let(:auth_config) { %w[baz foo] }
|
|
82
112
|
|
|
83
|
-
it { expect
|
|
113
|
+
it { expect { response }.to raise_error(ArgumentError) }
|
|
84
114
|
|
|
85
115
|
include_examples 'does not interfere with existing authentication'
|
|
86
116
|
end
|
|
@@ -30,13 +30,11 @@ RSpec.describe Faraday::Request::Instrumentation do
|
|
|
30
30
|
|
|
31
31
|
it { expect(options.name).to eq('request.faraday') }
|
|
32
32
|
it 'defaults to ActiveSupport::Notifications' do
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
expect(res).to eq(ActiveSupport::Notifications)
|
|
39
|
-
end
|
|
33
|
+
res = options.instrumenter
|
|
34
|
+
rescue NameError => e
|
|
35
|
+
expect(e.to_s).to match('ActiveSupport')
|
|
36
|
+
else
|
|
37
|
+
expect(res).to eq(ActiveSupport::Notifications)
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
it 'instruments with default name' do
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Faraday::Request::Json do
|
|
4
|
+
let(:middleware) { described_class.new(->(env) { Faraday::Response.new(env) }) }
|
|
5
|
+
|
|
6
|
+
def process(body, content_type = nil)
|
|
7
|
+
env = { body: body, request_headers: Faraday::Utils::Headers.new }
|
|
8
|
+
env[:request_headers]['content-type'] = content_type if content_type
|
|
9
|
+
middleware.call(Faraday::Env.from(env)).env
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def result_body
|
|
13
|
+
result[:body]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def result_type
|
|
17
|
+
result[:request_headers]['content-type']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'no body' do
|
|
21
|
+
let(:result) { process(nil) }
|
|
22
|
+
|
|
23
|
+
it "doesn't change body" do
|
|
24
|
+
expect(result_body).to be_nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "doesn't add content type" do
|
|
28
|
+
expect(result_type).to be_nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context 'empty body' do
|
|
33
|
+
let(:result) { process('') }
|
|
34
|
+
|
|
35
|
+
it "doesn't change body" do
|
|
36
|
+
expect(result_body).to be_empty
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "doesn't add content type" do
|
|
40
|
+
expect(result_type).to be_nil
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context 'string body' do
|
|
45
|
+
let(:result) { process('{"a":1}') }
|
|
46
|
+
|
|
47
|
+
it "doesn't change body" do
|
|
48
|
+
expect(result_body).to eq('{"a":1}')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'adds content type' do
|
|
52
|
+
expect(result_type).to eq('application/json')
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context 'object body' do
|
|
57
|
+
let(:result) { process(a: 1) }
|
|
58
|
+
|
|
59
|
+
it 'encodes body' do
|
|
60
|
+
expect(result_body).to eq('{"a":1}')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'adds content type' do
|
|
64
|
+
expect(result_type).to eq('application/json')
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context 'empty object body' do
|
|
69
|
+
let(:result) { process({}) }
|
|
70
|
+
|
|
71
|
+
it 'encodes body' do
|
|
72
|
+
expect(result_body).to eq('{}')
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context 'true body' do
|
|
77
|
+
let(:result) { process(true) }
|
|
78
|
+
|
|
79
|
+
it 'encodes body' do
|
|
80
|
+
expect(result_body).to eq('true')
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'adds content type' do
|
|
84
|
+
expect(result_type).to eq('application/json')
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
context 'false body' do
|
|
89
|
+
let(:result) { process(false) }
|
|
90
|
+
|
|
91
|
+
it 'encodes body' do
|
|
92
|
+
expect(result_body).to eq('false')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'adds content type' do
|
|
96
|
+
expect(result_type).to eq('application/json')
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context 'object body with json type' do
|
|
101
|
+
let(:result) { process({ a: 1 }, 'application/json; charset=utf-8') }
|
|
102
|
+
|
|
103
|
+
it 'encodes body' do
|
|
104
|
+
expect(result_body).to eq('{"a":1}')
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "doesn't change content type" do
|
|
108
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
context 'object body with vendor json type' do
|
|
113
|
+
let(:result) { process({ a: 1 }, 'application/vnd.myapp.v1+json; charset=utf-8') }
|
|
114
|
+
|
|
115
|
+
it 'encodes body' do
|
|
116
|
+
expect(result_body).to eq('{"a":1}')
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "doesn't change content type" do
|
|
120
|
+
expect(result_type).to eq('application/vnd.myapp.v1+json; charset=utf-8')
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
context 'object body with incompatible type' do
|
|
125
|
+
let(:result) { process({ a: 1 }, 'application/xml; charset=utf-8') }
|
|
126
|
+
|
|
127
|
+
it "doesn't change body" do
|
|
128
|
+
expect(result_body).to eq(a: 1)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "doesn't change content type" do
|
|
132
|
+
expect(result_type).to eq('application/xml; charset=utf-8')
|
|
133
|
+
end
|
|
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
|
|
199
|
+
end
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'stringio'
|
|
4
|
+
|
|
3
5
|
RSpec.describe Faraday::Request::UrlEncoded do
|
|
4
6
|
let(:conn) do
|
|
5
7
|
Faraday.new do |b|
|
|
6
|
-
b.request :multipart
|
|
7
8
|
b.request :url_encoded
|
|
8
9
|
b.adapter :test do |stub|
|
|
9
10
|
stub.post('/echo') do |env|
|
|
10
11
|
posted_as = env[:request_headers]['Content-Type']
|
|
11
|
-
|
|
12
|
+
body = env[:body]
|
|
13
|
+
if body.respond_to?(:read)
|
|
14
|
+
body = body.read
|
|
15
|
+
end
|
|
16
|
+
[200, { 'Content-Type' => posted_as }, body]
|
|
12
17
|
end
|
|
13
18
|
end
|
|
14
19
|
end
|
|
@@ -68,6 +73,11 @@ RSpec.describe Faraday::Request::UrlEncoded do
|
|
|
68
73
|
expect(response.body).to eq('a%5Bb%5D%5Bc%5D%5B%5D=d')
|
|
69
74
|
end
|
|
70
75
|
|
|
76
|
+
it 'works with files' do
|
|
77
|
+
response = conn.post('/echo', StringIO.new('str=apple'))
|
|
78
|
+
expect(response.body).to eq('str=apple')
|
|
79
|
+
end
|
|
80
|
+
|
|
71
81
|
context 'customising default_space_encoding' do
|
|
72
82
|
around do |example|
|
|
73
83
|
Faraday::Utils.default_space_encoding = '%20'
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe Faraday::Request do
|
|
4
4
|
let(:conn) do
|
|
5
|
-
Faraday.new(url: 'http://
|
|
5
|
+
Faraday.new(url: 'http://httpbingo.org/api',
|
|
6
6
|
headers: { 'Mime-Version' => '1.0' },
|
|
7
7
|
request: { oauth: { consumer_key: 'anonymous' } })
|
|
8
8
|
end
|
|
@@ -14,6 +14,7 @@ RSpec.describe Faraday::Request do
|
|
|
14
14
|
context 'when nothing particular is configured' do
|
|
15
15
|
it { expect(subject.http_method).to eq(:get) }
|
|
16
16
|
it { expect(subject.to_env(conn).ssl.verify).to be_falsey }
|
|
17
|
+
it { expect(subject.to_env(conn).ssl.verify_hostname).to be_falsey }
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
context 'when HTTP method is post' do
|
|
@@ -22,23 +23,21 @@ RSpec.describe Faraday::Request do
|
|
|
22
23
|
it { expect(subject.http_method).to eq(:post) }
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
describe 'deprecate method for HTTP method' do
|
|
26
|
-
let(:http_method) { :post }
|
|
27
|
-
let(:expected_warning) do
|
|
28
|
-
%r{WARNING: `Faraday::Request#method` is deprecated; use `#http_method` instead. It will be removed in or after version 2.0.\n`Faraday::Request#method` called from .+/spec/faraday/request_spec.rb:\d+.}
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it { expect(subject.method).to eq(:post) }
|
|
32
|
-
|
|
33
|
-
it { expect { subject.method }.to output(expected_warning).to_stderr }
|
|
34
|
-
end
|
|
35
|
-
|
|
36
26
|
context 'when setting the url on setup with a URI' do
|
|
37
27
|
let(:block) { proc { |req| req.url URI.parse('foo.json?a=1') } }
|
|
38
28
|
|
|
39
29
|
it { expect(subject.path).to eq(URI.parse('foo.json')) }
|
|
40
30
|
it { expect(subject.params).to eq('a' => '1') }
|
|
41
|
-
it { expect(subject.to_env(conn).url.to_s).to eq('http://
|
|
31
|
+
it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context 'when setting the url on setup with a protocol-relative URI' do
|
|
35
|
+
let(:block) { proc { |req| req.url URI.parse('//evil.com/path?token=1') } }
|
|
36
|
+
let(:url) { subject.to_env(conn).url }
|
|
37
|
+
|
|
38
|
+
it { expect(url.host).to eq('httpbingo.org') }
|
|
39
|
+
it { expect(url.path).to eq('/api///evil.com/path') }
|
|
40
|
+
it { expect(url.query).to eq('token=1') }
|
|
42
41
|
end
|
|
43
42
|
|
|
44
43
|
context 'when setting the url on setup with a string path and params' do
|
|
@@ -46,7 +45,7 @@ RSpec.describe Faraday::Request do
|
|
|
46
45
|
|
|
47
46
|
it { expect(subject.path).to eq('foo.json') }
|
|
48
47
|
it { expect(subject.params).to eq('a' => 1) }
|
|
49
|
-
it { expect(subject.to_env(conn).url.to_s).to eq('http://
|
|
48
|
+
it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') }
|
|
50
49
|
end
|
|
51
50
|
|
|
52
51
|
context 'when setting the url on setup with a path including params' do
|
|
@@ -54,7 +53,7 @@ RSpec.describe Faraday::Request do
|
|
|
54
53
|
|
|
55
54
|
it { expect(subject.path).to eq('foo.json') }
|
|
56
55
|
it { expect(subject.params).to eq('a' => '1', 'b' => '2') }
|
|
57
|
-
it { expect(subject.to_env(conn).url.to_s).to eq('http://
|
|
56
|
+
it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1&b=2') }
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
context 'when setting a header on setup with []= syntax' do
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Faraday::Response::Json, type: :response do
|
|
4
|
+
let(:options) { {} }
|
|
5
|
+
let(:headers) { {} }
|
|
6
|
+
let(:middleware) do
|
|
7
|
+
described_class.new(lambda { |env|
|
|
8
|
+
Faraday::Response.new(env)
|
|
9
|
+
}, **options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def process(body, content_type = 'application/json', options = {})
|
|
13
|
+
env = {
|
|
14
|
+
body: body, request: options,
|
|
15
|
+
request_headers: Faraday::Utils::Headers.new,
|
|
16
|
+
response_headers: Faraday::Utils::Headers.new(headers)
|
|
17
|
+
}
|
|
18
|
+
env[:response_headers]['content-type'] = content_type if content_type
|
|
19
|
+
yield(env) if block_given?
|
|
20
|
+
middleware.call(Faraday::Env.from(env))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context 'no type matching' do
|
|
24
|
+
it "doesn't change nil body" do
|
|
25
|
+
expect(process(nil).body).to be_nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'nullifies empty body' do
|
|
29
|
+
expect(process('').body).to be_nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'parses json body' do
|
|
33
|
+
response = process('{"a":1}')
|
|
34
|
+
expect(response.body).to eq('a' => 1)
|
|
35
|
+
expect(response.env[:raw_body]).to be_nil
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'with preserving raw' do
|
|
40
|
+
let(:options) { { preserve_raw: true } }
|
|
41
|
+
|
|
42
|
+
it 'parses json body' do
|
|
43
|
+
response = process('{"a":1}')
|
|
44
|
+
expect(response.body).to eq('a' => 1)
|
|
45
|
+
expect(response.env[:raw_body]).to eq('{"a":1}')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context 'with default regexp type matching' do
|
|
50
|
+
it 'parses json body of correct type' do
|
|
51
|
+
response = process('{"a":1}', 'application/x-json')
|
|
52
|
+
expect(response.body).to eq('a' => 1)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'ignores json body of incorrect type' do
|
|
56
|
+
response = process('{"a":1}', 'text/json-xml')
|
|
57
|
+
expect(response.body).to eq('{"a":1}')
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
context 'with array type matching' do
|
|
62
|
+
let(:options) { { content_type: %w[a/b c/d] } }
|
|
63
|
+
|
|
64
|
+
it 'parses json body of correct type' do
|
|
65
|
+
expect(process('{"a":1}', 'a/b').body).to be_a(Hash)
|
|
66
|
+
expect(process('{"a":1}', 'c/d').body).to be_a(Hash)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'ignores json body of incorrect type' do
|
|
70
|
+
expect(process('{"a":1}', 'a/d').body).not_to be_a(Hash)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'chokes on invalid json' do
|
|
75
|
+
expect { process('{!') }.to raise_error(Faraday::ParsingError)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'includes the response on the ParsingError instance' do
|
|
79
|
+
process('{') { |env| env[:response] = Faraday::Response.new }
|
|
80
|
+
raise 'Parsing should have failed.'
|
|
81
|
+
rescue Faraday::ParsingError => e
|
|
82
|
+
expect(e.response).to be_a(Faraday::Response)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context 'HEAD responses' do
|
|
86
|
+
it "nullifies the body if it's only one space" do
|
|
87
|
+
response = process(' ')
|
|
88
|
+
expect(response.body).to be_nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "nullifies the body if it's two spaces" do
|
|
92
|
+
response = process(' ')
|
|
93
|
+
expect(response.body).to be_nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context 'JSON options' do
|
|
98
|
+
let(:body) { '{"a": 1}' }
|
|
99
|
+
let(:result) { { a: 1 } }
|
|
100
|
+
let(:options) do
|
|
101
|
+
{
|
|
102
|
+
parser_options: {
|
|
103
|
+
symbolize_names: true
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'passes relevant options to JSON parse' do
|
|
109
|
+
expect(JSON).to receive(:parse)
|
|
110
|
+
.with(body, options[:parser_options])
|
|
111
|
+
.and_return(result)
|
|
112
|
+
|
|
113
|
+
response = process(body)
|
|
114
|
+
expect(response.body).to eq(result)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
context 'with decoder' do
|
|
119
|
+
let(:decoder) do
|
|
120
|
+
double('Decoder').tap do |e|
|
|
121
|
+
allow(e).to receive(:load) { |s, opts| JSON.parse(s, opts) }
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
let(:body) { '{"a": 1}' }
|
|
126
|
+
let(:result) { { a: 1 } }
|
|
127
|
+
|
|
128
|
+
context 'when decoder is passed as object' do
|
|
129
|
+
let(:options) do
|
|
130
|
+
{
|
|
131
|
+
parser_options: {
|
|
132
|
+
decoder: decoder,
|
|
133
|
+
option: :option_value,
|
|
134
|
+
symbolize_names: true
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'passes relevant options to specified decoder\'s load method' do
|
|
140
|
+
expect(decoder).to receive(:load)
|
|
141
|
+
.with(body, { option: :option_value, symbolize_names: true })
|
|
142
|
+
.and_return(result)
|
|
143
|
+
|
|
144
|
+
response = process(body)
|
|
145
|
+
expect(response.body).to eq(result)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
context 'when decoder is passed as an object-method pair' do
|
|
150
|
+
let(:options) do
|
|
151
|
+
{
|
|
152
|
+
parser_options: {
|
|
153
|
+
decoder: [decoder, :load],
|
|
154
|
+
option: :option_value,
|
|
155
|
+
symbolize_names: true
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'passes relevant options to specified decoder\'s method' do
|
|
161
|
+
expect(decoder).to receive(:load)
|
|
162
|
+
.with(body, { option: :option_value, symbolize_names: true })
|
|
163
|
+
.and_return(result)
|
|
164
|
+
|
|
165
|
+
response = process(body)
|
|
166
|
+
expect(response.body).to eq(result)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
context 'when decoder is not passed' do
|
|
171
|
+
let(:options) do
|
|
172
|
+
{
|
|
173
|
+
parser_options: {
|
|
174
|
+
symbolize_names: true
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'passes relevant options to JSON parse' do
|
|
180
|
+
expect(JSON).to receive(:parse)
|
|
181
|
+
.with(body, { symbolize_names: true })
|
|
182
|
+
.and_return(result)
|
|
183
|
+
|
|
184
|
+
response = process(body)
|
|
185
|
+
expect(response.body).to eq(result)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it 'passes relevant options to JSON parse even when nil responds to :load' do
|
|
189
|
+
original_allow_message_expectations_on_nil = RSpec::Mocks.configuration.allow_message_expectations_on_nil
|
|
190
|
+
RSpec::Mocks.configuration.allow_message_expectations_on_nil = true
|
|
191
|
+
allow(nil).to receive(:respond_to?)
|
|
192
|
+
.with(:load)
|
|
193
|
+
.and_return(true)
|
|
194
|
+
|
|
195
|
+
expect(JSON).to receive(:parse)
|
|
196
|
+
.with(body, { symbolize_names: true })
|
|
197
|
+
.and_return(result)
|
|
198
|
+
|
|
199
|
+
response = process(body)
|
|
200
|
+
expect(response.body).to eq(result)
|
|
201
|
+
ensure
|
|
202
|
+
RSpec::Mocks.configuration.allow_message_expectations_on_nil = original_allow_message_expectations_on_nil
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|